28 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
30 import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
31
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.ListIterator;
37 import java.util.regex.Pattern;
38 import jdk.nashorn.internal.ir.AccessNode;
39 import jdk.nashorn.internal.ir.BaseNode;
40 import jdk.nashorn.internal.ir.BinaryNode;
41 import jdk.nashorn.internal.ir.Block;
42 import jdk.nashorn.internal.ir.BlockLexicalContext;
43 import jdk.nashorn.internal.ir.BlockStatement;
44 import jdk.nashorn.internal.ir.BreakNode;
45 import jdk.nashorn.internal.ir.CallNode;
46 import jdk.nashorn.internal.ir.CaseNode;
47 import jdk.nashorn.internal.ir.CatchNode;
48 import jdk.nashorn.internal.ir.ContinueNode;
49 import jdk.nashorn.internal.ir.EmptyNode;
50 import jdk.nashorn.internal.ir.Expression;
51 import jdk.nashorn.internal.ir.ExpressionStatement;
52 import jdk.nashorn.internal.ir.ForNode;
53 import jdk.nashorn.internal.ir.FunctionNode;
54 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
55 import jdk.nashorn.internal.ir.IdentNode;
56 import jdk.nashorn.internal.ir.IfNode;
57 import jdk.nashorn.internal.ir.IndexNode;
58 import jdk.nashorn.internal.ir.JumpStatement;
59 import jdk.nashorn.internal.ir.JumpToInlinedFinally;
60 import jdk.nashorn.internal.ir.LabelNode;
61 import jdk.nashorn.internal.ir.LexicalContext;
62 import jdk.nashorn.internal.ir.LiteralNode;
63 import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode;
64 import jdk.nashorn.internal.ir.LoopNode;
65 import jdk.nashorn.internal.ir.Node;
66 import jdk.nashorn.internal.ir.ReturnNode;
67 import jdk.nashorn.internal.ir.RuntimeNode;
168 return false;
169 }
170
171 @Override
172 public Node leaveCallNode(final CallNode callNode) {
173 return checkEval(callNode.setFunction(markerFunction(callNode.getFunction())));
174 }
175
176 @Override
177 public Node leaveCatchNode(final CatchNode catchNode) {
178 return addStatement(catchNode);
179 }
180
181 @Override
182 public boolean enterContinueNode(final ContinueNode continueNode) {
183 addStatement(continueNode);
184 return false;
185 }
186
187 @Override
188 public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) {
189 addStatement(jumpToInlinedFinally);
190 return false;
191 }
192
193 @Override
194 public boolean enterEmptyNode(final EmptyNode emptyNode) {
195 return false;
196 }
197
198 @Override
199 public Node leaveIndexNode(final IndexNode indexNode) {
200 final String name = getConstantPropertyName(indexNode.getIndex());
201 if (name != null) {
202 // If index node is a constant property name convert index node to access node.
203 assert Token.descType(indexNode.getToken()) == TokenType.LBRACKET;
204 return new AccessNode(indexNode.getToken(), indexNode.getFinish(), indexNode.getBase(), name);
205 }
206 return super.leaveIndexNode(indexNode);
207 }
429 return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, jump));
430 }
431 return jump;
432 }
433
434 @Override
435 public Node leaveReturnNode(final ReturnNode returnNode) {
436 final Expression expr = returnNode.getExpression();
437 if (isTerminalFinally(finallyBlock)) {
438 if (expr == null) {
439 // Terminal finally; no return expression.
440 return createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock));
441 }
442 // Terminal finally; has a return expression.
443 final List<Statement> newStatements = new ArrayList<>(2);
444 final int retLineNumber = returnNode.getLineNumber();
445 final long retToken = returnNode.getToken();
446 // Expression is evaluated for side effects.
447 newStatements.add(new ExpressionStatement(retLineNumber, retToken, returnNode.getFinish(), expr));
448 newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock)));
449 return new BlockStatement(retLineNumber, new Block(retToken, finallyBlock.getFinish(), newStatements));
450 } else if (expr == null || expr instanceof PrimitiveLiteralNode<?> || (expr instanceof IdentNode && RETURN.symbolName().equals(((IdentNode)expr).getName()))) {
451 // Nonterminal finally; no return expression, or returns a primitive literal, or returns :return.
452 // Just move the return expression into the finally block.
453 return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode));
454 } else {
455 // We need to evaluate the result of the return in case it is complex while still in the try block,
456 // store it in :return, and return it afterwards.
457 final List<Statement> newStatements = new ArrayList<>();
458 final int retLineNumber = returnNode.getLineNumber();
459 final long retToken = returnNode.getToken();
460 final int retFinish = returnNode.getFinish();
461 final Expression resultNode = new IdentNode(expr.getToken(), expr.getFinish(), RETURN.symbolName());
462 // ":return = <expr>;"
463 newStatements.add(new ExpressionStatement(retLineNumber, retToken, retFinish, new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
464 // inline finally and end it with "return :return;"
465 newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode.setExpression(resultNode))));
466 return new BlockStatement(retLineNumber, new Block(retToken, retFinish, newStatements));
467 }
468 }
469 });
470 addStatement(inlinedFinallies.isEmpty() ? newTryNode : newTryNode.setInlinedFinallies(lc, inlinedFinallies));
471 // TODO: if finallyStatement is terminal, we could just have sites of inlined finallies jump here.
472 addStatement(new BlockStatement(finallyBlock));
473
474 return newTryNode;
475 }
476
477 private static JumpToInlinedFinally createJumpToInlinedFinally(final FunctionNode fn, final List<Block> inlinedFinallies, final Block finallyBlock) {
478 final String labelName = fn.uniqueName(":finally");
479 final long token = finallyBlock.getToken();
480 final int finish = finallyBlock.getFinish();
481 inlinedFinallies.add(new Block(token, finish, new LabelNode(finallyBlock.getFirstStatementLineNumber(),
482 token, finish, labelName, finallyBlock)));
483 return new JumpToInlinedFinally(labelName);
484 }
485
486 private static Block prependFinally(final Block finallyBlock, final Statement statement) {
|
28 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
30 import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
31
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.ListIterator;
37 import java.util.regex.Pattern;
38 import jdk.nashorn.internal.ir.AccessNode;
39 import jdk.nashorn.internal.ir.BaseNode;
40 import jdk.nashorn.internal.ir.BinaryNode;
41 import jdk.nashorn.internal.ir.Block;
42 import jdk.nashorn.internal.ir.BlockLexicalContext;
43 import jdk.nashorn.internal.ir.BlockStatement;
44 import jdk.nashorn.internal.ir.BreakNode;
45 import jdk.nashorn.internal.ir.CallNode;
46 import jdk.nashorn.internal.ir.CaseNode;
47 import jdk.nashorn.internal.ir.CatchNode;
48 import jdk.nashorn.internal.ir.DebuggerNode;
49 import jdk.nashorn.internal.ir.ContinueNode;
50 import jdk.nashorn.internal.ir.EmptyNode;
51 import jdk.nashorn.internal.ir.Expression;
52 import jdk.nashorn.internal.ir.ExpressionStatement;
53 import jdk.nashorn.internal.ir.ForNode;
54 import jdk.nashorn.internal.ir.FunctionNode;
55 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
56 import jdk.nashorn.internal.ir.IdentNode;
57 import jdk.nashorn.internal.ir.IfNode;
58 import jdk.nashorn.internal.ir.IndexNode;
59 import jdk.nashorn.internal.ir.JumpStatement;
60 import jdk.nashorn.internal.ir.JumpToInlinedFinally;
61 import jdk.nashorn.internal.ir.LabelNode;
62 import jdk.nashorn.internal.ir.LexicalContext;
63 import jdk.nashorn.internal.ir.LiteralNode;
64 import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode;
65 import jdk.nashorn.internal.ir.LoopNode;
66 import jdk.nashorn.internal.ir.Node;
67 import jdk.nashorn.internal.ir.ReturnNode;
68 import jdk.nashorn.internal.ir.RuntimeNode;
169 return false;
170 }
171
172 @Override
173 public Node leaveCallNode(final CallNode callNode) {
174 return checkEval(callNode.setFunction(markerFunction(callNode.getFunction())));
175 }
176
177 @Override
178 public Node leaveCatchNode(final CatchNode catchNode) {
179 return addStatement(catchNode);
180 }
181
182 @Override
183 public boolean enterContinueNode(final ContinueNode continueNode) {
184 addStatement(continueNode);
185 return false;
186 }
187
188 @Override
189 public boolean enterDebuggerNode(final DebuggerNode debuggerNode) {
190 final int line = debuggerNode.getLineNumber();
191 final long token = debuggerNode.getToken();
192 final int finish = debuggerNode.getFinish();
193 addStatement(new ExpressionStatement(line, token, finish, new RuntimeNode(token, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Expression>())));
194 return false;
195 }
196
197 @Override
198 public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) {
199 addStatement(jumpToInlinedFinally);
200 return false;
201 }
202
203 @Override
204 public boolean enterEmptyNode(final EmptyNode emptyNode) {
205 return false;
206 }
207
208 @Override
209 public Node leaveIndexNode(final IndexNode indexNode) {
210 final String name = getConstantPropertyName(indexNode.getIndex());
211 if (name != null) {
212 // If index node is a constant property name convert index node to access node.
213 assert Token.descType(indexNode.getToken()) == TokenType.LBRACKET;
214 return new AccessNode(indexNode.getToken(), indexNode.getFinish(), indexNode.getBase(), name);
215 }
216 return super.leaveIndexNode(indexNode);
217 }
439 return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, jump));
440 }
441 return jump;
442 }
443
444 @Override
445 public Node leaveReturnNode(final ReturnNode returnNode) {
446 final Expression expr = returnNode.getExpression();
447 if (isTerminalFinally(finallyBlock)) {
448 if (expr == null) {
449 // Terminal finally; no return expression.
450 return createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock));
451 }
452 // Terminal finally; has a return expression.
453 final List<Statement> newStatements = new ArrayList<>(2);
454 final int retLineNumber = returnNode.getLineNumber();
455 final long retToken = returnNode.getToken();
456 // Expression is evaluated for side effects.
457 newStatements.add(new ExpressionStatement(retLineNumber, retToken, returnNode.getFinish(), expr));
458 newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock)));
459 return new BlockStatement(retLineNumber, new Block(retToken, finallyBlock.getFinish(), newStatements), true);
460 } else if (expr == null || expr instanceof PrimitiveLiteralNode<?> || (expr instanceof IdentNode && RETURN.symbolName().equals(((IdentNode)expr).getName()))) {
461 // Nonterminal finally; no return expression, or returns a primitive literal, or returns :return.
462 // Just move the return expression into the finally block.
463 return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode));
464 } else {
465 // We need to evaluate the result of the return in case it is complex while still in the try block,
466 // store it in :return, and return it afterwards.
467 final List<Statement> newStatements = new ArrayList<>();
468 final int retLineNumber = returnNode.getLineNumber();
469 final long retToken = returnNode.getToken();
470 final int retFinish = returnNode.getFinish();
471 final Expression resultNode = new IdentNode(expr.getToken(), expr.getFinish(), RETURN.symbolName());
472 // ":return = <expr>;"
473 newStatements.add(new ExpressionStatement(retLineNumber, retToken, retFinish, new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
474 // inline finally and end it with "return :return;"
475 newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode.setExpression(resultNode))));
476 return new BlockStatement(retLineNumber, new Block(retToken, retFinish, newStatements), true);
477 }
478 }
479 });
480 addStatement(inlinedFinallies.isEmpty() ? newTryNode : newTryNode.setInlinedFinallies(lc, inlinedFinallies));
481 // TODO: if finallyStatement is terminal, we could just have sites of inlined finallies jump here.
482 addStatement(new BlockStatement(finallyBlock));
483
484 return newTryNode;
485 }
486
487 private static JumpToInlinedFinally createJumpToInlinedFinally(final FunctionNode fn, final List<Block> inlinedFinallies, final Block finallyBlock) {
488 final String labelName = fn.uniqueName(":finally");
489 final long token = finallyBlock.getToken();
490 final int finish = finallyBlock.getFinish();
491 inlinedFinallies.add(new Block(token, finish, new LabelNode(finallyBlock.getFirstStatementLineNumber(),
492 token, finish, labelName, finallyBlock)));
493 return new JumpToInlinedFinally(labelName);
494 }
495
496 private static Block prependFinally(final Block finallyBlock, final Statement statement) {
|