diff options
Diffstat (limited to 'src/jdk/nashorn/internal/codegen/Lower.java')
-rw-r--r-- | src/jdk/nashorn/internal/codegen/Lower.java | 224 |
1 files changed, 141 insertions, 83 deletions
diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java index bf6872fe..a30e79a4 100644 --- a/src/jdk/nashorn/internal/codegen/Lower.java +++ b/src/jdk/nashorn/internal/codegen/Lower.java @@ -27,12 +27,15 @@ package jdk.nashorn.internal.codegen; import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL; import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; -import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; +import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.regex.Pattern; +import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; @@ -40,6 +43,7 @@ import jdk.nashorn.internal.ir.BlockLexicalContext; import jdk.nashorn.internal.ir.BlockStatement; import jdk.nashorn.internal.ir.BreakNode; import jdk.nashorn.internal.ir.CallNode; +import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.EmptyNode; @@ -50,12 +54,15 @@ import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; +import jdk.nashorn.internal.ir.IndexNode; +import jdk.nashorn.internal.ir.JumpStatement; import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ReturnNode; +import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -68,10 +75,12 @@ import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; -import jdk.nashorn.internal.runtime.CodeInstaller; -import jdk.nashorn.internal.runtime.DebugLogger; -import jdk.nashorn.internal.runtime.ScriptRuntime; +import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Source; +import jdk.nashorn.internal.runtime.logging.DebugLogger; +import jdk.nashorn.internal.runtime.logging.Loggable; +import jdk.nashorn.internal.runtime.logging.Logger; /** * Lower to more primitive operations. After lowering, an AST still has no symbols @@ -82,18 +91,19 @@ import jdk.nashorn.internal.runtime.Source; * harder and context dependent to do any code copying after symbols have been * finalized. */ +@Logger(name="lower") +final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Loggable { -final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { + private final DebugLogger log; - private static final DebugLogger LOG = new DebugLogger("lower"); - - // needed only to get unique eval id - private final CodeInstaller<?> installer; + // Conservative pattern to test if element names consist of characters valid for identifiers. + // This matches any non-zero length alphanumeric string including _ and $ and not starting with a digit. + private static Pattern SAFE_PROPERTY_NAME = Pattern.compile("[a-zA-Z_$][\\w$]*"); /** * Constructor. */ - Lower(final CodeInstaller<?> installer) { + Lower(final Compiler compiler) { super(new BlockLexicalContext() { @Override @@ -136,40 +146,18 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { return block.setIsTerminal(this, false); } }); - this.installer = installer; + + this.log = initLogger(compiler.getContext()); } @Override - public boolean enterBlock(final Block block) { - final FunctionNode function = lc.getCurrentFunction(); - if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) { - new ExpressionStatement(function.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this); - } - return true; + public DebugLogger getLogger() { + return log; } @Override - public Node leaveBlock(final Block block) { - //now we have committed the entire statement list to the block, but we need to truncate - //whatever is after the last terminal. block append won't append past it - - - if (lc.isFunctionBody()) { - final FunctionNode currentFunction = lc.getCurrentFunction(); - final boolean isProgram = currentFunction.isProgram(); - final Statement last = lc.getLastStatement(); - final ReturnNode returnNode = new ReturnNode( - last == null ? currentFunction.getLineNumber() : last.getLineNumber(), //TODO? - currentFunction.getToken(), - currentFunction.getFinish(), - isProgram ? - compilerConstant(RETURN) : - LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)); - - returnNode.accept(this); - } - - return block; + public DebugLogger initLogger(final Context context) { + return context.getLogger(this.getClass()); } @Override @@ -200,6 +188,28 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { } @Override + public Node leaveIndexNode(final IndexNode indexNode) { + final String name = getConstantPropertyName(indexNode.getIndex()); + if (name != null) { + // If index node is a constant property name convert index node to access node. + assert Token.descType(indexNode.getToken()) == TokenType.LBRACKET; + return new AccessNode(indexNode.getToken(), indexNode.getFinish(), indexNode.getBase(), name); + } + return super.leaveIndexNode(indexNode); + } + + // If expression is a primitive literal that is not an array index and does return its string value. Else return null. + private static String getConstantPropertyName(final Expression expression) { + if (expression instanceof LiteralNode.PrimitiveLiteralNode) { + final Object value = ((LiteralNode) expression).getValue(); + if (value instanceof String && SAFE_PROPERTY_NAME.matcher((String) value).matches()) { + return (String) value; + } + } + return null; + } + + @Override public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { final Expression expr = expressionStatement.getExpression(); ExpressionStatement node = expressionStatement; @@ -222,7 +232,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { } @Override - public Node leaveBlockStatement(BlockStatement blockStatement) { + public Node leaveBlockStatement(final BlockStatement blockStatement) { return addStatement(blockStatement); } @@ -230,22 +240,24 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { public Node leaveForNode(final ForNode forNode) { ForNode newForNode = forNode; - final Node test = forNode.getTest(); - if (!forNode.isForIn() && conservativeAlwaysTrue(test)) { + final Expression test = forNode.getTest(); + if (!forNode.isForIn() && isAlwaysTrue(test)) { newForNode = forNode.setTest(lc, null); } - return addStatement(checkEscape(newForNode)); - } - - @Override - public boolean enterFunctionNode(final FunctionNode functionNode) { - return !functionNode.isLazy(); + newForNode = checkEscape(newForNode); + if(newForNode.isForIn()) { + // Wrap it in a block so its internally created iterator is restricted in scope + addStatementEnclosedInBlock(newForNode); + } else { + addStatement(newForNode); + } + return newForNode; } @Override public Node leaveFunctionNode(final FunctionNode functionNode) { - LOG.info("END FunctionNode: ", functionNode.getName()); + log.info("END FunctionNode: ", functionNode.getName()); return functionNode.setState(lc, CompilationState.LOWERED); } @@ -255,6 +267,16 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { } @Override + public Node leaveIN(final BinaryNode binaryNode) { + return new RuntimeNode(binaryNode); + } + + @Override + public Node leaveINSTANCEOF(final BinaryNode binaryNode) { + return new RuntimeNode(binaryNode); + } + + @Override public Node leaveLabelNode(final LabelNode labelNode) { return addStatement(labelNode); } @@ -265,16 +287,35 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { return returnNode; } + @Override + public Node leaveCaseNode(final CaseNode caseNode) { + // Try to represent the case test as an integer + final Node test = caseNode.getTest(); + if (test instanceof LiteralNode) { + final LiteralNode<?> lit = (LiteralNode<?>)test; + if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) { + if (JSType.isRepresentableAsInt(lit.getNumber())) { + return caseNode.setTest((Expression)LiteralNode.newInstance(lit, lit.getInt32()).accept(this)); + } + } + } + return caseNode; + } @Override public Node leaveSwitchNode(final SwitchNode switchNode) { - return addStatement(switchNode); + if(!switchNode.isUniqueInteger()) { + // Wrap it in a block so its internally created tag is restricted in scope + addStatementEnclosedInBlock(switchNode); + } else { + addStatement(switchNode); + } + return switchNode; } @Override public Node leaveThrowNode(final ThrowNode throwNode) { - addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor - return throwNode; + return addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor } private static Node ensureUniqueNamesIn(final Node node) { @@ -308,12 +349,12 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { final long token = tryNode.getToken(); final int finish = tryNode.getFinish(); - final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName("catch_all")); + final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName(CompilerConstants.EXCEPTION_PREFIX.symbolName())); - final Block catchBody = new Block(token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW)); + final Block catchBody = new Block(token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), true)); assert catchBody.isTerminal(); //ends with throw, so terminal - final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody, CatchNode.IS_SYNTHETIC_RETHROW); + final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody, true); final Block catchAllBlock = new Block(token, finish, catchAllNode); //catchallblock -> catchallnode (catchnode) -> exception -> throw @@ -369,12 +410,16 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { @Override public Node leaveBreakNode(final BreakNode breakNode) { - return copy(breakNode, (Node)Lower.this.lc.getBreakable(breakNode.getLabel())); + return leaveJumpStatement(breakNode); } @Override public Node leaveContinueNode(final ContinueNode continueNode) { - return copy(continueNode, Lower.this.lc.getContinueTo(continueNode.getLabel())); + return leaveJumpStatement(continueNode); + } + + private Node leaveJumpStatement(final JumpStatement jump) { + return copy(jump, (Node)jump.getTarget(Lower.this.lc)); } @Override @@ -425,7 +470,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { final Block finallyBody = tryNode.getFinallyBody(); if (finallyBody == null) { - return addStatement(tryNode); + return addStatement(ensureUnconditionalCatch(tryNode)); } /* @@ -468,7 +513,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { if (tryNode.getCatchBlocks().isEmpty()) { newTryNode = tryNode.setFinallyBody(null); } else { - Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), tryNode.setFinallyBody(null)); + final Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), ensureUnconditionalCatch(tryNode.setFinallyBody(null))); newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null); } @@ -481,6 +526,18 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { return spliceFinally(newTryNode, rethrows, finallyBody); } + private TryNode ensureUnconditionalCatch(final TryNode tryNode) { + final List<CatchNode> catches = tryNode.getCatches(); + if(catches == null || catches.isEmpty() || catches.get(catches.size() - 1).getExceptionCondition() == null) { + return tryNode; + } + // If the last catch block is conditional, add an unconditional rethrow block + final List<Block> newCatchBlocks = new ArrayList<>(tryNode.getCatchBlocks()); + + newCatchBlocks.add(catchAllBlock(tryNode)); + return tryNode.setCatchBlocks(newCatchBlocks); + } + @Override public Node leaveVarNode(final VarNode varNode) { addStatement(varNode); @@ -492,12 +549,12 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { @Override public Node leaveWhileNode(final WhileNode whileNode) { - final Node test = whileNode.getTest(); + final Expression test = whileNode.getTest(); final Block body = whileNode.getBody(); - if (conservativeAlwaysTrue(test)) { + if (isAlwaysTrue(test)) { //turn it into a for node without a test. - final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this); + final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, 0).accept(this); lc.replace(whileNode, forNode); return forNode; } @@ -535,16 +592,13 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { private String evalLocation(final IdentNode node) { final Source source = lc.getCurrentFunction().getSource(); final int pos = node.position(); - // Code installer is null when running with --compile-only, use 0 as id in that case - final long id = installer == null ? 0 : installer.getUniqueEvalId(); return new StringBuilder(). append(source.getName()). append('#'). append(source.getLine(pos)). append(':'). append(source.getColumn(pos)). - append("<eval>@"). - append(id). + append("<eval>"). toString(); } @@ -571,23 +625,17 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { // 'eval' call with at least one argument if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) { - final FunctionNode currentFunction = lc.getCurrentFunction(); - return callNode.setEvalArgs( - new CallNode.EvalArgs( - (Expression)ensureUniqueNamesIn(args.get(0)).accept(this), - compilerConstant(THIS), - evalLocation(callee), - currentFunction.isStrict())); + final List<Expression> evalArgs = new ArrayList<>(args.size()); + for(final Expression arg: args) { + evalArgs.add((Expression)ensureUniqueNamesIn(arg).accept(this)); + } + return callNode.setEvalArgs(new CallNode.EvalArgs(evalArgs, evalLocation(callee))); } } return callNode; } - private static boolean conservativeAlwaysTrue(final Node node) { - return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue())); - } - /** * Helper that given a loop body makes sure that it is not terminal if it * has a continue that leads to the loop header or to outer loops' loop @@ -610,7 +658,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { @Override public Node leaveContinueNode(final ContinueNode node) { // all inner loops have been popped. - if (lex.contains(lex.getContinueTo(node.getLabel()))) { + if (lex.contains(node.getTarget(lex))) { escapes.add(node); } return node; @@ -620,10 +668,11 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { return !escapes.isEmpty(); } - private LoopNode checkEscape(final LoopNode loopNode) { + @SuppressWarnings("unchecked") + private <T extends LoopNode> T checkEscape(final T loopNode) { final boolean escapes = controlFlowEscapes(lc, loopNode.getBody()); if (escapes) { - return loopNode. + return (T)loopNode. setBody(lc, loopNode.getBody().setIsTerminal(lc, false)). setControlFlowEscapes(lc, escapes); } @@ -636,6 +685,14 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { return statement; } + private void addStatementEnclosedInBlock(final Statement stmt) { + BlockStatement b = BlockStatement.createReplacement(stmt, Collections.<Statement>singletonList(stmt)); + if(stmt.isTerminal()) { + b = b.setBlock(b.getBlock().setIsTerminal(null, true)); + } + addStatement(b); + } + /** * An internal expression has a symbol that is tagged internal. Check if * this is such a node @@ -644,7 +701,10 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { * @return true if internal, false otherwise */ private static boolean isInternalExpression(final Expression expression) { - final Symbol symbol = expression.getSymbol(); + if (!(expression instanceof IdentNode)) { + return false; + } + final Symbol symbol = ((IdentNode)expression).getSymbol(); return symbol != null && symbol.isInternal(); } @@ -656,8 +716,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { * @return true if an assignment to eval result, false otherwise */ private static boolean isEvalResultAssignment(final Node expression) { - Node e = expression; - assert e.tokenType() != TokenType.DISCARD; //there are no discards this early anymore + final Node e = expression; if (e instanceof BinaryNode) { final Node lhs = ((BinaryNode)e).lhs(); if (lhs instanceof IdentNode) { @@ -666,5 +725,4 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { } return false; } - } |