nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java
changeset 28690 78317797ab62
parent 27204 06ec78f29a56
child 30701 1a25e71187ff
equal deleted inserted replaced
28597:b2f9702efbe9 28690:78317797ab62
    25 
    25 
    26 package jdk.nashorn.internal.ir;
    26 package jdk.nashorn.internal.ir;
    27 
    27 
    28 import java.util.ArrayList;
    28 import java.util.ArrayList;
    29 import java.util.Collections;
    29 import java.util.Collections;
       
    30 import java.util.HashSet;
    30 import java.util.List;
    31 import java.util.List;
       
    32 import java.util.Set;
    31 import jdk.nashorn.internal.ir.annotations.Immutable;
    33 import jdk.nashorn.internal.ir.annotations.Immutable;
    32 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    34 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    33 
    35 
    34 /**
    36 /**
    35  * IR representation of a TRY statement.
    37  * IR representation of a TRY statement.
    36  */
    38  */
    37 @Immutable
    39 @Immutable
    38 public final class TryNode extends Statement implements JoinPredecessor {
    40 public final class TryNode extends LexicalContextStatement implements JoinPredecessor {
    39     private static final long serialVersionUID = 1L;
    41     private static final long serialVersionUID = 1L;
    40 
    42 
    41     /** Try statements. */
    43     /** Try statements. */
    42     private final Block body;
    44     private final Block body;
    43 
    45 
    45     private final List<Block> catchBlocks;
    47     private final List<Block> catchBlocks;
    46 
    48 
    47     /** Finally clause. */
    49     /** Finally clause. */
    48     private final Block finallyBody;
    50     private final Block finallyBody;
    49 
    51 
       
    52     /**
       
    53      * List of inlined finally blocks. The structure of every inlined finally is:
       
    54      * Block(LabelNode(label, Block(finally-statements, (JumpStatement|ReturnNode)?))).
       
    55      * That is, the block has a single LabelNode statement with the label and a block containing the
       
    56      * statements of the inlined finally block with the jump or return statement appended (if the finally
       
    57      * block was not terminal; the original jump/return is simply ignored if the finally block itself
       
    58      * terminates). The reason for this somewhat strange arrangement is that we didn't want to create a
       
    59      * separate class for the (label, BlockStatement pair) but rather reused the already available LabelNode.
       
    60      * However, if we simply used List<LabelNode> without wrapping the label nodes in an additional Block,
       
    61      * that would've thrown off visitors relying on BlockLexicalContext -- same reason why we never use
       
    62      * Statement as the type of bodies of e.g. IfNode, WhileNode etc. but rather blockify them even when they're
       
    63      * single statements.
       
    64      */
       
    65     private final List<Block> inlinedFinallies;
       
    66 
    50     /** Exception symbol. */
    67     /** Exception symbol. */
    51     private Symbol exception;
    68     private Symbol exception;
    52 
       
    53     /** Catchall exception for finally expansion, where applicable */
       
    54     private Symbol finallyCatchAll;
       
    55 
    69 
    56     private final LocalVariableConversion conversion;
    70     private final LocalVariableConversion conversion;
    57 
    71 
    58     /**
    72     /**
    59      * Constructor
    73      * Constructor
    69         super(lineNumber, token, finish);
    83         super(lineNumber, token, finish);
    70         this.body        = body;
    84         this.body        = body;
    71         this.catchBlocks = catchBlocks;
    85         this.catchBlocks = catchBlocks;
    72         this.finallyBody = finallyBody;
    86         this.finallyBody = finallyBody;
    73         this.conversion  = null;
    87         this.conversion  = null;
    74     }
    88         this.inlinedFinallies = Collections.emptyList();
    75 
    89     }
    76     private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion) {
    90 
       
    91     private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies) {
    77         super(tryNode);
    92         super(tryNode);
    78         this.body        = body;
    93         this.body        = body;
    79         this.catchBlocks = catchBlocks;
    94         this.catchBlocks = catchBlocks;
    80         this.finallyBody = finallyBody;
    95         this.finallyBody = finallyBody;
    81         this.conversion  = conversion;
    96         this.conversion  = conversion;
       
    97         this.inlinedFinallies = inlinedFinallies;
    82         this.exception = tryNode.exception;
    98         this.exception = tryNode.exception;
    83     }
    99     }
    84 
   100 
    85     @Override
   101     @Override
    86     public Node ensureUniqueLabels(final LexicalContext lc) {
   102     public Node ensureUniqueLabels(final LexicalContext lc) {
    87         //try nodes are never in lex context
   103         //try nodes are never in lex context
    88         return new TryNode(this, body, catchBlocks, finallyBody, conversion);
   104         return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies);
    89     }
   105     }
    90 
   106 
    91     @Override
   107     @Override
    92     public boolean isTerminal() {
   108     public boolean isTerminal() {
    93         if (body.isTerminal()) {
   109         if (body.isTerminal()) {
   104     /**
   120     /**
   105      * Assist in IR navigation.
   121      * Assist in IR navigation.
   106      * @param visitor IR navigating visitor.
   122      * @param visitor IR navigating visitor.
   107      */
   123      */
   108     @Override
   124     @Override
   109     public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
   125     public Node accept(final LexicalContext lc, NodeVisitor<? extends LexicalContext> visitor) {
   110         if (visitor.enterTryNode(this)) {
   126         if (visitor.enterTryNode(this)) {
   111             // Need to do finallybody first for termination analysis. TODO still necessary?
   127             // Need to do finallybody first for termination analysis. TODO still necessary?
   112             final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
   128             final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
   113             final Block newBody        = (Block)body.accept(visitor);
   129             final Block newBody        = (Block)body.accept(visitor);
   114             return visitor.leaveTryNode(
   130             return visitor.leaveTryNode(
   115                 setBody(newBody).
   131                 setBody(lc, newBody).
   116                 setFinallyBody(newFinallyBody).
   132                 setFinallyBody(lc, newFinallyBody).
   117                 setCatchBlocks(Node.accept(visitor, catchBlocks)).
   133                 setCatchBlocks(lc, Node.accept(visitor, catchBlocks)).
   118                 setFinallyCatchAll(finallyCatchAll));
   134                 setInlinedFinallies(lc, Node.accept(visitor, inlinedFinallies)));
   119         }
   135         }
   120 
   136 
   121         return this;
   137         return this;
   122     }
   138     }
   123 
   139 
   134         return body;
   150         return body;
   135     }
   151     }
   136 
   152 
   137     /**
   153     /**
   138      * Reset the body of this try block
   154      * Reset the body of this try block
       
   155      * @param lc current lexical context
   139      * @param body new body
   156      * @param body new body
   140      * @return new TryNode or same if unchanged
   157      * @return new TryNode or same if unchanged
   141      */
   158      */
   142     public TryNode setBody(final Block body) {
   159     public TryNode setBody(final LexicalContext lc, final Block body) {
   143         if (this.body == body) {
   160         if (this.body == body) {
   144             return this;
   161             return this;
   145         }
   162         }
   146         return new TryNode(this,  body, catchBlocks, finallyBody, conversion);
   163         return Node.replaceInLexicalContext(lc, this, new TryNode(this,  body, catchBlocks, finallyBody, conversion, inlinedFinallies));
   147     }
   164     }
   148 
   165 
   149     /**
   166     /**
   150      * Get the catches for this try block
   167      * Get the catches for this try block
   151      * @return a list of catch nodes
   168      * @return a list of catch nodes
   170         return Collections.unmodifiableList(catchBlocks);
   187         return Collections.unmodifiableList(catchBlocks);
   171     }
   188     }
   172 
   189 
   173     /**
   190     /**
   174      * Set the catch blocks of this try
   191      * Set the catch blocks of this try
       
   192      * @param lc current lexical context
   175      * @param catchBlocks list of catch blocks
   193      * @param catchBlocks list of catch blocks
   176      * @return new TryNode or same if unchanged
   194      * @return new TryNode or same if unchanged
   177      */
   195      */
   178     public TryNode setCatchBlocks(final List<Block> catchBlocks) {
   196     public TryNode setCatchBlocks(final LexicalContext lc, final List<Block> catchBlocks) {
   179         if (this.catchBlocks == catchBlocks) {
   197         if (this.catchBlocks == catchBlocks) {
   180             return this;
   198             return this;
   181         }
   199         }
   182         return new TryNode(this, body, catchBlocks, finallyBody, conversion);
   200         return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies));
   183     }
   201     }
   184 
   202 
   185     /**
   203     /**
   186      * Get the exception symbol for this try block
   204      * Get the exception symbol for this try block
   187      * @return a symbol for the compiler to store the exception in
   205      * @return a symbol for the compiler to store the exception in
   198         this.exception = exception;
   216         this.exception = exception;
   199         return this;
   217         return this;
   200     }
   218     }
   201 
   219 
   202     /**
   220     /**
   203      * Get the catch all symbol for this try block
       
   204      * @return catch all symbol
       
   205      */
       
   206     public Symbol getFinallyCatchAll() {
       
   207         return this.finallyCatchAll;
       
   208     }
       
   209 
       
   210     /**
       
   211      * If a finally block exists, the synthetic catchall needs another symbol to
       
   212      * store its throwable
       
   213      * @param finallyCatchAll a symbol for the finally catch all exception
       
   214      * @return new TryNode or same if unchanged
       
   215      *
       
   216      * TODO can this still be stateful?
       
   217      */
       
   218     public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) {
       
   219         this.finallyCatchAll = finallyCatchAll;
       
   220         return this;
       
   221     }
       
   222 
       
   223     /**
       
   224      * Get the body of the finally clause for this try
   221      * Get the body of the finally clause for this try
   225      * @return finally body, or null if no finally
   222      * @return finally body, or null if no finally
   226      */
   223      */
   227     public Block getFinallyBody() {
   224     public Block getFinallyBody() {
   228         return finallyBody;
   225         return finallyBody;
   229     }
   226     }
   230 
   227 
   231     /**
   228     /**
       
   229      * Get the inlined finally block with the given label name. This returns the actual finally block in the
       
   230      * {@link LabelNode}, not the outer wrapper block for the {@link LabelNode}.
       
   231      * @param labelName the name of the inlined finally's label
       
   232      * @return the requested finally block, or null if no finally block's label matches the name.
       
   233      */
       
   234     public Block getInlinedFinally(final String labelName) {
       
   235         for(final Block inlinedFinally: inlinedFinallies) {
       
   236             final LabelNode labelNode = getInlinedFinallyLabelNode(inlinedFinally);
       
   237             if (labelNode.getLabelName().equals(labelName)) {
       
   238                 return labelNode.getBody();
       
   239             }
       
   240         }
       
   241         return null;
       
   242     }
       
   243 
       
   244     private static LabelNode getInlinedFinallyLabelNode(final Block inlinedFinally) {
       
   245         return (LabelNode)inlinedFinally.getStatements().get(0);
       
   246     }
       
   247 
       
   248     /**
       
   249      * Given an outer wrapper block for the {@link LabelNode} as returned by {@link #getInlinedFinallies()},
       
   250      * returns its actual inlined finally block.
       
   251      * @param inlinedFinally the outer block for inlined finally, as returned as an element of
       
   252      * {@link #getInlinedFinallies()}.
       
   253      * @return the block contained in the {@link LabelNode} contained in the passed block.
       
   254      */
       
   255     public static Block getLabelledInlinedFinallyBlock(final Block inlinedFinally) {
       
   256         return getInlinedFinallyLabelNode(inlinedFinally).getBody();
       
   257     }
       
   258 
       
   259     /**
       
   260      * Returns a list of inlined finally blocks. Note that this returns a list of {@link Block}s such that each one of
       
   261      * them has a single {@link LabelNode}, which in turn contains the label name for the finally block and the
       
   262      * actual finally block. To safely extract the actual finally block, use
       
   263      * {@link #getLabelledInlinedFinallyBlock(Block)}.
       
   264      * @return a list of inlined finally blocks.
       
   265      */
       
   266     public List<Block> getInlinedFinallies() {
       
   267         return Collections.unmodifiableList(inlinedFinallies);
       
   268     }
       
   269 
       
   270     /**
   232      * Set the finally body of this try
   271      * Set the finally body of this try
       
   272      * @param lc current lexical context
   233      * @param finallyBody new finally body
   273      * @param finallyBody new finally body
   234      * @return new TryNode or same if unchanged
   274      * @return new TryNode or same if unchanged
   235      */
   275      */
   236     public TryNode setFinallyBody(final Block finallyBody) {
   276     public TryNode setFinallyBody(final LexicalContext lc, final Block finallyBody) {
   237         if (this.finallyBody == finallyBody) {
   277         if (this.finallyBody == finallyBody) {
   238             return this;
   278             return this;
   239         }
   279         }
   240         return new TryNode(this, body, catchBlocks, finallyBody, conversion);
   280         return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies));
       
   281     }
       
   282 
       
   283     /**
       
   284      * Set the inlined finally blocks of this try. Each element should be a block with a single statement that is a
       
   285      * {@link LabelNode} with a unique label, and the block within the label node should contain the actual inlined
       
   286      * finally block.
       
   287      * @param lc current lexical context
       
   288      * @param inlinedFinallies list of inlined finally blocks
       
   289      * @return new TryNode or same if unchanged
       
   290      */
       
   291     public TryNode setInlinedFinallies(final LexicalContext lc, final List<Block> inlinedFinallies) {
       
   292         if (this.inlinedFinallies == inlinedFinallies) {
       
   293             return this;
       
   294         }
       
   295         assert checkInlinedFinallies(inlinedFinallies);
       
   296         return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies));
       
   297     }
       
   298 
       
   299     private static boolean checkInlinedFinallies(final List<Block> inlinedFinallies) {
       
   300         if (!inlinedFinallies.isEmpty()) {
       
   301             final Set<String> labels = new HashSet<>();
       
   302             for (final Block inlinedFinally : inlinedFinallies) {
       
   303                 final List<Statement> stmts = inlinedFinally.getStatements();
       
   304                 assert stmts.size() == 1;
       
   305                 final LabelNode ln = getInlinedFinallyLabelNode(inlinedFinally);
       
   306                 assert labels.add(ln.getLabelName()); // unique label
       
   307             }
       
   308         }
       
   309         return true;
   241     }
   310     }
   242 
   311 
   243     @Override
   312     @Override
   244     public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
   313     public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
   245         if(this.conversion == conversion) {
   314         if(this.conversion == conversion) {
   246             return this;
   315             return this;
   247         }
   316         }
   248         return new TryNode(this, body, catchBlocks, finallyBody, conversion);
   317         return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies);
   249     }
   318     }
   250 
   319 
   251     @Override
   320     @Override
   252     public LocalVariableConversion getLocalVariableConversion() {
   321     public LocalVariableConversion getLocalVariableConversion() {
   253         return conversion;
   322         return conversion;