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.java202
1 files changed, 114 insertions, 88 deletions
diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java
index a30e79a4..1f0e5ae3 100644
--- a/src/jdk/nashorn/internal/codegen/Lower.java
+++ b/src/jdk/nashorn/internal/codegen/Lower.java
@@ -51,14 +51,15 @@ import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
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.JumpToInlinedFinally;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReturnNode;
@@ -72,7 +73,7 @@ import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Context;
@@ -115,17 +116,11 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
for (final Statement statement : statements) {
if (!terminated) {
newStatements.add(statement);
- if (statement.isTerminal() || statement instanceof BreakNode || statement instanceof ContinueNode) { //TODO hasGoto? But some Loops are hasGoto too - why?
+ if (statement.isTerminal() || statement instanceof JumpStatement) { //TODO hasGoto? But some Loops are hasGoto too - why?
terminated = true;
}
} else {
- statement.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
- @Override
- public boolean enterVarNode(final VarNode varNode) {
- newStatements.add(varNode.setInit(null));
- return false;
- }
- });
+ FoldConstants.extractVarNodesFromDeadCode(statement, newStatements);
}
}
return newStatements;
@@ -183,6 +178,12 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
}
@Override
+ public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) {
+ addStatement(jumpToInlinedFinally);
+ return false;
+ }
+
+ @Override
public boolean enterEmptyNode(final EmptyNode emptyNode) {
return false;
}
@@ -192,7 +193,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
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;
+ assert indexNode.isIndex();
return new AccessNode(indexNode.getToken(), indexNode.getFinish(), indexNode.getBase(), name);
}
return super.leaveIndexNode(indexNode);
@@ -258,7 +259,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
log.info("END FunctionNode: ", functionNode.getName());
- return functionNode.setState(lc, CompilationState.LOWERED);
+ return functionNode;
}
@Override
@@ -318,8 +319,9 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
return addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
}
- private static Node ensureUniqueNamesIn(final Node node) {
- return node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @SuppressWarnings("unchecked")
+ private static <T extends Node> T ensureUniqueNamesIn(final T node) {
+ return (T)node.accept(new SimpleNodeVisitor() {
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
final String name = functionNode.getName();
@@ -333,15 +335,15 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
});
}
- private static List<Statement> copyFinally(final Block finallyBody) {
+ private static Block createFinallyBlock(final Block finallyBody) {
final List<Statement> newStatements = new ArrayList<>();
for (final Statement statement : finallyBody.getStatements()) {
- newStatements.add((Statement)ensureUniqueNamesIn(statement));
+ newStatements.add(statement);
if (statement.hasTerminalFlags()) {
- return newStatements;
+ break;
}
}
- return newStatements;
+ return finallyBody.setStatements(null, newStatements);
}
private Block catchAllBlock(final TryNode tryNode) {
@@ -367,28 +369,24 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
}
- private static boolean isTerminal(final List<Statement> statements) {
- return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags();
+ private static boolean isTerminalFinally(final Block finallyBlock) {
+ return finallyBlock.getLastStatement().hasTerminalFlags();
}
/**
* Splice finally code into all endpoints of a trynode
* @param tryNode the try node
- * @param rethrows list of rethrowing throw nodes from synthetic catch blocks
+ * @param rethrow the rethrowing throw nodes from the synthetic catch block
* @param finallyBody the code in the original finally block
* @return new try node after splicing finally code (same if nop)
*/
- private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
+ private TryNode spliceFinally(final TryNode tryNode, final ThrowNode rethrow, final Block finallyBody) {
assert tryNode.getFinallyBody() == null;
- final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
- final List<Node> insideTry = new ArrayList<>();
-
- @Override
- public boolean enterDefault(final Node node) {
- insideTry.add(node);
- return true;
- }
+ final Block finallyBlock = createFinallyBlock(finallyBody);
+ final ArrayList<Block> inlinedFinallies = new ArrayList<>();
+ final FunctionNode fn = lc.getCurrentFunction();
+ final TryNode newTryNode = (TryNode)tryNode.accept(new SimpleNodeVisitor() {
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
@@ -398,12 +396,8 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveThrowNode(final ThrowNode throwNode) {
- if (rethrows.contains(throwNode)) {
- final List<Statement> newStatements = copyFinally(finallyBody);
- if (!isTerminal(newStatements)) {
- newStatements.add(throwNode);
- }
- return BlockStatement.createReplacement(throwNode, newStatements);
+ if (rethrow == throwNode) {
+ return new BlockStatement(prependFinally(finallyBlock, throwNode));
}
return throwNode;
}
@@ -419,62 +413,98 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
}
private Node leaveJumpStatement(final JumpStatement jump) {
- return copy(jump, (Node)jump.getTarget(Lower.this.lc));
- }
-
- @Override
- public Node leaveReturnNode(final ReturnNode returnNode) {
- final Expression expr = returnNode.getExpression();
- final List<Statement> newStatements = new ArrayList<>();
-
- final Expression resultNode;
- if (expr != null) {
- //we need to evaluate the result of the return in case it is complex while
- //still in the try block, store it in a result value and return it afterwards
- resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
- newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
- } else {
- resultNode = null;
- }
+ // NOTE: leaveJumpToInlinedFinally deliberately does not delegate to this method, only break and
+ // continue are edited. JTIF nodes should not be changed, rather the surroundings of
+ // break/continue/return that were moved into the inlined finally block itself will be changed.
- newStatements.addAll(copyFinally(finallyBody));
- if (!isTerminal(newStatements)) {
- newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
+ // If this visitor's lc doesn't find the target of the jump, it means it's external to the try block.
+ if (jump.getTarget(lc) == null) {
+ return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, jump));
}
-
- return BlockStatement.createReplacement(returnNode, lc.getCurrentBlock().getFinish(), newStatements);
+ return jump;
}
- private Node copy(final Statement endpoint, final Node targetNode) {
- if (!insideTry.contains(targetNode)) {
- final List<Statement> newStatements = copyFinally(finallyBody);
- if (!isTerminal(newStatements)) {
- newStatements.add(endpoint);
+ @Override
+ public Node leaveReturnNode(final ReturnNode returnNode) {
+ final Expression expr = returnNode.getExpression();
+ if (isTerminalFinally(finallyBlock)) {
+ if (expr == null) {
+ // Terminal finally; no return expression.
+ return createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock));
}
- return BlockStatement.createReplacement(endpoint, tryNode.getFinish(), newStatements);
+ // Terminal finally; has a return expression.
+ final List<Statement> newStatements = new ArrayList<>(2);
+ final int retLineNumber = returnNode.getLineNumber();
+ final long retToken = returnNode.getToken();
+ // Expression is evaluated for side effects.
+ newStatements.add(new ExpressionStatement(retLineNumber, retToken, returnNode.getFinish(), expr));
+ newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock)));
+ return new BlockStatement(retLineNumber, new Block(retToken, finallyBlock.getFinish(), newStatements));
+ } else if (expr == null || expr instanceof PrimitiveLiteralNode<?> || (expr instanceof IdentNode && RETURN.symbolName().equals(((IdentNode)expr).getName()))) {
+ // Nonterminal finally; no return expression, or returns a primitive literal, or returns :return.
+ // Just move the return expression into the finally block.
+ return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode));
+ } else {
+ // We need to evaluate the result of the return in case it is complex while still in the try block,
+ // store it in :return, and return it afterwards.
+ final List<Statement> newStatements = new ArrayList<>();
+ final int retLineNumber = returnNode.getLineNumber();
+ final long retToken = returnNode.getToken();
+ final int retFinish = returnNode.getFinish();
+ final Expression resultNode = new IdentNode(expr.getToken(), expr.getFinish(), RETURN.symbolName());
+ // ":return = <expr>;"
+ newStatements.add(new ExpressionStatement(retLineNumber, retToken, retFinish, new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
+ // inline finally and end it with "return :return;"
+ newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode.setExpression(resultNode))));
+ return new BlockStatement(retLineNumber, new Block(retToken, retFinish, newStatements));
}
- return endpoint;
}
});
-
- addStatement(newTryNode);
- for (final Node statement : finallyBody.getStatements()) {
- addStatement((Statement)statement);
- }
+ addStatement(inlinedFinallies.isEmpty() ? newTryNode : newTryNode.setInlinedFinallies(lc, inlinedFinallies));
+ // TODO: if finallyStatement is terminal, we could just have sites of inlined finallies jump here.
+ addStatement(new BlockStatement(finallyBlock));
return newTryNode;
}
+ private static JumpToInlinedFinally createJumpToInlinedFinally(final FunctionNode fn, final List<Block> inlinedFinallies, final Block finallyBlock) {
+ final String labelName = fn.uniqueName(":finally");
+ final long token = finallyBlock.getToken();
+ final int finish = finallyBlock.getFinish();
+ inlinedFinallies.add(new Block(token, finish, new LabelNode(finallyBlock.getFirstStatementLineNumber(),
+ token, finish, labelName, finallyBlock)));
+ return new JumpToInlinedFinally(labelName);
+ }
+
+ private static Block prependFinally(final Block finallyBlock, final Statement statement) {
+ final Block inlinedFinally = ensureUniqueNamesIn(finallyBlock);
+ if (isTerminalFinally(finallyBlock)) {
+ return inlinedFinally;
+ }
+ final List<Statement> stmts = inlinedFinally.getStatements();
+ final List<Statement> newStmts = new ArrayList<>(stmts.size() + 1);
+ newStmts.addAll(stmts);
+ newStmts.add(statement);
+ return new Block(inlinedFinally.getToken(), statement.getFinish(), newStmts);
+ }
+
@Override
public Node leaveTryNode(final TryNode tryNode) {
final Block finallyBody = tryNode.getFinallyBody();
-
- if (finallyBody == null) {
- return addStatement(ensureUnconditionalCatch(tryNode));
+ TryNode newTryNode = tryNode.setFinallyBody(lc, null);
+
+ // No finally or empty finally
+ if (finallyBody == null || finallyBody.getStatementCount() == 0) {
+ final List<CatchNode> catches = newTryNode.getCatches();
+ if (catches == null || catches.isEmpty()) {
+ // A completely degenerate try block: empty finally, no catches. Replace it with try body.
+ return addStatement(new BlockStatement(tryNode.getBody()));
+ }
+ return addStatement(ensureUnconditionalCatch(newTryNode));
}
/*
- * create a new trynode
+ * create a new try node
* if we have catches:
*
* try try
@@ -485,7 +515,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
* catchall
* rethrow
*
- * otheriwse
+ * otherwise
*
* try try
* x x
@@ -496,12 +526,10 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
* now splice in finally code wherever needed
*
*/
- TryNode newTryNode;
-
final Block catchAll = catchAllBlock(tryNode);
- final List<ThrowNode> rethrows = new ArrayList<>();
- catchAll.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ final List<ThrowNode> rethrows = new ArrayList<>(1);
+ catchAll.accept(new SimpleNodeVisitor() {
@Override
public boolean enterThrowNode(final ThrowNode throwNode) {
rethrows.add(throwNode);
@@ -510,20 +538,18 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
});
assert rethrows.size() == 1;
- if (tryNode.getCatchBlocks().isEmpty()) {
- newTryNode = tryNode.setFinallyBody(null);
- } else {
- final Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), ensureUnconditionalCatch(tryNode.setFinallyBody(null)));
- newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
+ if (!tryNode.getCatchBlocks().isEmpty()) {
+ final Block outerBody = new Block(newTryNode.getToken(), newTryNode.getFinish(), ensureUnconditionalCatch(newTryNode));
+ newTryNode = newTryNode.setBody(lc, outerBody).setCatchBlocks(lc, null);
}
- newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null);
+ newTryNode = newTryNode.setCatchBlocks(lc, Arrays.asList(catchAll));
/*
* Now that the transform is done, we have to go into the try and splice
* the finally block in front of any statement that is outside the try
*/
- return spliceFinally(newTryNode, rethrows, finallyBody);
+ return (TryNode)lc.replace(tryNode, spliceFinally(newTryNode, rethrows.get(0), finallyBody));
}
private TryNode ensureUnconditionalCatch(final TryNode tryNode) {
@@ -535,7 +561,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
final List<Block> newCatchBlocks = new ArrayList<>(tryNode.getCatchBlocks());
newCatchBlocks.add(catchAllBlock(tryNode));
- return tryNode.setCatchBlocks(newCatchBlocks);
+ return tryNode.setCatchBlocks(lc, newCatchBlocks);
}
@Override
@@ -648,7 +674,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) {
final List<Node> escapes = new ArrayList<>();
- loopBody.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ loopBody.accept(new SimpleNodeVisitor() {
@Override
public Node leaveBreakNode(final BreakNode node) {
escapes.add(node);