aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/codegen/Lower.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/codegen/Lower.java')
-rw-r--r--src/jdk/nashorn/internal/codegen/Lower.java224
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;
}
-
}