nashorn/src/jdk/nashorn/internal/codegen/Lower.java
changeset 17524 703643aeb0d6
parent 17523 cb4a7c901e0d
child 17745 86e5a15b3b20
equal deleted inserted replaced
17523:cb4a7c901e0d 17524:703643aeb0d6
    30 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
    30 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
    31 
    31 
    32 import java.util.ArrayList;
    32 import java.util.ArrayList;
    33 import java.util.Arrays;
    33 import java.util.Arrays;
    34 import java.util.List;
    34 import java.util.List;
       
    35 
    35 import jdk.nashorn.internal.ir.BaseNode;
    36 import jdk.nashorn.internal.ir.BaseNode;
    36 import jdk.nashorn.internal.ir.BinaryNode;
    37 import jdk.nashorn.internal.ir.BinaryNode;
    37 import jdk.nashorn.internal.ir.Block;
    38 import jdk.nashorn.internal.ir.Block;
    38 import jdk.nashorn.internal.ir.BlockLexicalContext;
    39 import jdk.nashorn.internal.ir.BlockLexicalContext;
    39 import jdk.nashorn.internal.ir.BreakNode;
    40 import jdk.nashorn.internal.ir.BreakNode;
    47 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
    48 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
    48 import jdk.nashorn.internal.ir.IdentNode;
    49 import jdk.nashorn.internal.ir.IdentNode;
    49 import jdk.nashorn.internal.ir.IfNode;
    50 import jdk.nashorn.internal.ir.IfNode;
    50 import jdk.nashorn.internal.ir.LabelNode;
    51 import jdk.nashorn.internal.ir.LabelNode;
    51 import jdk.nashorn.internal.ir.LexicalContext;
    52 import jdk.nashorn.internal.ir.LexicalContext;
    52 import jdk.nashorn.internal.ir.LineNumberNode;
       
    53 import jdk.nashorn.internal.ir.LiteralNode;
    53 import jdk.nashorn.internal.ir.LiteralNode;
    54 import jdk.nashorn.internal.ir.LoopNode;
    54 import jdk.nashorn.internal.ir.LoopNode;
    55 import jdk.nashorn.internal.ir.Node;
    55 import jdk.nashorn.internal.ir.Node;
    56 import jdk.nashorn.internal.ir.ReturnNode;
    56 import jdk.nashorn.internal.ir.ReturnNode;
       
    57 import jdk.nashorn.internal.ir.Statement;
    57 import jdk.nashorn.internal.ir.SwitchNode;
    58 import jdk.nashorn.internal.ir.SwitchNode;
    58 import jdk.nashorn.internal.ir.Symbol;
    59 import jdk.nashorn.internal.ir.Symbol;
    59 import jdk.nashorn.internal.ir.ThrowNode;
    60 import jdk.nashorn.internal.ir.ThrowNode;
    60 import jdk.nashorn.internal.ir.TryNode;
    61 import jdk.nashorn.internal.ir.TryNode;
    61 import jdk.nashorn.internal.ir.UnaryNode;
       
    62 import jdk.nashorn.internal.ir.VarNode;
    62 import jdk.nashorn.internal.ir.VarNode;
    63 import jdk.nashorn.internal.ir.WhileNode;
    63 import jdk.nashorn.internal.ir.WhileNode;
    64 import jdk.nashorn.internal.ir.WithNode;
    64 import jdk.nashorn.internal.ir.WithNode;
    65 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
    65 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
    66 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    66 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    91      */
    91      */
    92     Lower() {
    92     Lower() {
    93         super(new BlockLexicalContext() {
    93         super(new BlockLexicalContext() {
    94 
    94 
    95             @Override
    95             @Override
    96             public List<Node> popStatements() {
    96             public List<Statement> popStatements() {
    97                 List<Node> newStatements = new ArrayList<>();
    97                 final List<Statement> newStatements = new ArrayList<>();
    98                 boolean terminated = false;
    98                 boolean terminated = false;
    99 
    99 
   100                 final List<Node> statements = super.popStatements();
   100                 final List<Statement> statements = super.popStatements();
   101                 for (final Node statement : statements) {
   101                 for (final Statement statement : statements) {
   102                     if (!terminated) {
   102                     if (!terminated) {
   103                         newStatements.add(statement);
   103                         newStatements.add(statement);
   104                         if (statement.isTerminal()) {
   104                         if (statement.isTerminal() || statement instanceof BreakNode || statement instanceof ContinueNode) { //TODO hasGoto? But some Loops are hasGoto too - why?
   105                             terminated = true;
   105                             terminated = true;
   106                         }
   106                         }
   107                     } else {
   107                     } else {
   108                         if (statement instanceof VarNode) {
   108                         statement.accept(new NodeVisitor() {
   109                             newStatements.add(((VarNode)statement).setInit(null));
   109                             @Override
   110                         }
   110                             public boolean enterVarNode(final VarNode varNode) {
       
   111                                 newStatements.add(varNode.setInit(null));
       
   112                                 return false;
       
   113                             }
       
   114                         });
   111                     }
   115                     }
   112                 }
   116                 }
   113                 return newStatements;
   117                 return newStatements;
   114             }
   118             }
   115         });
   119         });
   118     @Override
   122     @Override
   119     public boolean enterBlock(final Block block) {
   123     public boolean enterBlock(final Block block) {
   120         final LexicalContext lc = getLexicalContext();
   124         final LexicalContext lc = getLexicalContext();
   121         final FunctionNode   function = lc.getCurrentFunction();
   125         final FunctionNode   function = lc.getCurrentFunction();
   122         if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) {
   126         if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) {
   123             new ExecuteNode(block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
   127             new ExecuteNode(block.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
   124         }
   128         }
   125         return true;
   129         return true;
   126     }
   130     }
   127 
   131 
   128     @Override
   132     @Override
   130         //now we have committed the entire statement list to the block, but we need to truncate
   134         //now we have committed the entire statement list to the block, but we need to truncate
   131         //whatever is after the last terminal. block append won't append past it
   135         //whatever is after the last terminal. block append won't append past it
   132 
   136 
   133         final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext();
   137         final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext();
   134 
   138 
   135         Node last = lc.getLastStatement();
   139         Statement last = lc.getLastStatement();
   136 
   140 
   137         if (lc.isFunctionBody()) {
   141         if (lc.isFunctionBody()) {
   138             final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
   142             final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
   139             final boolean isProgram = currentFunction.isProgram();
   143             final boolean isProgram = currentFunction.isProgram();
   140             final ReturnNode returnNode = new ReturnNode(
   144             final ReturnNode returnNode = new ReturnNode(
       
   145                 last == null ? block.getLineNumber() : last.getLineNumber(), //TODO?
   141                 currentFunction.getToken(),
   146                 currentFunction.getToken(),
   142                 currentFunction.getFinish(),
   147                 currentFunction.getFinish(),
   143                 isProgram ?
   148                 isProgram ?
   144                     compilerConstant(RETURN) :
   149                     compilerConstant(RETURN) :
   145                     LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
   150                     LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
   146 
   151 
   147             last = returnNode.accept(this);
   152             last = (Statement)returnNode.accept(this);
   148         }
   153         }
   149 
   154 
   150         if (last != null && last.isTerminal()) {
   155         if (last != null && last.isTerminal()) {
   151             return block.setIsTerminal(lc, true);
   156             return block.setIsTerminal(lc, true);
   152         }
   157         }
   237     public Node leaveLabelNode(final LabelNode labelNode) {
   242     public Node leaveLabelNode(final LabelNode labelNode) {
   238         return addStatement(labelNode);
   243         return addStatement(labelNode);
   239     }
   244     }
   240 
   245 
   241     @Override
   246     @Override
   242     public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
       
   243         addStatement(lineNumberNode); // don't put it in lastStatement cache
       
   244         return false;
       
   245     }
       
   246 
       
   247     @Override
       
   248     public Node leaveReturnNode(final ReturnNode returnNode) {
   247     public Node leaveReturnNode(final ReturnNode returnNode) {
   249         addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
   248         addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
   250         return returnNode;
   249         return returnNode;
   251     }
   250     }
   252 
   251 
   269                return labelledNode.ensureUniqueLabels(getLexicalContext());
   268                return labelledNode.ensureUniqueLabels(getLexicalContext());
   270            }
   269            }
   271         });
   270         });
   272     }
   271     }
   273 
   272 
   274     private static List<Node> copyFinally(final Block finallyBody) {
   273     private static List<Statement> copyFinally(final Block finallyBody) {
   275         final List<Node> newStatements = new ArrayList<>();
   274         final List<Statement> newStatements = new ArrayList<>();
   276         for (final Node statement : finallyBody.getStatements()) {
   275         for (final Statement statement : finallyBody.getStatements()) {
   277             newStatements.add(ensureUniqueLabelsIn(statement));
   276             newStatements.add((Statement)ensureUniqueLabelsIn(statement));
   278             if (statement.hasTerminalFlags()) {
   277             if (statement.hasTerminalFlags()) {
   279                 return newStatements;
   278                 return newStatements;
   280             }
   279             }
   281         }
   280         }
   282         return newStatements;
   281         return newStatements;
   283     }
   282     }
   284 
   283 
   285     private Block catchAllBlock(final TryNode tryNode) {
   284     private Block catchAllBlock(final TryNode tryNode) {
   286         final long   token  = tryNode.getToken();
   285         final int  lineNumber = tryNode.getLineNumber();
   287         final int    finish = tryNode.getFinish();
   286         final long token      = tryNode.getToken();
       
   287         final int  finish     = tryNode.getFinish();
   288 
   288 
   289         final IdentNode exception = new IdentNode(token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all"));
   289         final IdentNode exception = new IdentNode(token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all"));
   290 
   290 
   291         final Block catchBody = new Block(token, finish, new ThrowNode(token, finish, new IdentNode(exception))).
   291         final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception))).
   292                 setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal
   292                 setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal
   293 
   293 
   294         final CatchNode catchAllNode  = new CatchNode(token, finish, new IdentNode(exception), null, catchBody);
   294         final CatchNode catchAllNode  = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody);
   295         final Block     catchAllBlock = new Block(token, finish, catchAllNode);
   295         final Block     catchAllBlock = new Block(lineNumber, token, finish, catchAllNode);
   296 
   296 
   297         //catchallblock -> catchallnode (catchnode) -> exception -> throw
   297         //catchallblock -> catchallnode (catchnode) -> exception -> throw
   298 
   298 
   299         return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower
   299         return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower
   300     }
   300     }
   302     private IdentNode compilerConstant(final CompilerConstants cc) {
   302     private IdentNode compilerConstant(final CompilerConstants cc) {
   303         final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
   303         final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
   304         return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
   304         return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
   305     }
   305     }
   306 
   306 
   307     private static boolean isTerminal(final List<Node> statements) {
   307     private static boolean isTerminal(final List<Statement> statements) {
   308         return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags();
   308         return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags();
   309     }
   309     }
   310 
   310 
   311     /**
   311     /**
   312      * Splice finally code into all endpoints of a trynode
   312      * Splice finally code into all endpoints of a trynode
   314      * @param list of rethrowing throw nodes from synthetic catch blocks
   314      * @param list of rethrowing throw nodes from synthetic catch blocks
   315      * @param finallyBody the code in the original finally block
   315      * @param finallyBody the code in the original finally block
   316      * @return new try node after splicing finally code (same if nop)
   316      * @return new try node after splicing finally code (same if nop)
   317      */
   317      */
   318     private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
   318     private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
   319         final int    finish = tryNode.getFinish();
   319         final int finish = tryNode.getFinish();
   320 
   320 
   321         assert tryNode.getFinallyBody() == null;
   321         assert tryNode.getFinallyBody() == null;
   322 
   322 
   323         final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() {
   323         final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() {
   324             final List<Node> insideTry = new ArrayList<>();
   324             final List<Node> insideTry = new ArrayList<>();
   336             }
   336             }
   337 
   337 
   338             @Override
   338             @Override
   339             public Node leaveThrowNode(final ThrowNode throwNode) {
   339             public Node leaveThrowNode(final ThrowNode throwNode) {
   340                 if (rethrows.contains(throwNode)) {
   340                 if (rethrows.contains(throwNode)) {
   341                     final List<Node> newStatements = copyFinally(finallyBody);
   341                     final List<Statement> newStatements = copyFinally(finallyBody);
   342                     if (!isTerminal(newStatements)) {
   342                     if (!isTerminal(newStatements)) {
   343                         newStatements.add(throwNode);
   343                         newStatements.add(throwNode);
   344                     }
   344                     }
   345                     return new Block(throwNode.getToken(), throwNode.getFinish(), newStatements);
   345                     return new Block(throwNode.getLineNumber(), throwNode.getToken(), throwNode.getFinish(), newStatements);
   346                 }
   346                 }
   347                 return throwNode;
   347                 return throwNode;
   348             }
   348             }
   349 
   349 
   350             @Override
   350             @Override
   358             }
   358             }
   359 
   359 
   360             @Override
   360             @Override
   361             public Node leaveReturnNode(final ReturnNode returnNode) {
   361             public Node leaveReturnNode(final ReturnNode returnNode) {
   362                 final Node  expr  = returnNode.getExpression();
   362                 final Node  expr  = returnNode.getExpression();
   363                 final List<Node> newStatements = new ArrayList<>();
   363                 final List<Statement> newStatements = new ArrayList<>();
   364 
   364 
   365                 final Node resultNode;
   365                 final Node resultNode;
   366                 if (expr != null) {
   366                 if (expr != null) {
   367                     //we need to evaluate the result of the return in case it is complex while
   367                     //we need to evaluate the result of the return in case it is complex while
   368                     //still in the try block, store it in a result value and return it afterwards
   368                     //still in the try block, store it in a result value and return it afterwards
   369                     resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
   369                     resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
   370                     newStatements.add(new ExecuteNode(new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
   370                     newStatements.add(new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
   371                 } else {
   371                 } else {
   372                     resultNode = null;
   372                     resultNode = null;
   373                 }
   373                 }
   374 
   374 
   375                 newStatements.addAll(copyFinally(finallyBody));
   375                 newStatements.addAll(copyFinally(finallyBody));
   376                 if (!isTerminal(newStatements)) {
   376                 if (!isTerminal(newStatements)) {
   377                     newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
   377                     newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
   378                 }
   378                 }
   379 
   379 
   380                 return new ExecuteNode(new Block(returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements));
   380                 return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements));
   381             }
   381             }
   382 
   382 
   383             private Node copy(final Node endpoint, final Node targetNode) {
   383             private Node copy(final Statement endpoint, final Node targetNode) {
   384                 if (!insideTry.contains(targetNode)) {
   384                 if (!insideTry.contains(targetNode)) {
   385                     final List<Node> newStatements = copyFinally(finallyBody);
   385                     final List<Statement> newStatements = copyFinally(finallyBody);
   386                     if (!isTerminal(newStatements)) {
   386                     if (!isTerminal(newStatements)) {
   387                         newStatements.add(endpoint);
   387                         newStatements.add(endpoint);
   388                     }
   388                     }
   389                     return new ExecuteNode(new Block(endpoint.getToken(), finish, newStatements));
   389                     return new ExecuteNode(endpoint.getLineNumber(), endpoint.getToken(), endpoint.getFinish(), new Block(endpoint.getLineNumber(), endpoint.getToken(), finish, newStatements));
   390                 }
   390                 }
   391                 return endpoint;
   391                 return endpoint;
   392             }
   392             }
   393         });
   393         });
   394 
   394 
   395         addStatement(newTryNode);
   395         addStatement(newTryNode);
   396         for (final Node statement : finallyBody.getStatements()) {
   396         for (final Node statement : finallyBody.getStatements()) {
   397             addStatement(statement);
   397             addStatement((Statement)statement);
   398         }
   398         }
   399 
   399 
   400         return newTryNode;
   400         return newTryNode;
   401     }
   401     }
   402 
   402 
   446         assert rethrows.size() == 1;
   446         assert rethrows.size() == 1;
   447 
   447 
   448         if (tryNode.getCatchBlocks().isEmpty()) {
   448         if (tryNode.getCatchBlocks().isEmpty()) {
   449             newTryNode = tryNode.setFinallyBody(null);
   449             newTryNode = tryNode.setFinallyBody(null);
   450         } else {
   450         } else {
   451             Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), new ArrayList<Node>(Arrays.asList(tryNode.setFinallyBody(null))));
   451             Block outerBody = new Block(tryNode.getLineNumber(), tryNode.getToken(), tryNode.getFinish(), new ArrayList<Statement>(Arrays.asList(tryNode.setFinallyBody(null))));
   452             newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
   452             newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
   453         }
   453         }
   454 
   454 
   455         newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null);
   455         newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null);
   456 
   456 
   463 
   463 
   464     @Override
   464     @Override
   465     public Node leaveVarNode(final VarNode varNode) {
   465     public Node leaveVarNode(final VarNode varNode) {
   466         addStatement(varNode);
   466         addStatement(varNode);
   467         if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && getLexicalContext().getCurrentFunction().isProgram()) {
   467         if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && getLexicalContext().getCurrentFunction().isProgram()) {
   468             new ExecuteNode(varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
   468             new ExecuteNode(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
   469         }
   469         }
   470         return varNode;
   470         return varNode;
   471     }
   471     }
   472 
   472 
   473     @Override
   473     @Override
   475         final Node test  = whileNode.getTest();
   475         final Node test  = whileNode.getTest();
   476         final Block body = whileNode.getBody();
   476         final Block body = whileNode.getBody();
   477 
   477 
   478         if (conservativeAlwaysTrue(test)) {
   478         if (conservativeAlwaysTrue(test)) {
   479             //turn it into a for node without a test.
   479             //turn it into a for node without a test.
   480             final ForNode forNode = (ForNode)new ForNode(whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
   480             final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
   481             getLexicalContext().replace(whileNode, forNode);
   481             getLexicalContext().replace(whileNode, forNode);
   482             return forNode;
   482             return forNode;
   483         }
   483         }
   484 
   484 
   485          return addStatement(checkEscape(whileNode));
   485          return addStatement(checkEscape(whileNode));
   486     }
   486     }
   487 
   487 
   488     @Override
   488     @Override
   489     public Node leaveWithNode(final WithNode withNode) {
   489     public Node leaveWithNode(final WithNode withNode) {
   490         return addStatement(withNode);
   490         return addStatement(withNode);
   491     }
       
   492 
       
   493     @Override
       
   494     public Node leaveDELETE(final UnaryNode unaryNode) {
       
   495         final Node rhs = unaryNode.rhs();
       
   496         if (rhs instanceof IdentNode || rhs instanceof BaseNode) {
       
   497             return unaryNode;
       
   498         }
       
   499         addStatement(new ExecuteNode(rhs));
       
   500         return LiteralNode.newInstance(unaryNode, true);
       
   501     }
   491     }
   502 
   492 
   503     /**
   493     /**
   504      * Given a function node that is a callee in a CallNode, replace it with
   494      * Given a function node that is a callee in a CallNode, replace it with
   505      * the appropriate marker function. This is used by {@link CodeGenerator}
   495      * the appropriate marker function. This is used by {@link CodeGenerator}
   614         }
   604         }
   615         return loopNode;
   605         return loopNode;
   616     }
   606     }
   617 
   607 
   618 
   608 
   619     private Node addStatement(final Node statement) {
   609     private Node addStatement(final Statement statement) {
   620         ((BlockLexicalContext)getLexicalContext()).appendStatement(statement);
   610         ((BlockLexicalContext)getLexicalContext()).appendStatement(statement);
   621         return statement;
   611         return statement;
   622     }
   612     }
   623 
   613 
   624     /**
   614     /**