Wed, 28 Jan 2015 17:58:08 +0100
8067139: Finally blocks inlined incorrectly
Reviewed-by: hannesw, lagergren
1.1 --- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Dec 03 16:31:39 2014 +0100 1.2 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Jan 28 17:58:08 2015 +0100 1.3 @@ -919,9 +919,7 @@ 1.4 @Override 1.5 public Node leaveTryNode(final TryNode tryNode) { 1.6 tryNode.setException(exceptionSymbol()); 1.7 - if (tryNode.getFinallyBody() != null) { 1.8 - tryNode.setFinallyCatchAll(exceptionSymbol()); 1.9 - } 1.10 + assert tryNode.getFinallyBody() == null; 1.11 1.12 end(tryNode); 1.13
2.1 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Dec 03 16:31:39 2014 +0100 2.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Jan 28 17:58:08 2015 +0100 2.3 @@ -85,7 +85,6 @@ 2.4 import jdk.nashorn.internal.ir.Block; 2.5 import jdk.nashorn.internal.ir.BlockStatement; 2.6 import jdk.nashorn.internal.ir.BreakNode; 2.7 -import jdk.nashorn.internal.ir.BreakableNode; 2.8 import jdk.nashorn.internal.ir.CallNode; 2.9 import jdk.nashorn.internal.ir.CaseNode; 2.10 import jdk.nashorn.internal.ir.CatchNode; 2.11 @@ -102,6 +101,7 @@ 2.12 import jdk.nashorn.internal.ir.IndexNode; 2.13 import jdk.nashorn.internal.ir.JoinPredecessorExpression; 2.14 import jdk.nashorn.internal.ir.JumpStatement; 2.15 +import jdk.nashorn.internal.ir.JumpToInlinedFinally; 2.16 import jdk.nashorn.internal.ir.LabelNode; 2.17 import jdk.nashorn.internal.ir.LexicalContext; 2.18 import jdk.nashorn.internal.ir.LexicalContextNode; 2.19 @@ -1110,7 +1110,14 @@ 2.20 2.21 @Override 2.22 public boolean enterBlock(final Block block) { 2.23 - method.label(block.getEntryLabel()); 2.24 + final Label entryLabel = block.getEntryLabel(); 2.25 + if (entryLabel.isBreakTarget()) { 2.26 + // Entry label is a break target only for an inlined finally block. 2.27 + assert !method.isReachable(); 2.28 + method.breakLabel(entryLabel, lc.getUsedSlotCount()); 2.29 + } else { 2.30 + method.label(entryLabel); 2.31 + } 2.32 if(!method.isReachable()) { 2.33 return false; 2.34 } 2.35 @@ -1240,6 +1247,11 @@ 2.36 return enterJumpStatement(breakNode); 2.37 } 2.38 2.39 + @Override 2.40 + public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { 2.41 + return enterJumpStatement(jumpToInlinedFinally); 2.42 + } 2.43 + 2.44 private boolean enterJumpStatement(final JumpStatement jump) { 2.45 if(!method.isReachable()) { 2.46 return false; 2.47 @@ -1247,9 +1259,8 @@ 2.48 enterStatement(jump); 2.49 2.50 method.beforeJoinPoint(jump); 2.51 - final BreakableNode target = jump.getTarget(lc); 2.52 - popScopesUntil(target); 2.53 - final Label targetLabel = jump.getTargetLabel(target); 2.54 + popScopesUntil(jump.getPopScopeLimit(lc)); 2.55 + final Label targetLabel = jump.getTargetLabel(lc); 2.56 targetLabel.markAsBreakTarget(); 2.57 method._goto(targetLabel); 2.58 2.59 @@ -3053,6 +3064,14 @@ 2.60 if (method.isReachable()) { 2.61 method._goto(skip); 2.62 } 2.63 + 2.64 + for (final Block inlinedFinally : tryNode.getInlinedFinallies()) { 2.65 + TryNode.getLabelledInlinedFinallyBlock(inlinedFinally).accept(this); 2.66 + // All inlined finallies end with a jump or a return 2.67 + assert !method.isReachable(); 2.68 + } 2.69 + 2.70 + 2.71 method._catch(recovery); 2.72 method.store(vmException, EXCEPTION_TYPE); 2.73 2.74 @@ -3112,15 +3131,14 @@ 2.75 catchBody.accept(this); 2.76 leaveBlock(catchBlock); 2.77 lc.pop(catchBlock); 2.78 - if(method.isReachable()) { 2.79 - method._goto(afterCatch); 2.80 - } 2.81 if(nextCatch != null) { 2.82 + if(method.isReachable()) { 2.83 + method._goto(afterCatch); 2.84 + } 2.85 method.breakLabel(nextCatch, lc.getUsedSlotCount()); 2.86 } 2.87 } 2.88 2.89 - assert !method.isReachable(); 2.90 // afterCatch could be the same as skip, except that we need to establish that the vmException is dead. 2.91 method.label(afterCatch); 2.92 if(method.isReachable()) { 2.93 @@ -3129,6 +3147,8 @@ 2.94 method.label(skip); 2.95 2.96 // Finally body is always inlined elsewhere so it doesn't need to be emitted 2.97 + assert tryNode.getFinallyBody() == null; 2.98 + 2.99 return false; 2.100 } 2.101
3.1 --- a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Wed Dec 03 16:31:39 2014 +0100 3.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Wed Jan 28 17:58:08 2015 +0100 3.3 @@ -74,7 +74,7 @@ 3.4 /** size of next free slot vector */ 3.5 private int nextFreeSlotsSize; 3.6 3.7 - private boolean isWithBoundary(final LexicalContextNode node) { 3.8 + private boolean isWithBoundary(final Object node) { 3.9 return node instanceof Block && !isEmpty() && peek() instanceof WithNode; 3.10 } 3.11 3.12 @@ -102,7 +102,7 @@ 3.13 } 3.14 3.15 @Override 3.16 - public <T extends LexicalContextNode> T pop(final T node) { 3.17 + public <T extends Node> T pop(final T node) { 3.18 final T popped = super.pop(node); 3.19 if (isWithBoundary(node)) { 3.20 dynamicScopeCount--;
4.1 --- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Dec 03 16:31:39 2014 +0100 4.2 +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Jan 28 17:58:08 2015 +0100 4.3 @@ -62,6 +62,7 @@ 4.4 import jdk.nashorn.internal.ir.JoinPredecessor; 4.5 import jdk.nashorn.internal.ir.JoinPredecessorExpression; 4.6 import jdk.nashorn.internal.ir.JumpStatement; 4.7 +import jdk.nashorn.internal.ir.JumpToInlinedFinally; 4.8 import jdk.nashorn.internal.ir.LabelNode; 4.9 import jdk.nashorn.internal.ir.LexicalContext; 4.10 import jdk.nashorn.internal.ir.LexicalContextNode; 4.11 @@ -529,8 +530,7 @@ 4.12 return false; 4.13 } 4.14 assertTypeStackIsEmpty(); 4.15 - final BreakableNode target = jump.getTarget(lc); 4.16 - jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target)); 4.17 + jumpToLabel(jump, jump.getTargetLabel(lc), getBreakTargetTypes(jump.getPopScopeLimit(lc))); 4.18 doesNotContinueSequentially(); 4.19 return false; 4.20 } 4.21 @@ -784,6 +784,11 @@ 4.22 } 4.23 4.24 @Override 4.25 + public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { 4.26 + return enterJumpStatement(jumpToInlinedFinally); 4.27 + } 4.28 + 4.29 + @Override 4.30 public boolean enterLiteralNode(final LiteralNode<?> literalNode) { 4.31 if (literalNode instanceof ArrayLiteralNode) { 4.32 final List<Expression> expressions = ((ArrayLiteralNode)literalNode).getElementExpressions(); 4.33 @@ -1042,6 +1047,17 @@ 4.34 } 4.35 doesNotContinueSequentially(); 4.36 4.37 + for (final Block inlinedFinally : tryNode.getInlinedFinallies()) { 4.38 + final Block finallyBody = TryNode.getLabelledInlinedFinallyBlock(inlinedFinally); 4.39 + joinOnLabel(finallyBody.getEntryLabel()); 4.40 + // NOTE: the jump to inlined finally can end up in dead code, so it is not necessarily reachable. 4.41 + if (reachable) { 4.42 + finallyBody.accept(this); 4.43 + // All inlined finallies end with a jump or a return 4.44 + assert !reachable; 4.45 + } 4.46 + } 4.47 + 4.48 joinOnLabel(catchLabel); 4.49 for(final CatchNode catchNode: tryNode.getCatches()) { 4.50 final IdentNode exception = catchNode.getException(); 4.51 @@ -1125,7 +1141,7 @@ 4.52 return false; 4.53 }; 4.54 4.55 - private Map<Symbol, LvarType> getBreakTargetTypes(final BreakableNode target) { 4.56 + private Map<Symbol, LvarType> getBreakTargetTypes(final LexicalContextNode target) { 4.57 // Remove symbols defined in the the blocks that are being broken out of. 4.58 Map<Symbol, LvarType> types = localVariableTypes; 4.59 for(final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { 4.60 @@ -1380,7 +1396,11 @@ 4.61 if(node instanceof JoinPredecessor) { 4.62 final JoinPredecessor original = joinPredecessors.pop(); 4.63 assert original.getClass() == node.getClass() : original.getClass().getName() + "!=" + node.getClass().getName(); 4.64 - return (Node)setLocalVariableConversion(original, (JoinPredecessor)node); 4.65 + final JoinPredecessor newNode = setLocalVariableConversion(original, (JoinPredecessor)node); 4.66 + if (newNode instanceof LexicalContextNode) { 4.67 + lc.replace((LexicalContextNode)node, (LexicalContextNode)newNode); 4.68 + } 4.69 + return (Node)newNode; 4.70 } 4.71 return node; 4.72 }
5.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java Wed Dec 03 16:31:39 2014 +0100 5.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Wed Jan 28 17:58:08 2015 +0100 5.3 @@ -56,9 +56,11 @@ 5.4 import jdk.nashorn.internal.ir.IfNode; 5.5 import jdk.nashorn.internal.ir.IndexNode; 5.6 import jdk.nashorn.internal.ir.JumpStatement; 5.7 +import jdk.nashorn.internal.ir.JumpToInlinedFinally; 5.8 import jdk.nashorn.internal.ir.LabelNode; 5.9 import jdk.nashorn.internal.ir.LexicalContext; 5.10 import jdk.nashorn.internal.ir.LiteralNode; 5.11 +import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode; 5.12 import jdk.nashorn.internal.ir.LoopNode; 5.13 import jdk.nashorn.internal.ir.Node; 5.14 import jdk.nashorn.internal.ir.ReturnNode; 5.15 @@ -115,7 +117,7 @@ 5.16 for (final Statement statement : statements) { 5.17 if (!terminated) { 5.18 newStatements.add(statement); 5.19 - if (statement.isTerminal() || statement instanceof BreakNode || statement instanceof ContinueNode) { //TODO hasGoto? But some Loops are hasGoto too - why? 5.20 + if (statement.isTerminal() || statement instanceof JumpStatement) { //TODO hasGoto? But some Loops are hasGoto too - why? 5.21 terminated = true; 5.22 } 5.23 } else { 5.24 @@ -183,6 +185,12 @@ 5.25 } 5.26 5.27 @Override 5.28 + public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { 5.29 + addStatement(jumpToInlinedFinally); 5.30 + return false; 5.31 + } 5.32 + 5.33 + @Override 5.34 public boolean enterEmptyNode(final EmptyNode emptyNode) { 5.35 return false; 5.36 } 5.37 @@ -318,8 +326,8 @@ 5.38 return addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor 5.39 } 5.40 5.41 - private static Node ensureUniqueNamesIn(final Node node) { 5.42 - return node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 5.43 + private static <T extends Node> T ensureUniqueNamesIn(final T node) { 5.44 + return (T)node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 5.45 @Override 5.46 public Node leaveFunctionNode(final FunctionNode functionNode) { 5.47 final String name = functionNode.getName(); 5.48 @@ -333,15 +341,15 @@ 5.49 }); 5.50 } 5.51 5.52 - private static List<Statement> copyFinally(final Block finallyBody) { 5.53 + private static Block createFinallyBlock(final Block finallyBody) { 5.54 final List<Statement> newStatements = new ArrayList<>(); 5.55 for (final Statement statement : finallyBody.getStatements()) { 5.56 - newStatements.add((Statement)ensureUniqueNamesIn(statement)); 5.57 + newStatements.add(statement); 5.58 if (statement.hasTerminalFlags()) { 5.59 - return newStatements; 5.60 + break; 5.61 } 5.62 } 5.63 - return newStatements; 5.64 + return finallyBody.setStatements(null, newStatements); 5.65 } 5.66 5.67 private Block catchAllBlock(final TryNode tryNode) { 5.68 @@ -367,28 +375,24 @@ 5.69 return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName()); 5.70 } 5.71 5.72 - private static boolean isTerminal(final List<Statement> statements) { 5.73 - return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags(); 5.74 + private static boolean isTerminalFinally(final Block finallyBlock) { 5.75 + return finallyBlock.getLastStatement().hasTerminalFlags(); 5.76 } 5.77 5.78 /** 5.79 * Splice finally code into all endpoints of a trynode 5.80 * @param tryNode the try node 5.81 - * @param rethrows list of rethrowing throw nodes from synthetic catch blocks 5.82 + * @param rethrow the rethrowing throw nodes from the synthetic catch block 5.83 * @param finallyBody the code in the original finally block 5.84 * @return new try node after splicing finally code (same if nop) 5.85 */ 5.86 - private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) { 5.87 + private TryNode spliceFinally(final TryNode tryNode, final ThrowNode rethrow, final Block finallyBody) { 5.88 assert tryNode.getFinallyBody() == null; 5.89 5.90 + final Block finallyBlock = createFinallyBlock(finallyBody); 5.91 + final ArrayList<Block> inlinedFinallies = new ArrayList<>(); 5.92 + final FunctionNode fn = lc.getCurrentFunction(); 5.93 final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 5.94 - final List<Node> insideTry = new ArrayList<>(); 5.95 - 5.96 - @Override 5.97 - public boolean enterDefault(final Node node) { 5.98 - insideTry.add(node); 5.99 - return true; 5.100 - } 5.101 5.102 @Override 5.103 public boolean enterFunctionNode(final FunctionNode functionNode) { 5.104 @@ -398,12 +402,8 @@ 5.105 5.106 @Override 5.107 public Node leaveThrowNode(final ThrowNode throwNode) { 5.108 - if (rethrows.contains(throwNode)) { 5.109 - final List<Statement> newStatements = copyFinally(finallyBody); 5.110 - if (!isTerminal(newStatements)) { 5.111 - newStatements.add(throwNode); 5.112 - } 5.113 - return BlockStatement.createReplacement(throwNode, newStatements); 5.114 + if (rethrow == throwNode) { 5.115 + return new BlockStatement(prependFinally(finallyBlock, throwNode)); 5.116 } 5.117 return throwNode; 5.118 } 5.119 @@ -419,58 +419,94 @@ 5.120 } 5.121 5.122 private Node leaveJumpStatement(final JumpStatement jump) { 5.123 - return copy(jump, (Node)jump.getTarget(Lower.this.lc)); 5.124 + // NOTE: leaveJumpToInlinedFinally deliberately does not delegate to this method, only break and 5.125 + // continue are edited. JTIF nodes should not be changed, rather the surroundings of 5.126 + // break/continue/return that were moved into the inlined finally block itself will be changed. 5.127 + 5.128 + // If this visitor's lc doesn't find the target of the jump, it means it's external to the try block. 5.129 + if (jump.getTarget(lc) == null) { 5.130 + return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, jump)); 5.131 + } 5.132 + return jump; 5.133 } 5.134 5.135 @Override 5.136 public Node leaveReturnNode(final ReturnNode returnNode) { 5.137 - final Expression expr = returnNode.getExpression(); 5.138 - final List<Statement> newStatements = new ArrayList<>(); 5.139 - 5.140 - final Expression resultNode; 5.141 - if (expr != null) { 5.142 - //we need to evaluate the result of the return in case it is complex while 5.143 - //still in the try block, store it in a result value and return it afterwards 5.144 - resultNode = new IdentNode(Lower.this.compilerConstant(RETURN)); 5.145 - newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); 5.146 + final Expression expr = returnNode.getExpression(); 5.147 + if (isTerminalFinally(finallyBlock)) { 5.148 + if (expr == null) { 5.149 + // Terminal finally; no return expression. 5.150 + return createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock)); 5.151 + } 5.152 + // Terminal finally; has a return expression. 5.153 + final List<Statement> newStatements = new ArrayList<>(2); 5.154 + final int retLineNumber = returnNode.getLineNumber(); 5.155 + final long retToken = returnNode.getToken(); 5.156 + // Expression is evaluated for side effects. 5.157 + newStatements.add(new ExpressionStatement(retLineNumber, retToken, returnNode.getFinish(), expr)); 5.158 + newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock))); 5.159 + return new BlockStatement(retLineNumber, new Block(retToken, finallyBlock.getFinish(), newStatements)); 5.160 + } else if (expr == null || expr instanceof PrimitiveLiteralNode<?> || (expr instanceof IdentNode && RETURN.symbolName().equals(((IdentNode)expr).getName()))) { 5.161 + // Nonterminal finally; no return expression, or returns a primitive literal, or returns :return. 5.162 + // Just move the return expression into the finally block. 5.163 + return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode)); 5.164 } else { 5.165 - resultNode = null; 5.166 + // We need to evaluate the result of the return in case it is complex while still in the try block, 5.167 + // store it in :return, and return it afterwards. 5.168 + final List<Statement> newStatements = new ArrayList<>(); 5.169 + final int retLineNumber = returnNode.getLineNumber(); 5.170 + final long retToken = returnNode.getToken(); 5.171 + final int retFinish = returnNode.getFinish(); 5.172 + final Expression resultNode = new IdentNode(expr.getToken(), expr.getFinish(), RETURN.symbolName()); 5.173 + // ":return = <expr>;" 5.174 + newStatements.add(new ExpressionStatement(retLineNumber, retToken, retFinish, new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); 5.175 + // inline finally and end it with "return :return;" 5.176 + newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode.setExpression(resultNode)))); 5.177 + return new BlockStatement(retLineNumber, new Block(retToken, retFinish, newStatements)); 5.178 } 5.179 - 5.180 - newStatements.addAll(copyFinally(finallyBody)); 5.181 - if (!isTerminal(newStatements)) { 5.182 - newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode)); 5.183 - } 5.184 - 5.185 - return BlockStatement.createReplacement(returnNode, lc.getCurrentBlock().getFinish(), newStatements); 5.186 - } 5.187 - 5.188 - private Node copy(final Statement endpoint, final Node targetNode) { 5.189 - if (!insideTry.contains(targetNode)) { 5.190 - final List<Statement> newStatements = copyFinally(finallyBody); 5.191 - if (!isTerminal(newStatements)) { 5.192 - newStatements.add(endpoint); 5.193 - } 5.194 - return BlockStatement.createReplacement(endpoint, tryNode.getFinish(), newStatements); 5.195 - } 5.196 - return endpoint; 5.197 } 5.198 }); 5.199 - 5.200 - addStatement(newTryNode); 5.201 - for (final Node statement : finallyBody.getStatements()) { 5.202 - addStatement((Statement)statement); 5.203 - } 5.204 + addStatement(inlinedFinallies.isEmpty() ? newTryNode : newTryNode.setInlinedFinallies(lc, inlinedFinallies)); 5.205 + // TODO: if finallyStatement is terminal, we could just have sites of inlined finallies jump here. 5.206 + addStatement(new BlockStatement(finallyBlock)); 5.207 5.208 return newTryNode; 5.209 } 5.210 5.211 + private static JumpToInlinedFinally createJumpToInlinedFinally(final FunctionNode fn, final List<Block> inlinedFinallies, final Block finallyBlock) { 5.212 + final String labelName = fn.uniqueName(":finally"); 5.213 + final long token = finallyBlock.getToken(); 5.214 + final int finish = finallyBlock.getFinish(); 5.215 + inlinedFinallies.add(new Block(token, finish, new LabelNode(finallyBlock.getFirstStatementLineNumber(), 5.216 + token, finish, labelName, finallyBlock))); 5.217 + return new JumpToInlinedFinally(labelName); 5.218 + } 5.219 + 5.220 + private static Block prependFinally(final Block finallyBlock, final Statement statement) { 5.221 + final Block inlinedFinally = ensureUniqueNamesIn(finallyBlock); 5.222 + if (isTerminalFinally(finallyBlock)) { 5.223 + return inlinedFinally; 5.224 + } 5.225 + final List<Statement> stmts = inlinedFinally.getStatements(); 5.226 + final List<Statement> newStmts = new ArrayList<>(stmts.size() + 1); 5.227 + newStmts.addAll(stmts); 5.228 + newStmts.add(statement); 5.229 + return new Block(inlinedFinally.getToken(), statement.getFinish(), newStmts); 5.230 + } 5.231 + 5.232 @Override 5.233 public Node leaveTryNode(final TryNode tryNode) { 5.234 final Block finallyBody = tryNode.getFinallyBody(); 5.235 + TryNode newTryNode = tryNode.setFinallyBody(lc, null); 5.236 5.237 - if (finallyBody == null) { 5.238 - return addStatement(ensureUnconditionalCatch(tryNode)); 5.239 + // No finally or empty finally 5.240 + if (finallyBody == null || finallyBody.getStatementCount() == 0) { 5.241 + final List<CatchNode> catches = newTryNode.getCatches(); 5.242 + if (catches == null || catches.isEmpty()) { 5.243 + // A completely degenerate try block: empty finally, no catches. Replace it with try body. 5.244 + return addStatement(new BlockStatement(tryNode.getBody())); 5.245 + } 5.246 + return addStatement(ensureUnconditionalCatch(newTryNode)); 5.247 } 5.248 5.249 /* 5.250 @@ -496,11 +532,9 @@ 5.251 * now splice in finally code wherever needed 5.252 * 5.253 */ 5.254 - TryNode newTryNode; 5.255 - 5.256 final Block catchAll = catchAllBlock(tryNode); 5.257 5.258 - final List<ThrowNode> rethrows = new ArrayList<>(); 5.259 + final List<ThrowNode> rethrows = new ArrayList<>(1); 5.260 catchAll.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 5.261 @Override 5.262 public boolean enterThrowNode(final ThrowNode throwNode) { 5.263 @@ -510,20 +544,18 @@ 5.264 }); 5.265 assert rethrows.size() == 1; 5.266 5.267 - if (tryNode.getCatchBlocks().isEmpty()) { 5.268 - newTryNode = tryNode.setFinallyBody(null); 5.269 - } else { 5.270 - final Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), ensureUnconditionalCatch(tryNode.setFinallyBody(null))); 5.271 - newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null); 5.272 + if (!tryNode.getCatchBlocks().isEmpty()) { 5.273 + final Block outerBody = new Block(newTryNode.getToken(), newTryNode.getFinish(), ensureUnconditionalCatch(newTryNode)); 5.274 + newTryNode = newTryNode.setBody(lc, outerBody).setCatchBlocks(lc, null); 5.275 } 5.276 5.277 - newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null); 5.278 + newTryNode = newTryNode.setCatchBlocks(lc, Arrays.asList(catchAll)); 5.279 5.280 /* 5.281 * Now that the transform is done, we have to go into the try and splice 5.282 * the finally block in front of any statement that is outside the try 5.283 */ 5.284 - return spliceFinally(newTryNode, rethrows, finallyBody); 5.285 + return (TryNode)lc.replace(tryNode, spliceFinally(newTryNode, rethrows.get(0), finallyBody)); 5.286 } 5.287 5.288 private TryNode ensureUnconditionalCatch(final TryNode tryNode) { 5.289 @@ -535,7 +567,7 @@ 5.290 final List<Block> newCatchBlocks = new ArrayList<>(tryNode.getCatchBlocks()); 5.291 5.292 newCatchBlocks.add(catchAllBlock(tryNode)); 5.293 - return tryNode.setCatchBlocks(newCatchBlocks); 5.294 + return tryNode.setCatchBlocks(lc, newCatchBlocks); 5.295 } 5.296 5.297 @Override
6.1 --- a/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Wed Dec 03 16:31:39 2014 +0100 6.2 +++ b/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Wed Jan 28 17:58:08 2015 +0100 6.3 @@ -51,6 +51,7 @@ 6.4 import jdk.nashorn.internal.ir.IdentNode; 6.5 import jdk.nashorn.internal.ir.IfNode; 6.6 import jdk.nashorn.internal.ir.JumpStatement; 6.7 +import jdk.nashorn.internal.ir.JumpToInlinedFinally; 6.8 import jdk.nashorn.internal.ir.LiteralNode; 6.9 import jdk.nashorn.internal.ir.Node; 6.10 import jdk.nashorn.internal.ir.ReturnNode; 6.11 @@ -355,6 +356,11 @@ 6.12 return leaveJumpNode(continueNode); 6.13 } 6.14 6.15 + @Override 6.16 + public Node leaveJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { 6.17 + return leaveJumpNode(jumpToInlinedFinally); 6.18 + } 6.19 + 6.20 private JumpStatement leaveJumpNode(final JumpStatement jump) { 6.21 if (inSplitNode()) { 6.22 final SplitState splitState = getCurrentSplitState();
7.1 --- a/src/jdk/nashorn/internal/codegen/WeighNodes.java Wed Dec 03 16:31:39 2014 +0100 7.2 +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java Wed Jan 28 17:58:08 2015 +0100 7.3 @@ -40,6 +40,7 @@ 7.4 import jdk.nashorn.internal.ir.IdentNode; 7.5 import jdk.nashorn.internal.ir.IfNode; 7.6 import jdk.nashorn.internal.ir.IndexNode; 7.7 +import jdk.nashorn.internal.ir.JumpToInlinedFinally; 7.8 import jdk.nashorn.internal.ir.LexicalContext; 7.9 import jdk.nashorn.internal.ir.LiteralNode; 7.10 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 7.11 @@ -197,6 +198,12 @@ 7.12 return indexNode; 7.13 } 7.14 7.15 + @Override 7.16 + public Node leaveJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { 7.17 + weight += BREAK_WEIGHT; 7.18 + return jumpToInlinedFinally; 7.19 + } 7.20 + 7.21 @SuppressWarnings("rawtypes") 7.22 @Override 7.23 public boolean enterLiteralNode(final LiteralNode literalNode) {
8.1 --- a/src/jdk/nashorn/internal/ir/Block.java Wed Dec 03 16:31:39 2014 +0100 8.2 +++ b/src/jdk/nashorn/internal/ir/Block.java Wed Jan 28 17:58:08 2015 +0100 8.3 @@ -298,6 +298,14 @@ 8.4 } 8.5 8.6 /** 8.7 + * Returns the last statement in the block. 8.8 + * @return the last statement in the block, or null if the block has no statements. 8.9 + */ 8.10 + public Statement getLastStatement() { 8.11 + return statements.isEmpty() ? null : statements.get(statements.size() - 1); 8.12 + } 8.13 + 8.14 + /** 8.15 * Reset the statement list for this block 8.16 * 8.17 * @param lc lexical context
9.1 --- a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Wed Dec 03 16:31:39 2014 +0100 9.2 +++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Wed Jan 28 17:58:08 2015 +0100 9.3 @@ -74,7 +74,7 @@ 9.4 9.5 @SuppressWarnings("unchecked") 9.6 @Override 9.7 - public <T extends LexicalContextNode> T pop(final T node) { 9.8 + public <T extends Node> T pop(final T node) { 9.9 T expected = node; 9.10 if (node instanceof Block) { 9.11 final List<Statement> newStatements = popStatements();
10.1 --- a/src/jdk/nashorn/internal/ir/BlockStatement.java Wed Dec 03 16:31:39 2014 +0100 10.2 +++ b/src/jdk/nashorn/internal/ir/BlockStatement.java Wed Jan 28 17:58:08 2015 +0100 10.3 @@ -40,6 +40,15 @@ 10.4 /** 10.5 * Constructor 10.6 * 10.7 + * @param block the block to execute 10.8 + */ 10.9 + public BlockStatement(final Block block) { 10.10 + this(block.getFirstStatementLineNumber(), block); 10.11 + } 10.12 + 10.13 + /** 10.14 + * Constructor 10.15 + * 10.16 * @param lineNumber line number 10.17 * @param block the block to execute 10.18 */
11.1 --- a/src/jdk/nashorn/internal/ir/BreakNode.java Wed Dec 03 16:31:39 2014 +0100 11.2 +++ b/src/jdk/nashorn/internal/ir/BreakNode.java Wed Jan 28 17:58:08 2015 +0100 11.3 @@ -77,7 +77,7 @@ 11.4 } 11.5 11.6 @Override 11.7 - public Label getTargetLabel(final BreakableNode target) { 11.8 + Label getTargetLabel(final BreakableNode target) { 11.9 return target.getBreakLabel(); 11.10 } 11.11 }
12.1 --- a/src/jdk/nashorn/internal/ir/ContinueNode.java Wed Dec 03 16:31:39 2014 +0100 12.2 +++ b/src/jdk/nashorn/internal/ir/ContinueNode.java Wed Jan 28 17:58:08 2015 +0100 12.3 @@ -78,7 +78,7 @@ 12.4 } 12.5 12.6 @Override 12.7 - public Label getTargetLabel(final BreakableNode target) { 12.8 + Label getTargetLabel(final BreakableNode target) { 12.9 return ((LoopNode)target).getContinueLabel(); 12.10 } 12.11 }
13.1 --- a/src/jdk/nashorn/internal/ir/JumpStatement.java Wed Dec 03 16:31:39 2014 +0100 13.2 +++ b/src/jdk/nashorn/internal/ir/JumpStatement.java Wed Jan 28 17:58:08 2015 +0100 13.3 @@ -101,7 +101,26 @@ 13.4 * @throws ClassCastException if invoked on the kind of breakable node that this jump statement is not prepared to 13.5 * handle. 13.6 */ 13.7 - public abstract Label getTargetLabel(final BreakableNode target); 13.8 + abstract Label getTargetLabel(final BreakableNode target); 13.9 + 13.10 + /** 13.11 + * Returns the label this jump statement targets. 13.12 + * @param lc the lexical context 13.13 + * @return the label this jump statement targets. 13.14 + */ 13.15 + public Label getTargetLabel(final LexicalContext lc) { 13.16 + return getTargetLabel(getTarget(lc)); 13.17 + } 13.18 + 13.19 + /** 13.20 + * Returns the limit node for popping scopes when this jump statement is effected. 13.21 + * @param lc the current lexical context 13.22 + * @return the limit node for popping scopes when this jump statement is effected. 13.23 + */ 13.24 + public LexicalContextNode getPopScopeLimit(final LexicalContext lc) { 13.25 + // In most cases (break and continue) this is equal to the target. 13.26 + return getTarget(lc); 13.27 + } 13.28 13.29 @Override 13.30 public JumpStatement setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/src/jdk/nashorn/internal/ir/JumpToInlinedFinally.java Wed Jan 28 17:58:08 2015 +0100 14.3 @@ -0,0 +1,90 @@ 14.4 +/* 14.5 + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 14.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 14.7 + * 14.8 + * This code is free software; you can redistribute it and/or modify it 14.9 + * under the terms of the GNU General Public License version 2 only, as 14.10 + * published by the Free Software Foundation. Oracle designates this 14.11 + * particular file as subject to the "Classpath" exception as provided 14.12 + * by Oracle in the LICENSE file that accompanied this code. 14.13 + * 14.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 14.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14.17 + * version 2 for more details (a copy is included in the LICENSE file that 14.18 + * accompanied this code). 14.19 + * 14.20 + * You should have received a copy of the GNU General Public License version 14.21 + * 2 along with this work; if not, write to the Free Software Foundation, 14.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 14.23 + * 14.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 14.25 + * or visit www.oracle.com if you need additional information or have any 14.26 + * questions. 14.27 + */ 14.28 + 14.29 +package jdk.nashorn.internal.ir; 14.30 + 14.31 +import java.util.Objects; 14.32 +import jdk.nashorn.internal.codegen.Label; 14.33 +import jdk.nashorn.internal.ir.annotations.Immutable; 14.34 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 14.35 + 14.36 +/** 14.37 + * IR representation for synthetic jump into an inlined finally statement. 14.38 + */ 14.39 +@Immutable 14.40 +public final class JumpToInlinedFinally extends JumpStatement { 14.41 + private static final long serialVersionUID = 1L; 14.42 + 14.43 + /** 14.44 + * Constructor 14.45 + * 14.46 + * @param labelName label name for inlined finally block 14.47 + */ 14.48 + public JumpToInlinedFinally(final String labelName) { 14.49 + super(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH, Objects.requireNonNull(labelName)); 14.50 + } 14.51 + 14.52 + private JumpToInlinedFinally(final JumpToInlinedFinally breakNode, final LocalVariableConversion conversion) { 14.53 + super(breakNode, conversion); 14.54 + } 14.55 + 14.56 + @Override 14.57 + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 14.58 + if (visitor.enterJumpToInlinedFinally(this)) { 14.59 + return visitor.leaveJumpToInlinedFinally(this); 14.60 + } 14.61 + 14.62 + return this; 14.63 + } 14.64 + 14.65 + @Override 14.66 + JumpStatement createNewJumpStatement(final LocalVariableConversion conversion) { 14.67 + return new JumpToInlinedFinally(this, conversion); 14.68 + } 14.69 + 14.70 + @Override 14.71 + String getStatementName() { 14.72 + return ":jumpToInlinedFinally"; 14.73 + } 14.74 + 14.75 + @Override 14.76 + public Block getTarget(final LexicalContext lc) { 14.77 + return lc.getInlinedFinally(getLabelName()); 14.78 + } 14.79 + 14.80 + @Override 14.81 + public TryNode getPopScopeLimit(final LexicalContext lc) { 14.82 + // Returns the try node to which this jump's target belongs. This will make scope popping also pop the scope 14.83 + // for the body of the try block, if it needs scope. 14.84 + return lc.getTryNodeForInlinedFinally(getLabelName()); 14.85 + } 14.86 + 14.87 + @Override 14.88 + Label getTargetLabel(final BreakableNode target) { 14.89 + assert target != null; 14.90 + // We're jumping to the entry of the inlined finally block 14.91 + return ((Block)target).getEntryLabel(); 14.92 + } 14.93 +}
15.1 --- a/src/jdk/nashorn/internal/ir/LexicalContext.java Wed Dec 03 16:31:39 2014 +0100 15.2 +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java Wed Jan 28 17:58:08 2015 +0100 15.3 @@ -190,7 +190,7 @@ 15.4 * @return the node that was popped 15.5 */ 15.6 @SuppressWarnings("unchecked") 15.7 - public <T extends LexicalContextNode> T pop(final T node) { 15.8 + public <T extends Node> T pop(final T node) { 15.9 --sp; 15.10 final LexicalContextNode popped = stack[sp]; 15.11 stack[sp] = null; 15.12 @@ -469,7 +469,7 @@ 15.13 * scopes that need to be explicitly popped in order to perform a break or continue jump within the current bytecode 15.14 * method. For this reason, the method returns 0 if it encounters a {@code SplitNode} between the current location 15.15 * and the break/continue target. 15.16 - * @param until node to stop counting at. Must be within the current function 15.17 + * @param until node to stop counting at. Must be within the current function 15.18 * @return number of with scopes encountered in the context 15.19 */ 15.20 public int getScopeNestingLevelTo(final LexicalContextNode until) { 15.21 @@ -565,11 +565,41 @@ 15.22 } 15.23 15.24 /** 15.25 + * Find the inlined finally block node corresponding to this label. 15.26 + * @param labelName label name to search for. Must not be null. 15.27 + * @return closest inlined finally block with the given label 15.28 + */ 15.29 + public Block getInlinedFinally(final String labelName) { 15.30 + for (final NodeIterator<TryNode> iter = new NodeIterator<>(TryNode.class); iter.hasNext(); ) { 15.31 + final Block inlinedFinally = iter.next().getInlinedFinally(labelName); 15.32 + if (inlinedFinally != null) { 15.33 + return inlinedFinally; 15.34 + } 15.35 + } 15.36 + return null; 15.37 + } 15.38 + 15.39 + /** 15.40 + * Find the try node for an inlined finally block corresponding to this label. 15.41 + * @param labelName label name to search for. Must not be null. 15.42 + * @return the try node to which the labelled inlined finally block belongs. 15.43 + */ 15.44 + public TryNode getTryNodeForInlinedFinally(final String labelName) { 15.45 + for (final NodeIterator<TryNode> iter = new NodeIterator<>(TryNode.class); iter.hasNext(); ) { 15.46 + final TryNode tryNode = iter.next(); 15.47 + if (tryNode.getInlinedFinally(labelName) != null) { 15.48 + return tryNode; 15.49 + } 15.50 + } 15.51 + return null; 15.52 + } 15.53 + 15.54 + /** 15.55 * Check the lexical context for a given label node by name 15.56 * @param name name of the label 15.57 * @return LabelNode if found, null otherwise 15.58 */ 15.59 - public LabelNode findLabel(final String name) { 15.60 + private LabelNode findLabel(final String name) { 15.61 for (final Iterator<LabelNode> iter = new NodeIterator<>(LabelNode.class, getCurrentFunction()); iter.hasNext(); ) { 15.62 final LabelNode next = iter.next(); 15.63 if (next.getLabelName().equals(name)) { 15.64 @@ -592,6 +622,12 @@ 15.65 return true; 15.66 } else if (next == target) { 15.67 return false; 15.68 + } else if (next instanceof TryNode) { 15.69 + for(final Block inlinedFinally: ((TryNode)next).getInlinedFinallies()) { 15.70 + if (TryNode.getLabelledInlinedFinallyBlock(inlinedFinally) == target) { 15.71 + return false; 15.72 + } 15.73 + } 15.74 } 15.75 } 15.76 throw new AssertionError(target + " was expected in lexical context " + LexicalContext.this + " but wasn't");
16.1 --- a/src/jdk/nashorn/internal/ir/LexicalContextNode.java Wed Dec 03 16:31:39 2014 +0100 16.2 +++ b/src/jdk/nashorn/internal/ir/LexicalContextNode.java Wed Jan 28 17:58:08 2015 +0100 16.3 @@ -54,8 +54,8 @@ 16.4 static Node accept(final LexicalContextNode node, final NodeVisitor<? extends LexicalContext> visitor) { 16.5 final LexicalContext lc = visitor.getLexicalContext(); 16.6 lc.push(node); 16.7 - final LexicalContextNode newNode = (LexicalContextNode)node.accept(lc, visitor); 16.8 - return (Node)lc.pop(newNode); 16.9 + final Node newNode = node.accept(lc, visitor); 16.10 + return lc.pop(newNode); 16.11 } 16.12 } 16.13 }
17.1 --- a/src/jdk/nashorn/internal/ir/OptimisticLexicalContext.java Wed Dec 03 16:31:39 2014 +0100 17.2 +++ b/src/jdk/nashorn/internal/ir/OptimisticLexicalContext.java Wed Jan 28 17:58:08 2015 +0100 17.3 @@ -115,7 +115,7 @@ 17.4 } 17.5 17.6 @Override 17.7 - public <T extends LexicalContextNode> T pop(final T node) { 17.8 + public <T extends Node> T pop(final T node) { 17.9 final T popped = super.pop(node); 17.10 if (isEnabled) { 17.11 if(node instanceof FunctionNode) {
18.1 --- a/src/jdk/nashorn/internal/ir/TryNode.java Wed Dec 03 16:31:39 2014 +0100 18.2 +++ b/src/jdk/nashorn/internal/ir/TryNode.java Wed Jan 28 17:58:08 2015 +0100 18.3 @@ -27,7 +27,9 @@ 18.4 18.5 import java.util.ArrayList; 18.6 import java.util.Collections; 18.7 +import java.util.HashSet; 18.8 import java.util.List; 18.9 +import java.util.Set; 18.10 import jdk.nashorn.internal.ir.annotations.Immutable; 18.11 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 18.12 18.13 @@ -35,7 +37,7 @@ 18.14 * IR representation of a TRY statement. 18.15 */ 18.16 @Immutable 18.17 -public final class TryNode extends Statement implements JoinPredecessor { 18.18 +public final class TryNode extends LexicalContextStatement implements JoinPredecessor { 18.19 private static final long serialVersionUID = 1L; 18.20 18.21 /** Try statements. */ 18.22 @@ -47,12 +49,24 @@ 18.23 /** Finally clause. */ 18.24 private final Block finallyBody; 18.25 18.26 + /** 18.27 + * List of inlined finally blocks. The structure of every inlined finally is: 18.28 + * Block(LabelNode(label, Block(finally-statements, (JumpStatement|ReturnNode)?))). 18.29 + * That is, the block has a single LabelNode statement with the label and a block containing the 18.30 + * statements of the inlined finally block with the jump or return statement appended (if the finally 18.31 + * block was not terminal; the original jump/return is simply ignored if the finally block itself 18.32 + * terminates). The reason for this somewhat strange arrangement is that we didn't want to create a 18.33 + * separate class for the (label, BlockStatement pair) but rather reused the already available LabelNode. 18.34 + * However, if we simply used List<LabelNode> without wrapping the label nodes in an additional Block, 18.35 + * that would've thrown off visitors relying on BlockLexicalContext -- same reason why we never use 18.36 + * Statement as the type of bodies of e.g. IfNode, WhileNode etc. but rather blockify them even when they're 18.37 + * single statements. 18.38 + */ 18.39 + private final List<Block> inlinedFinallies; 18.40 + 18.41 /** Exception symbol. */ 18.42 private Symbol exception; 18.43 18.44 - /** Catchall exception for finally expansion, where applicable */ 18.45 - private Symbol finallyCatchAll; 18.46 - 18.47 private final LocalVariableConversion conversion; 18.48 18.49 /** 18.50 @@ -71,21 +85,23 @@ 18.51 this.catchBlocks = catchBlocks; 18.52 this.finallyBody = finallyBody; 18.53 this.conversion = null; 18.54 + this.inlinedFinallies = Collections.emptyList(); 18.55 } 18.56 18.57 - private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion) { 18.58 + private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies) { 18.59 super(tryNode); 18.60 this.body = body; 18.61 this.catchBlocks = catchBlocks; 18.62 this.finallyBody = finallyBody; 18.63 this.conversion = conversion; 18.64 + this.inlinedFinallies = inlinedFinallies; 18.65 this.exception = tryNode.exception; 18.66 } 18.67 18.68 @Override 18.69 public Node ensureUniqueLabels(final LexicalContext lc) { 18.70 //try nodes are never in lex context 18.71 - return new TryNode(this, body, catchBlocks, finallyBody, conversion); 18.72 + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); 18.73 } 18.74 18.75 @Override 18.76 @@ -106,16 +122,16 @@ 18.77 * @param visitor IR navigating visitor. 18.78 */ 18.79 @Override 18.80 - public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 18.81 + public Node accept(final LexicalContext lc, NodeVisitor<? extends LexicalContext> visitor) { 18.82 if (visitor.enterTryNode(this)) { 18.83 // Need to do finallybody first for termination analysis. TODO still necessary? 18.84 final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor); 18.85 final Block newBody = (Block)body.accept(visitor); 18.86 return visitor.leaveTryNode( 18.87 - setBody(newBody). 18.88 - setFinallyBody(newFinallyBody). 18.89 - setCatchBlocks(Node.accept(visitor, catchBlocks)). 18.90 - setFinallyCatchAll(finallyCatchAll)); 18.91 + setBody(lc, newBody). 18.92 + setFinallyBody(lc, newFinallyBody). 18.93 + setCatchBlocks(lc, Node.accept(visitor, catchBlocks)). 18.94 + setInlinedFinallies(lc, Node.accept(visitor, inlinedFinallies))); 18.95 } 18.96 18.97 return this; 18.98 @@ -136,14 +152,15 @@ 18.99 18.100 /** 18.101 * Reset the body of this try block 18.102 + * @param lc current lexical context 18.103 * @param body new body 18.104 * @return new TryNode or same if unchanged 18.105 */ 18.106 - public TryNode setBody(final Block body) { 18.107 + public TryNode setBody(final LexicalContext lc, final Block body) { 18.108 if (this.body == body) { 18.109 return this; 18.110 } 18.111 - return new TryNode(this, body, catchBlocks, finallyBody, conversion); 18.112 + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); 18.113 } 18.114 18.115 /** 18.116 @@ -172,14 +189,15 @@ 18.117 18.118 /** 18.119 * Set the catch blocks of this try 18.120 + * @param lc current lexical context 18.121 * @param catchBlocks list of catch blocks 18.122 * @return new TryNode or same if unchanged 18.123 */ 18.124 - public TryNode setCatchBlocks(final List<Block> catchBlocks) { 18.125 + public TryNode setCatchBlocks(final LexicalContext lc, final List<Block> catchBlocks) { 18.126 if (this.catchBlocks == catchBlocks) { 18.127 return this; 18.128 } 18.129 - return new TryNode(this, body, catchBlocks, finallyBody, conversion); 18.130 + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); 18.131 } 18.132 18.133 /** 18.134 @@ -200,27 +218,6 @@ 18.135 } 18.136 18.137 /** 18.138 - * Get the catch all symbol for this try block 18.139 - * @return catch all symbol 18.140 - */ 18.141 - public Symbol getFinallyCatchAll() { 18.142 - return this.finallyCatchAll; 18.143 - } 18.144 - 18.145 - /** 18.146 - * If a finally block exists, the synthetic catchall needs another symbol to 18.147 - * store its throwable 18.148 - * @param finallyCatchAll a symbol for the finally catch all exception 18.149 - * @return new TryNode or same if unchanged 18.150 - * 18.151 - * TODO can this still be stateful? 18.152 - */ 18.153 - public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) { 18.154 - this.finallyCatchAll = finallyCatchAll; 18.155 - return this; 18.156 - } 18.157 - 18.158 - /** 18.159 * Get the body of the finally clause for this try 18.160 * @return finally body, or null if no finally 18.161 */ 18.162 @@ -229,15 +226,87 @@ 18.163 } 18.164 18.165 /** 18.166 + * Get the inlined finally block with the given label name. This returns the actual finally block in the 18.167 + * {@link LabelNode}, not the outer wrapper block for the {@link LabelNode}. 18.168 + * @param labelName the name of the inlined finally's label 18.169 + * @return the requested finally block, or null if no finally block's label matches the name. 18.170 + */ 18.171 + public Block getInlinedFinally(final String labelName) { 18.172 + for(final Block inlinedFinally: inlinedFinallies) { 18.173 + final LabelNode labelNode = getInlinedFinallyLabelNode(inlinedFinally); 18.174 + if (labelNode.getLabelName().equals(labelName)) { 18.175 + return labelNode.getBody(); 18.176 + } 18.177 + } 18.178 + return null; 18.179 + } 18.180 + 18.181 + private static LabelNode getInlinedFinallyLabelNode(final Block inlinedFinally) { 18.182 + return (LabelNode)inlinedFinally.getStatements().get(0); 18.183 + } 18.184 + 18.185 + /** 18.186 + * Given an outer wrapper block for the {@link LabelNode} as returned by {@link #getInlinedFinallies()}, 18.187 + * returns its actual inlined finally block. 18.188 + * @param inlinedFinally the outer block for inlined finally, as returned as an element of 18.189 + * {@link #getInlinedFinallies()}. 18.190 + * @return the block contained in the {@link LabelNode} contained in the passed block. 18.191 + */ 18.192 + public static Block getLabelledInlinedFinallyBlock(final Block inlinedFinally) { 18.193 + return getInlinedFinallyLabelNode(inlinedFinally).getBody(); 18.194 + } 18.195 + 18.196 + /** 18.197 + * Returns a list of inlined finally blocks. Note that this returns a list of {@link Block}s such that each one of 18.198 + * them has a single {@link LabelNode}, which in turn contains the label name for the finally block and the 18.199 + * actual finally block. To safely extract the actual finally block, use 18.200 + * {@link #getLabelledInlinedFinallyBlock(Block)}. 18.201 + * @return a list of inlined finally blocks. 18.202 + */ 18.203 + public List<Block> getInlinedFinallies() { 18.204 + return Collections.unmodifiableList(inlinedFinallies); 18.205 + } 18.206 + 18.207 + /** 18.208 * Set the finally body of this try 18.209 + * @param lc current lexical context 18.210 * @param finallyBody new finally body 18.211 * @return new TryNode or same if unchanged 18.212 */ 18.213 - public TryNode setFinallyBody(final Block finallyBody) { 18.214 + public TryNode setFinallyBody(final LexicalContext lc, final Block finallyBody) { 18.215 if (this.finallyBody == finallyBody) { 18.216 return this; 18.217 } 18.218 - return new TryNode(this, body, catchBlocks, finallyBody, conversion); 18.219 + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); 18.220 + } 18.221 + 18.222 + /** 18.223 + * Set the inlined finally blocks of this try. Each element should be a block with a single statement that is a 18.224 + * {@link LabelNode} with a unique label, and the block within the label node should contain the actual inlined 18.225 + * finally block. 18.226 + * @param lc current lexical context 18.227 + * @param inlinedFinallies list of inlined finally blocks 18.228 + * @return new TryNode or same if unchanged 18.229 + */ 18.230 + public TryNode setInlinedFinallies(final LexicalContext lc, final List<Block> inlinedFinallies) { 18.231 + if (this.inlinedFinallies == inlinedFinallies) { 18.232 + return this; 18.233 + } 18.234 + assert checkInlinedFinallies(inlinedFinallies); 18.235 + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); 18.236 + } 18.237 + 18.238 + private static boolean checkInlinedFinallies(final List<Block> inlinedFinallies) { 18.239 + if (!inlinedFinallies.isEmpty()) { 18.240 + final Set<String> labels = new HashSet<>(); 18.241 + for (final Block inlinedFinally : inlinedFinallies) { 18.242 + final List<Statement> stmts = inlinedFinally.getStatements(); 18.243 + assert stmts.size() == 1; 18.244 + final LabelNode ln = getInlinedFinallyLabelNode(inlinedFinally); 18.245 + assert labels.add(ln.getLabelName()); // unique label 18.246 + } 18.247 + } 18.248 + return true; 18.249 } 18.250 18.251 @Override 18.252 @@ -245,7 +314,7 @@ 18.253 if(this.conversion == conversion) { 18.254 return this; 18.255 } 18.256 - return new TryNode(this, body, catchBlocks, finallyBody, conversion); 18.257 + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); 18.258 } 18.259 18.260 @Override
19.1 --- a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Wed Dec 03 16:31:39 2014 +0100 19.2 +++ b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Wed Jan 28 17:58:08 2015 +0100 19.3 @@ -391,6 +391,9 @@ 19.4 finallyBody.accept(this); 19.5 } 19.6 19.7 + for (final Block inlinedFinally : tryNode.getInlinedFinallies()) { 19.8 + inlinedFinally.accept(this); 19.9 + } 19.10 return false; 19.11 } 19.12
20.1 --- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Wed Dec 03 16:31:39 2014 +0100 20.2 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Wed Jan 28 17:58:08 2015 +0100 20.3 @@ -43,6 +43,7 @@ 20.4 import jdk.nashorn.internal.ir.IfNode; 20.5 import jdk.nashorn.internal.ir.IndexNode; 20.6 import jdk.nashorn.internal.ir.JoinPredecessorExpression; 20.7 +import jdk.nashorn.internal.ir.JumpToInlinedFinally; 20.8 import jdk.nashorn.internal.ir.LabelNode; 20.9 import jdk.nashorn.internal.ir.LexicalContext; 20.10 import jdk.nashorn.internal.ir.LiteralNode; 20.11 @@ -473,6 +474,26 @@ 20.12 } 20.13 20.14 /** 20.15 + * Callback for entering a JumpToInlinedFinally 20.16 + * 20.17 + * @param jumpToInlinedFinally the node 20.18 + * @return true if traversal should continue and node children be traversed, false otherwise 20.19 + */ 20.20 + public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { 20.21 + return enterDefault(jumpToInlinedFinally); 20.22 + } 20.23 + 20.24 + /** 20.25 + * Callback for leaving a JumpToInlinedFinally 20.26 + * 20.27 + * @param jumpToInlinedFinally the node 20.28 + * @return processed node, which will replace the original one, or the original node 20.29 + */ 20.30 + public Node leaveJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { 20.31 + return leaveDefault(jumpToInlinedFinally); 20.32 + } 20.33 + 20.34 + /** 20.35 * Callback for entering a LabelNode 20.36 * 20.37 * @param labelNode the node
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 21.2 +++ b/test/script/basic/JDK-8067139.js Wed Jan 28 17:58:08 2015 +0100 21.3 @@ -0,0 +1,92 @@ 21.4 +/* 21.5 + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. 21.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 21.7 + * 21.8 + * This code is free software; you can redistribute it and/or modify it 21.9 + * under the terms of the GNU General Public License version 2 only, as 21.10 + * published by the Free Software Foundation. 21.11 + * 21.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 21.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 21.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 21.15 + * version 2 for more details (a copy is included in the LICENSE file that 21.16 + * accompanied this code). 21.17 + * 21.18 + * You should have received a copy of the GNU General Public License version 21.19 + * 2 along with this work; if not, write to the Free Software Foundation, 21.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21.21 + * 21.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21.23 + * or visit www.oracle.com if you need additional information or have any 21.24 + * questions. 21.25 + */ 21.26 + 21.27 +/** 21.28 + * JDK-8067139: Finally blocks inlined incorrectly 21.29 + * 21.30 + * @test 21.31 + * @run 21.32 + */ 21.33 + 21.34 +// Test case for JDK-8067139 21.35 +// as well as for JDK-8030198 which is a duplicate. 21.36 +(function(){ 21.37 + var catchCount = 0; 21.38 + try { 21.39 + (function (){ 21.40 + try { 21.41 + return; 21.42 + } catch(x) { 21.43 + ++catchCount; 21.44 + } finally { 21.45 + throw 0; 21.46 + } 21.47 + })(); 21.48 + Assert.fail(); // must throw 21.49 + } catch(e) { 21.50 + Assert.assertEquals(e, 0); // threw 0 21.51 + Assert.assertEquals(catchCount, 0); // inner catch never executed 21.52 + } 21.53 +})(); 21.54 + 21.55 +// Test case for JDK-8048862 which is a duplicate of this bug 21.56 +var ret = (function(o) { 21.57 + try{ 21.58 + with(o) { 21.59 + return x; 21.60 + } 21.61 + } finally { 21.62 + try { 21.63 + return x; 21.64 + } catch(e) { 21.65 + Assert.assertTrue(e instanceof ReferenceError); 21.66 + return 2; 21.67 + } 21.68 + } 21.69 +})({x: 1}); 21.70 +Assert.assertEquals(ret, 2); // executed the catch block 21.71 + 21.72 +// Test cases for JDK-8066231 that is a duplicate of this bug 21.73 +// Case 1 21.74 +(function (){ try { Object; } catch(x if x >>>=0) { throw x2; } finally { } })(); 21.75 +// Case 2 21.76 +try { 21.77 + (function (){ try { return; } catch(x) { return x ^= 0; } finally { throw 0; } })(); 21.78 + Assert.fail(); 21.79 +} catch(e) { 21.80 + Assert.assertEquals(e, 0); // threw 0 21.81 +} 21.82 +// Case 3 21.83 +try { 21.84 + (function (){ try { return; } catch(x) { return x ^= Object; } finally { throw Object; } })(); 21.85 + Assert.fail(); 21.86 +} catch(e) { 21.87 + Assert.assertEquals(e, Object); // threw Object 21.88 +} 21.89 +// Case from comment 21.90 +try { 21.91 + (function () { try { Object } catch(x) { (x=y); return; } finally { throw Object; } })(); 21.92 + Assert.fail(); 21.93 +} catch(e) { 21.94 + Assert.assertEquals(e, Object); // threw Object 21.95 +}