aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/codegen')
-rw-r--r--src/jdk/nashorn/internal/codegen/Attr.java881
-rw-r--r--src/jdk/nashorn/internal/codegen/ClassEmitter.java39
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGenerator.java1369
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilationPhase.java260
-rw-r--r--src/jdk/nashorn/internal/codegen/Compiler.java62
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilerConstants.java73
-rw-r--r--src/jdk/nashorn/internal/codegen/FieldObjectCreator.java5
-rw-r--r--src/jdk/nashorn/internal/codegen/FinalizeTypes.java212
-rw-r--r--src/jdk/nashorn/internal/codegen/FoldConstants.java16
-rw-r--r--src/jdk/nashorn/internal/codegen/Frame.java196
-rw-r--r--src/jdk/nashorn/internal/codegen/Lower.java935
-rw-r--r--src/jdk/nashorn/internal/codegen/MethodEmitter.java217
-rw-r--r--src/jdk/nashorn/internal/codegen/Namespace.java8
-rw-r--r--src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java32
-rw-r--r--src/jdk/nashorn/internal/codegen/ObjectCreator.java3
-rw-r--r--src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java100
-rw-r--r--src/jdk/nashorn/internal/codegen/Splitter.java264
-rw-r--r--src/jdk/nashorn/internal/codegen/WeighNodes.java39
-rw-r--r--src/jdk/nashorn/internal/codegen/types/BooleanType.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/types/IntType.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/types/LongType.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/types/NumberType.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/types/Type.java13
23 files changed, 2114 insertions, 2634 deletions
diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java
index 9ecf7c89..9b17c47f 100644
--- a/src/jdk/nashorn/internal/codegen/Attr.java
+++ b/src/jdk/nashorn/internal/codegen/Attr.java
@@ -25,14 +25,16 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
+import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.ir.Symbol.IS_LET;
@@ -42,18 +44,18 @@ import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
-import jdk.nashorn.internal.ir.CallNode.EvalArgs;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -62,6 +64,7 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.Node;
@@ -76,6 +79,7 @@ import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
+import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
@@ -105,21 +109,22 @@ import jdk.nashorn.internal.runtime.ScriptObject;
*/
final class Attr extends NodeOperatorVisitor {
+
/**
* Local definitions in current block (to discriminate from function
* declarations always defined in the function scope. This is for
* "can be undefined" analysis.
*/
- private Set<String> localDefs;
+ private final Deque<Set<String>> localDefs;
/**
* Local definitions in current block to guard against cases like
* NASHORN-467 when things can be undefined as they are used before
* their local var definition. *sigh* JavaScript...
*/
- private Set<String> localUses;
+ private final Deque<Set<String>> localUses;
- private final LexicalContext lexicalContext = new LexicalContext();
+ private final Deque<Type> returnTypes;
private static final DebugLogger LOG = new DebugLogger("attr");
private static final boolean DEBUG = LOG.isEnabled();
@@ -128,10 +133,13 @@ final class Attr extends NodeOperatorVisitor {
* Constructor.
*/
Attr() {
+ localDefs = new ArrayDeque<>();
+ localUses = new ArrayDeque<>();
+ returnTypes = new ArrayDeque<>();
}
@Override
- protected Node enterDefault(final Node node) {
+ protected boolean enterDefault(final Node node) {
return start(node);
}
@@ -142,76 +150,127 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveAccessNode(final AccessNode accessNode) {
- newTemporary(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
+ ensureSymbol(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
end(accessNode);
return accessNode;
}
- @Override
- public Node enterBlock(final Block block) {
- lexicalContext.push(block);
- start(block);
+ private void enterFunctionBody() {
- final Set<String> savedLocalDefs = localDefs;
- final Set<String> savedLocalUses = localUses;
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ final Block body = getLexicalContext().getCurrentBlock();
+ initCallee(body);
+ initThis(body);
+ if (functionNode.isVarArg()) {
+ initVarArg(body, functionNode.needsArguments());
+ }
- block.setFrame(getCurrentFunctionNode().pushFrame());
+ initParameters(functionNode, body);
+ initScope(body);
+ initReturn(body);
- try {
- // a block starts out by copying the local defs and local uses
- // from the outer level. But we need the copies, as when we
- // leave the block the def and use sets given upon entry must
- // be restored
- localDefs = new HashSet<>(savedLocalDefs);
- localUses = new HashSet<>(savedLocalUses);
-
- block.visitStatements(this);
- } finally {
- localDefs = savedLocalDefs;
- localUses = savedLocalUses;
+ if (functionNode.isProgram()) {
+ initFromPropertyMap(body);
+ } else if(!functionNode.isDeclared()) {
+ // It's neither declared nor program - it's a function expression then; assign it a self-symbol.
- getCurrentFunctionNode().popFrame();
+ if (functionNode.getSymbol() != null) {
+ // a temporary left over from an earlier pass when the function was lazy
+ assert functionNode.getSymbol().isTemp();
+ // remove it
+ functionNode.setSymbol(null);
+ }
+ final boolean anonymous = functionNode.isAnonymous();
+ final String name = anonymous ? null : functionNode.getIdent().getName();
+ if (anonymous || body.getExistingSymbol(name) != null) {
+ // The function is either anonymous, or another local identifier already trumps its name on entry:
+ // either it has the same name as one of its parameters, or is named "arguments" and also references the
+ // "arguments" identifier in its body.
+ ensureSymbol(functionNode, Type.typeFor(ScriptFunction.class), functionNode);
+ } else {
+ final Symbol selfSymbol = defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF, functionNode);
+ assert selfSymbol.isFunctionSelf();
+ newType(selfSymbol, Type.OBJECT);
+ }
}
- end(block);
+ /*
+ * This pushes all declarations (except for non-statements, i.e. for
+ * node temporaries) to the top of the function scope. This way we can
+ * get around problems like
+ *
+ * while (true) {
+ * break;
+ * if (true) {
+ * var s;
+ * }
+ * }
+ *
+ * to an arbitrary nesting depth.
+ *
+ * @see NASHORN-73
+ */
- lexicalContext.pop(block);
- return null;
- }
+ // This visitor will assign symbol to all declared variables, except function declarations (which are taken care
+ // in a separate step above) and "var" declarations in for loop initializers.
+ body.accept(new NodeOperatorVisitor() {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode nestedFn) {
+ return false;
+ }
- @Override
- public Node enterCallNode(final CallNode callNode) {
- start(callNode);
+ @Override
+ public boolean enterVarNode(final VarNode varNode) {
- callNode.getFunction().accept(this);
+ // any declared symbols that aren't visited need to be typed as well, hence the list
- final List<Node> acceptedArgs = new ArrayList<>(callNode.getArgs().size());
- for (final Node arg : callNode.getArgs()) {
- LOG.info("Doing call arg " + arg);
- acceptedArgs.add(arg.accept(this));
- }
- callNode.setArgs(acceptedArgs);
+ if (varNode.isStatement()) {
- final EvalArgs evalArgs = callNode.getEvalArgs();
- if (evalArgs != null) {
- evalArgs.setCode(evalArgs.getCode().accept(this));
+ final IdentNode ident = varNode.getName();
+ final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident));
+ functionNode.addDeclaredSymbol(symbol);
+ if (varNode.isFunctionDeclaration()) {
+ newType(symbol, FunctionNode.FUNCTION_TYPE);
+ }
+ }
+ return false;
+ }
+ });
+ }
- final IdentNode thisNode = new IdentNode(getCurrentFunctionNode().getThisNode());
- assert thisNode.getSymbol() != null; //should copy attributed symbol and that's it
- evalArgs.setThis(thisNode);
+ @Override
+ public boolean enterBlock(final Block block) {
+ start(block);
+
+ if (getLexicalContext().isFunctionBody()) {
+ enterFunctionBody();
}
+ pushLocalsBlock();
- newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later
+ return true;
+ }
- end(callNode);
+ @Override
+ public Node leaveBlock(final Block block) {
+ popLocals();
+ return end(block);
+ }
- return null;
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ ensureSymbol(callNode.getType(), callNode);
+ return end(callNode);
}
@Override
- public Node enterCatchNode(final CatchNode catchNode) {
+ public boolean enterCallNode(final CallNode callNode) {
+ return start(callNode);
+ }
+
+ @Override
+ public boolean enterCatchNode(final CatchNode catchNode) {
final IdentNode exception = catchNode.getException();
- final Block block = getCurrentBlock();
+ final Block block = getLexicalContext().getCurrentBlock();
start(catchNode);
@@ -220,7 +279,7 @@ final class Attr extends NodeOperatorVisitor {
newType(def, Type.OBJECT);
addLocalDef(exception.getName());
- return catchNode;
+ return true;
}
/**
@@ -240,7 +299,7 @@ final class Attr extends NodeOperatorVisitor {
flags |= IS_SCOPE;
}
- final FunctionNode function = lexicalContext.getFunction(block);
+ final FunctionNode function = getLexicalContext().getFunction(block);
if (symbol != null) {
// Symbol was already defined. Check if it needs to be redefined.
if ((flags & KINDMASK) == IS_PARAM) {
@@ -254,7 +313,6 @@ final class Attr extends NodeOperatorVisitor {
}
} else if ((flags & KINDMASK) == IS_VAR) {
if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
- assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block";
// Always create a new definition.
symbol = null;
} else {
@@ -272,17 +330,16 @@ final class Attr extends NodeOperatorVisitor {
// Determine where to create it.
if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
- symbolBlock = block;
+ symbolBlock = block; //internal vars are always defined in the block closest to them
} else {
- symbolBlock = function;
+ symbolBlock = getLexicalContext().getFunctionBody(function);
}
// Create and add to appropriate block.
- symbol = new Symbol(name, flags, node, symbolBlock);
+ symbol = new Symbol(name, flags);
symbolBlock.putSymbol(name, symbol);
if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
- symbolBlock.getFrame().addSymbol(symbol);
symbol.setNeedsSlot(true);
}
} else if (symbol.less(flags)) {
@@ -297,149 +354,100 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
start(functionNode, false);
- if (functionNode.isLazy()) {
- LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT");
- newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode);
- functionNode.setReturnType(Type.OBJECT);
- end(functionNode);
- return null;
- }
-
- lexicalContext.push(functionNode);
-
- clearLocalDefs();
- clearLocalUses();
-
- functionNode.setFrame(functionNode.pushFrame());
-
- initCallee(functionNode);
- initThis(functionNode);
- if (functionNode.isVarArg()) {
- initVarArg(functionNode);
- }
- initParameters(functionNode);
- initScope(functionNode);
- initReturn(functionNode);
-
- // Add all nested declared functions as symbols in this function
- for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) {
- final IdentNode ident = nestedFunction.getIdent();
- if (ident != null) {
- assert nestedFunction.isDeclared();
- final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction);
- newType(functionSymbol, Type.typeFor(ScriptFunction.class));
+ if (functionNode.isDeclared()) {
+ final Iterator<Block> blocks = getLexicalContext().getBlocks();
+ if (blocks.hasNext()) {
+ defineSymbol(
+ blocks.next(),
+ functionNode.getIdent().getName(),
+ IS_VAR,
+ functionNode);
+ } else {
+ // Q: What's an outermost function in a lexical context that is not a program?
+ // A: It's a function being compiled lazily!
+ assert getLexicalContext().getOutermostFunction() == functionNode && !functionNode.isProgram();
}
}
- if (functionNode.isProgram()) {
- initFromPropertyMap(functionNode);
- }
-
- // Add function name as local symbol
- if (!functionNode.isDeclared() && !functionNode.isProgram()) {
- if(functionNode.getSymbol() != null) {
- // a temporary left over from an earlier pass when the function was lazy
- assert functionNode.getSymbol().isTemp();
- // remove it
- functionNode.setSymbol(null);
- }
- final Symbol selfSymbol;
- if(functionNode.isAnonymous()) {
- selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode);
- } else {
- selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode);
- newType(selfSymbol, Type.OBJECT);
- selfSymbol.setNode(functionNode);
- }
+ if (functionNode.isLazy()) {
+ LOG.info("LAZY: ", functionNode.getName(), " => Promoting to OBJECT");
+ ensureSymbol(getLexicalContext().getCurrentFunction(), Type.OBJECT, functionNode);
+ end(functionNode);
+ return false;
}
- /*
- * This pushes all declarations (except for non-statements, i.e. for
- * node temporaries) to the top of the function scope. This way we can
- * get around problems like
- *
- * while (true) {
- * break;
- * if (true) {
- * var s;
- * }
- * }
- *
- * to an arbitrary nesting depth.
- *
- * @see NASHORN-73
- */
+ returnTypes.push(functionNode.getReturnType());
+ pushLocalsFunction();
+ return true;
+ }
- final List<Symbol> declaredSymbols = new ArrayList<>();
- // This visitor will assign symbol to all declared variables, except function declarations (which are taken care
- // in a separate step above) and "var" declarations in for loop initializers.
- functionNode.accept(new NodeOperatorVisitor() {
- @Override
- public Node enterFunctionNode(FunctionNode nestedFn) {
- // Don't descend into nested functions
- return nestedFn == functionNode ? nestedFn : null;
- }
- @Override
- public Node enterVarNode(VarNode varNode) {
- if(varNode.isStatement() && !varNode.isFunctionDeclaration()) {
- final IdentNode ident = varNode.getName();
- // any declared symbols that aren't visited need to be typed as well, hence the list
- declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident)));
- }
- return null;
- }
- });
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ FunctionNode newFunctionNode = functionNode;
- visitFunctionStatements(functionNode);
+ final LexicalContext lc = getLexicalContext();
//unknown parameters are promoted to object type.
- finalizeParameters(functionNode);
- finalizeTypes(functionNode);
- for (final Symbol symbol : declaredSymbols) {
+ finalizeParameters(newFunctionNode);
+ finalizeTypes(newFunctionNode);
+ for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
if (symbol.getSymbolType().isUnknown()) {
symbol.setType(Type.OBJECT);
symbol.setCanBeUndefined();
}
}
- if (functionNode.getReturnType().isUnknown()) {
- LOG.info("Unknown return type promoted to object");
- functionNode.setReturnType(Type.OBJECT);
- }
+ final Block body = newFunctionNode.getBody();
- if (functionNode.getSelfSymbolInit() != null) {
- LOG.info("Accepting self symbol init " + functionNode.getSelfSymbolInit() + " for " + functionNode.getName());
- final Node init = functionNode.getSelfSymbolInit();
- final List<Node> newStatements = new ArrayList<>();
- newStatements.add(init);
- newStatements.addAll(functionNode.getStatements());
- functionNode.setStatements(newStatements);
- functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this));
+ if (newFunctionNode.hasLazyChildren()) {
+ //the final body has already been assigned as we have left the function node block body by now
+ objectifySymbols(body);
}
- if (functionNode.hasLazyChildren()) {
- objectifySymbols(functionNode);
- }
+ if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
+ final IdentNode callee = compilerConstant(CALLEE);
+ final VarNode selfInit =
+ new VarNode(
+ newFunctionNode.getSource(),
+ newFunctionNode.getToken(),
+ newFunctionNode.getFinish(),
+ newFunctionNode.getIdent(),
+ callee);
- functionNode.popFrame();
+ LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName());
- functionNode.setState(CompilationState.ATTR);
+ final List<Node> newStatements = new ArrayList<>();
+ newStatements.add(selfInit);
+ assert callee.getSymbol() != null && callee.getSymbol().hasSlot();
- end(functionNode, false);
- lexicalContext.pop(functionNode);
+ final IdentNode name = selfInit.getName();
+ final Symbol nameSymbol = body.getExistingSymbol(name.getName());
- return null;
- }
+ assert nameSymbol != null;
+
+ name.setSymbol(nameSymbol);
+ selfInit.setSymbol(nameSymbol);
- private void visitFunctionStatements(final FunctionNode functionNode) {
- final List<Node> newStatements = new ArrayList<>(functionNode.getStatements());
- for(ListIterator<Node> stmts = newStatements.listIterator(); stmts.hasNext();) {
- stmts.set(stmts.next().accept(this));
+ newStatements.addAll(body.getStatements());
+ newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements));
+ }
+
+ if (returnTypes.peek().isUnknown()) {
+ LOG.info("Unknown return type promoted to object");
+ newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT);
}
- functionNode.setStatements(newStatements);
+ final Type returnType = returnTypes.pop();
+ newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType);
+ newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR);
+
+ popLocals();
+
+ end(newFunctionNode, false);
+
+ return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode));
}
@Override
@@ -450,7 +458,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
final String name = identNode.getName();
start(identNode);
@@ -458,31 +466,28 @@ final class Attr extends NodeOperatorVisitor {
if (identNode.isPropertyName()) {
// assign a pseudo symbol to property name
final Symbol pseudoSymbol = pseudoSymbol(name);
- LOG.info("IdentNode is property name -> assigning pseudo symbol " + pseudoSymbol);
+ LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol);
LOG.unindent();
identNode.setSymbol(pseudoSymbol);
- return null;
+ return false;
}
- final Block block = getCurrentBlock();
- final Symbol oldSymbol = identNode.getSymbol();
+ final LexicalContext lc = getLexicalContext();
+ final Block block = lc.getCurrentBlock();
+ final Symbol oldSymbol = identNode.getSymbol();
Symbol symbol = findSymbol(block, name);
//If an existing symbol with the name is found, use that otherwise, declare a new one
if (symbol != null) {
- LOG.info("Existing symbol = " + symbol);
- if (isFunctionExpressionSelfReference(symbol)) {
- final FunctionNode functionNode = (FunctionNode)symbol.getNode();
- assert functionNode.getCalleeNode() != null;
-
- final VarNode var = new VarNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
- //newTemporary(Type.OBJECT, var); //ScriptFunction? TODO
-
- functionNode.setNeedsSelfSymbol(var);
- }
-
- if (!identNode.isInitializedHere()) { // NASHORN-448
+ LOG.info("Existing symbol = ", symbol);
+ if (symbol.isFunctionSelf()) {
+ final FunctionNode functionNode = lc.getDefiningFunction(symbol);
+ assert functionNode != null;
+ assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
+ lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL);
+ newType(symbol, FunctionNode.FUNCTION_TYPE);
+ } else if (!identNode.isInitializedHere()) { // NASHORN-448
// here is a use outside the local def scope
if (!isLocalDef(name)) {
newType(symbol, Type.OBJECT);
@@ -491,26 +496,18 @@ final class Attr extends NodeOperatorVisitor {
}
identNode.setSymbol(symbol);
- // non-local: we need to put symbol in scope (if it isn't already)
- if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) {
- symbol.setIsScope();
- }
+ // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
+ maybeForceScope(symbol);
} else {
- LOG.info("No symbol exists. Declare undefined: " + symbol);
- symbol = useSymbol(block, name, identNode);
+ LOG.info("No symbol exists. Declare undefined: ", symbol);
+ symbol = defineSymbol(block, name, IS_GLOBAL, identNode);
// we have never seen this before, it can be undefined
newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
symbol.setCanBeUndefined();
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
}
- assert symbol != null;
- if(symbol.isGlobal()) {
- setUsesGlobalSymbol();
- } else if(symbol.isScope()) {
- final Iterator<Block> blocks = lexicalContext.getBlocks();
- blocks.next().setUsesScopeSymbol(symbol, blocks);
- }
+ setBlockScope(name, symbol);
if (symbol != oldSymbol && !identNode.isInitializedHere()) {
symbol.increaseUseCount();
@@ -519,42 +516,93 @@ final class Attr extends NodeOperatorVisitor {
end(identNode);
- return null;
+ return false;
}
/**
- * Marks the current function as one using any global symbol. The function and all its parent functions will all be
- * marked as needing parent scope.
- * @see #needsParentScope()
+ * If the symbol isn't already a scope symbol, and it is either not local to the current function, or it is being
+ * referenced from within a with block, we force it to be a scope symbol.
+ * @param symbol the symbol that might be scoped
*/
- private void setUsesGlobalSymbol() {
- for(final Iterator<FunctionNode> fns = lexicalContext.getFunctions(); fns.hasNext();) {
- fns.next().setUsesAncestorScope();
+ private void maybeForceScope(final Symbol symbol) {
+ if(!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
+ Symbol.setSymbolIsScope(getLexicalContext(), symbol);
}
}
- /**
- * Declare the use of a symbol in a block.
- *
- * @param block block in which the symbol is used
- * @param name Name of symbol.
- * @param node Using node
- *
- * @return Symbol for given name.
- */
- private Symbol useSymbol(final Block block, final String name, final Node node) {
- Symbol symbol = findSymbol(block, name);
+ private boolean symbolNeedsToBeScope(Symbol symbol) {
+ if(symbol.isThis() || symbol.isInternal()) {
+ return false;
+ }
+ boolean previousWasBlock = false;
+ for(final Iterator<LexicalContextNode> it = getLexicalContext().getAllNodes(); it.hasNext();) {
+ final LexicalContextNode node = it.next();
+ if(node instanceof FunctionNode) {
+ // We reached the function boundary without seeing a definition for the symbol - it needs to be in
+ // scope.
+ return true;
+ } else if(node instanceof WithNode) {
+ if(previousWasBlock) {
+ // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
+ // preceded by a block, this means we're currently processing its expression, not its body,
+ // therefore it doesn't count.
+ return true;
+ }
+ previousWasBlock = false;
+ } else if(node instanceof Block) {
+ if(((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
+ // We reached the block that defines the symbol without reaching either the function boundary, or a
+ // WithNode. The symbol need not be scoped.
+ return false;
+ }
+ previousWasBlock = true;
+ } else {
+ previousWasBlock = false;
+ }
+ }
+ throw new AssertionError();
+ }
- if (symbol == null) {
- // If not found, declare as a free var.
- symbol = defineSymbol(block, name, IS_GLOBAL, node);
- } else {
- node.setSymbol(symbol);
+ private void setBlockScope(final String name, final Symbol symbol) {
+ assert symbol != null;
+ if (symbol.isGlobal()) {
+ setUsesGlobalSymbol();
+ return;
}
- return symbol;
+ if (symbol.isScope()) {
+ final LexicalContext lc = getLexicalContext();
+
+ Block scopeBlock = null;
+ for (final Iterator<LexicalContextNode> contextNodeIter = getLexicalContext().getAllNodes(); contextNodeIter.hasNext(); ) {
+ final LexicalContextNode node = contextNodeIter.next();
+ if (node instanceof Block) {
+ if (((Block)node).getExistingSymbol(name) != null) {
+ scopeBlock = (Block)node;
+ break;
+ }
+ } else if (node instanceof FunctionNode) {
+ lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
+ }
+ }
+
+ if (scopeBlock != null) {
+ assert getLexicalContext().contains(scopeBlock);
+ lc.setFlag(scopeBlock, Block.NEEDS_SCOPE);
+ }
+ }
}
+ /**
+ * Marks the current function as one using any global symbol. The function and all its parent functions will all be
+ * marked as needing parent scope.
+ * @see #needsParentScope()
+ */
+ private void setUsesGlobalSymbol() {
+ for (final Iterator<FunctionNode> fns = getLexicalContext().getFunctions(); fns.hasNext();) {
+ getLexicalContext().setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
+ }
+ }
/**
* Search for symbol in the lexical context starting from the given block.
@@ -564,7 +612,7 @@ final class Attr extends NodeOperatorVisitor {
private Symbol findSymbol(final Block block, final String name) {
// Search up block chain to locate symbol.
- for(final Iterator<Block> blocks = lexicalContext.getBlocks(block); blocks.hasNext();) {
+ for(final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
// Find name.
final Symbol symbol = blocks.next().getExistingSymbol(name);
// If found then we are good.
@@ -577,13 +625,13 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveIndexNode(final IndexNode indexNode) {
- newTemporary(Type.OBJECT, indexNode); //TODO
+ ensureSymbol(Type.OBJECT, indexNode); //TODO
return indexNode;
}
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
try {
start(literalNode);
assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
@@ -604,26 +652,33 @@ final class Attr extends NodeOperatorVisitor {
assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
}
- getCurrentFunctionNode().newLiteral(literalNode);
+ getLexicalContext().getCurrentFunction().newLiteral(literalNode);
} finally {
end(literalNode);
}
- return null;
+
+ return false;
+ }
+
+ @Override
+ public boolean enterObjectNode(final ObjectNode objectNode) {
+ return start(objectNode);
}
@Override
public Node leaveObjectNode(final ObjectNode objectNode) {
- newTemporary(Type.OBJECT, objectNode);
- end(objectNode);
- return objectNode;
+ ensureSymbol(Type.OBJECT, objectNode);
+ return end(objectNode);
}
+ //TODO is this correct why not leave?
@Override
- public Node enterPropertyNode(final PropertyNode propertyNode) {
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
// assign a pseudo symbol to property name, see NASHORN-710
+ start(propertyNode);
propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
end(propertyNode);
- return propertyNode;
+ return true;
}
@Override
@@ -636,8 +691,10 @@ final class Attr extends NodeOperatorVisitor {
if (expr.getType().isUnknown() && symbol.isParam()) {
symbol.setType(Type.OBJECT);
}
- getCurrentFunctionNode().setReturnType(Type.widest(getCurrentFunctionNode().getReturnType(), symbol.getSymbolType()));
- LOG.info("Returntype is now " + getCurrentFunctionNode().getReturnType());
+
+ final Type returnType = Type.widest(returnTypes.pop(), symbol.getSymbolType());
+ returnTypes.push(returnType);
+ LOG.info("Returntype is now ", returnType);
}
end(returnNode);
@@ -649,25 +706,29 @@ final class Attr extends NodeOperatorVisitor {
public Node leaveSwitchNode(final SwitchNode switchNode) {
Type type = Type.UNKNOWN;
+ final List<CaseNode> newCases = new ArrayList<>();
for (final CaseNode caseNode : switchNode.getCases()) {
final Node test = caseNode.getTest();
+
+ CaseNode newCaseNode = caseNode;
if (test != null) {
if (test instanceof LiteralNode) {
//go down to integers if we can
final LiteralNode<?> lit = (LiteralNode<?>)test;
if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) {
if (JSType.isRepresentableAsInt(lit.getNumber())) {
- caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
+ newCaseNode = caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
}
}
} else {
// the "all integer" case that CodeGenerator optimizes for currently assumes literals only
type = Type.OBJECT;
- break;
}
- type = Type.widest(type, caseNode.getTest().getType());
+ type = Type.widest(type, newCaseNode.getTest().getType());
}
+
+ newCases.add(newCaseNode);
}
//only optimize for all integers
@@ -675,11 +736,11 @@ final class Attr extends NodeOperatorVisitor {
type = Type.OBJECT;
}
- switchNode.setTag(newInternal(getCurrentFunctionNode().uniqueName(SWITCH_TAG_PREFIX.tag()), type));
+ switchNode.setTag(newInternal(getLexicalContext().getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
end(switchNode);
- return switchNode;
+ return switchNode.setCases(getLexicalContext(), newCases);
}
@Override
@@ -696,25 +757,25 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
start(varNode);
final IdentNode ident = varNode.getName();
final String name = ident.getName();
- final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident);
+ final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR, ident);
assert symbol != null;
- LOG.info("VarNode " + varNode + " set symbol " + symbol);
+ LOG.info("VarNode ", varNode, " set symbol ", symbol);
varNode.setSymbol(symbol);
// NASHORN-467 - use before definition of vars - conservative
- if (localUses.contains(ident.getName())) {
+ if (isLocalUse(ident.getName())) {
newType(symbol, Type.OBJECT);
symbol.setCanBeUndefined();
}
- return varNode;
+ return true;
}
@Override
@@ -734,7 +795,7 @@ final class Attr extends NodeOperatorVisitor {
addLocalDef(name);
final Symbol symbol = varNode.getSymbol();
- final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).isProgram(); //see NASHORN-56
+ final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56
if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
// Forbid integers as local vars for now as we have no way to treat them as undefined
newType(symbol, init.getType());
@@ -751,14 +812,14 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveADD(final UnaryNode unaryNode) {
- newTemporary(arithType(), unaryNode);
+ ensureSymbol(arithType(), unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveBIT_NOT(final UnaryNode unaryNode) {
- newTemporary(Type.INT, unaryNode);
+ ensureSymbol(Type.INT, unaryNode);
end(unaryNode);
return unaryNode;
}
@@ -766,30 +827,29 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
// @see assignOffset
- ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs());
+ ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
final Type type = arithType();
newType(unaryNode.rhs().getSymbol(), type);
- newTemporary(type, unaryNode);
+ ensureSymbol(type, unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveDELETE(final UnaryNode unaryNode) {
- final FunctionNode currentFunctionNode = getCurrentFunctionNode();
- final boolean strictMode = currentFunctionNode.isStrictMode();
+ final FunctionNode currentFunctionNode = getLexicalContext().getCurrentFunction();
+ final boolean strictMode = currentFunctionNode.isStrict();
final Node rhs = unaryNode.rhs();
final Node strictFlagNode = LiteralNode.newInstance(unaryNode, strictMode).accept(this);
Request request = Request.DELETE;
- final RuntimeNode runtimeNode;
final List<Node> args = new ArrayList<>();
if (rhs instanceof IdentNode) {
// If this is a declared variable or a function parameter, delete always fails (except for globals).
final String name = ((IdentNode)rhs).getName();
- final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !rhs.getSymbol().isTopLevel());
+ final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name));
if (failDelete && rhs.getSymbol().isThis()) {
return LiteralNode.newInstance(unaryNode, true).accept(this);
@@ -797,7 +857,7 @@ final class Attr extends NodeOperatorVisitor {
final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this);
if (!failDelete) {
- args.add(currentFunctionNode.getScopeNode());
+ args.add(compilerConstant(SCOPE));
}
args.add(literalNode);
args.add(strictFlagNode);
@@ -825,42 +885,62 @@ final class Attr extends NodeOperatorVisitor {
return LiteralNode.newInstance(unaryNode, true).accept(this);
}
- runtimeNode = new RuntimeNode(unaryNode, request, args);
- assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this
+ final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, request, args);
+ assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //unary parent constructor should do this
return leaveRuntimeNode(runtimeNode);
}
+ /**
+ * Is the symbol denoted by the specified name in the current lexical context defined in the program level
+ * @param name the name of the symbol
+ * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
+ */
+ private boolean isProgramLevelSymbol(final String name) {
+ for(final Iterator<Block> it = getLexicalContext().getBlocks(); it.hasNext();) {
+ final Block next = it.next();
+ if(next.getExistingSymbol(name) != null) {
+ return next == getLexicalContext().getFunctionBody(getLexicalContext().getOutermostFunction());
+ }
+ }
+ throw new AssertionError("Couldn't find symbol " + name + " in the context");
+ }
+
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
- newTemporary(Type.OBJECT, unaryNode);
+ ensureSymbol(Type.OBJECT, unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveNOT(final UnaryNode unaryNode) {
- newTemporary(Type.BOOLEAN, unaryNode);
+ ensureSymbol(Type.BOOLEAN, unaryNode);
end(unaryNode);
return unaryNode;
}
+ private IdentNode compilerConstant(CompilerConstants cc) {
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ final IdentNode node = new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
+ node.setSymbol(functionNode.compilerConstant(cc));
+ return node;
+ }
+
@Override
public Node leaveTYPEOF(final UnaryNode unaryNode) {
- final Node rhs = unaryNode.rhs();
-
- RuntimeNode runtimeNode;
+ final Node rhs = unaryNode.rhs();
List<Node> args = new ArrayList<>();
if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
- args.add(getCurrentFunctionNode().getScopeNode());
+ args.add(compilerConstant(SCOPE));
args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
} else {
args.add(rhs);
args.add(LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
}
- runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
+ RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
assert runtimeNode.getSymbol() == unaryNode.getSymbol();
runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode);
@@ -872,21 +952,20 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
- newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
+ ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode);
return runtimeNode;
}
@Override
public Node leaveSUB(final UnaryNode unaryNode) {
- newTemporary(arithType(), unaryNode);
+ ensureSymbol(arithType(), unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveVOID(final UnaryNode unaryNode) {
- final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.VOID);
- runtimeNode.accept(this);
+ final RuntimeNode runtimeNode = (RuntimeNode)new RuntimeNode(unaryNode, Request.VOID).accept(this);
assert runtimeNode.getSymbol().getSymbolType().isObject();
end(unaryNode);
return runtimeNode;
@@ -903,7 +982,7 @@ final class Attr extends NodeOperatorVisitor {
ensureTypeNotUnknown(lhs);
ensureTypeNotUnknown(rhs);
- newTemporary(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
+ ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
end(binaryNode);
@@ -912,7 +991,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveAND(final BinaryNode binaryNode) {
- newTemporary(Type.OBJECT, binaryNode);
+ ensureSymbol(Type.OBJECT, binaryNode);
end(binaryNode);
return binaryNode;
}
@@ -921,39 +1000,39 @@ final class Attr extends NodeOperatorVisitor {
* This is a helper called before an assignment.
* @param binaryNode assignment node
*/
- private Node enterAssignmentNode(final BinaryNode binaryNode) {
+ private boolean enterAssignmentNode(final BinaryNode binaryNode) {
start(binaryNode);
final Node lhs = binaryNode.lhs();
if (lhs instanceof IdentNode) {
- final Block block = getCurrentBlock();
+ final Block block = getLexicalContext().getCurrentBlock();
final IdentNode ident = (IdentNode)lhs;
final String name = ident.getName();
- Symbol symbol = findSymbol(getCurrentBlock(), name);
+ Symbol symbol = findSymbol(block, name);
if (symbol == null) {
symbol = defineSymbol(block, name, IS_GLOBAL, ident);
binaryNode.setSymbol(symbol);
- } else if (!isLocal(getCurrentFunctionNode(), symbol)) {
- symbol.setIsScope();
+ } else {
+ maybeForceScope(symbol);
}
addLocalDef(name);
}
- return binaryNode;
+ return true;
}
private boolean isLocal(FunctionNode function, Symbol symbol) {
- final Block block = symbol.getBlock();
- // some temp symbols have no block, so can be assumed local
- return block == null || lexicalContext.getFunction(block) == function;
+ final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol);
+ // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
+ return definingFn == null || definingFn == function;
}
@Override
- public Node enterASSIGN(final BinaryNode binaryNode) {
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -963,7 +1042,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -978,7 +1057,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -988,7 +1067,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -998,7 +1077,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1008,7 +1087,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1018,7 +1097,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1028,7 +1107,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1038,7 +1117,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1048,7 +1127,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1058,7 +1137,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1068,7 +1147,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1094,13 +1173,13 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
- newTemporary(binaryNode.rhs().getType(), binaryNode);
+ ensureSymbol(binaryNode.rhs().getType(), binaryNode);
return binaryNode;
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
- newTemporary(binaryNode.lhs().getType(), binaryNode);
+ ensureSymbol(binaryNode.lhs().getType(), binaryNode);
return binaryNode;
}
@@ -1113,7 +1192,7 @@ final class Attr extends NodeOperatorVisitor {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
- newTemporary(Type.BOOLEAN, binaryNode);
+ ensureSymbol(Type.BOOLEAN, binaryNode);
ensureTypeNotUnknown(lhs);
ensureTypeNotUnknown(rhs);
@@ -1131,7 +1210,7 @@ final class Attr extends NodeOperatorVisitor {
//newType(binaryNode.lhs().getSymbol(), operandType);
//newType(binaryNode.rhs().getSymbol(), operandType);
- newTemporary(destType, binaryNode);
+ ensureSymbol(destType, binaryNode);
return binaryNode;
}
@@ -1216,7 +1295,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveOR(final BinaryNode binaryNode) {
- newTemporary(Type.OBJECT, binaryNode);
+ ensureSymbol(Type.OBJECT, binaryNode);
end(binaryNode);
return binaryNode;
}
@@ -1244,7 +1323,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveForNode(final ForNode forNode) {
if (forNode.isForIn()) {
- forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
+ forNode.setIterator(newInternal(getLexicalContext().getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73
/*
* Iterators return objects, so we need to widen the scope of the
* init variable if it, for example, has been assigned double type
@@ -1267,65 +1346,50 @@ final class Attr extends NodeOperatorVisitor {
ensureTypeNotUnknown(rhs);
final Type type = Type.widest(lhs.getType(), rhs.getType());
- newTemporary(type, ternaryNode);
+ ensureSymbol(type, ternaryNode);
end(ternaryNode);
+ assert ternaryNode.getSymbol() != null;
return ternaryNode;
}
- private void initThis(final FunctionNode functionNode) {
- final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null);
+ private void initThis(final Block block) {
+ final Symbol thisSymbol = defineSymbol(block, THIS.symbolName(), IS_PARAM | IS_THIS, null);
newType(thisSymbol, Type.OBJECT);
thisSymbol.setNeedsSlot(true);
- functionNode.getThisNode().setSymbol(thisSymbol);
- LOG.info("Initialized scope symbol: " + thisSymbol);
}
- private void initScope(final FunctionNode functionNode) {
- final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
+ private void initScope(final Block block) {
+ final Symbol scopeSymbol = defineSymbol(block, SCOPE.symbolName(), IS_VAR | IS_INTERNAL, null);
newType(scopeSymbol, Type.typeFor(ScriptObject.class));
scopeSymbol.setNeedsSlot(true);
- functionNode.getScopeNode().setSymbol(scopeSymbol);
- LOG.info("Initialized scope symbol: " + scopeSymbol);
}
- private void initReturn(final FunctionNode functionNode) {
- final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
+ private void initReturn(final Block block) {
+ final Symbol returnSymbol = defineSymbol(block, RETURN.symbolName(), IS_VAR | IS_INTERNAL, null);
newType(returnSymbol, Type.OBJECT);
returnSymbol.setNeedsSlot(true);
- functionNode.getResultNode().setSymbol(returnSymbol);
- LOG.info("Initialized return symbol: " + returnSymbol);
//return symbol is always object as it's the __return__ thing. What returnType is is another matter though
}
- private void initVarArg(final FunctionNode functionNode) {
- if (functionNode.isVarArg()) {
- final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
- varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
- varArgsSymbol.setNeedsSlot(true);
- functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
- LOG.info("Initialized varargs symbol: " + varArgsSymbol);
-
- if (functionNode.needsArguments()) {
- final String argumentsName = functionNode.getArgumentsNode().getName();
- final Symbol argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null);
- newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
- argumentsSymbol.setNeedsSlot(true);
- functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
- addLocalDef(argumentsName);
- LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol);
- }
+ private void initVarArg(final Block block, final boolean needsArguments) {
+ final Symbol varArgsSymbol = defineSymbol(block, VARARGS.symbolName(), IS_PARAM | IS_INTERNAL, null);
+ varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
+ varArgsSymbol.setNeedsSlot(true);
+
+ if (needsArguments) {
+ final Symbol argumentsSymbol = defineSymbol(block, ARGUMENTS.symbolName(), IS_VAR | IS_INTERNAL, null);
+ newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
+ argumentsSymbol.setNeedsSlot(true);
+ addLocalDef(ARGUMENTS.symbolName());
}
}
- private void initCallee(final FunctionNode functionNode) {
- assert functionNode.getCalleeNode() != null : functionNode + " has no callee";
- final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
- newType(calleeSymbol, Type.typeFor(ScriptFunction.class));
+ private void initCallee(final Block block) {
+ final Symbol calleeSymbol = defineSymbol(block, CALLEE.symbolName(), IS_PARAM | IS_INTERNAL, null);
+ newType(calleeSymbol, FunctionNode.FUNCTION_TYPE);
calleeSymbol.setNeedsSlot(true);
- functionNode.getCalleeNode().setSymbol(calleeSymbol);
- LOG.info("Initialized callee symbol " + calleeSymbol);
}
/**
@@ -1334,25 +1398,19 @@ final class Attr extends NodeOperatorVisitor {
*
* @param functionNode the function node
*/
- private void initParameters(final FunctionNode functionNode) {
- //If a function is specialized, we don't need to tag either it return
- // type or its parameters with the widest (OBJECT) type for safety.
- functionNode.setReturnType(Type.UNKNOWN);
-
+ private void initParameters(final FunctionNode functionNode, final Block body) {
for (final IdentNode param : functionNode.getParameters()) {
addLocalDef(param.getName());
- final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param);
+ final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM, param);
if (paramSymbol != null) {
final Type callSiteParamType = functionNode.getSpecializedType(param);
if (callSiteParamType != null) {
- LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that.");
-
- System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that.");
+ LOG.info("Param ", paramSymbol, " has a callsite type ", callSiteParamType, ". Using that.");
}
newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
}
- LOG.info("Initialized param " + paramSymbol);
+ LOG.info("Initialized param ", paramSymbol);
}
}
@@ -1378,7 +1436,7 @@ final class Attr extends NodeOperatorVisitor {
// this function, we can tell the runtime system that no matter what the
// call site is, use this information. TODO
if (!paramSymbol.getSymbolType().isObject()) {
- LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType());
+ LOG.finest("Parameter ", ident, " could profit from specialization to ", paramSymbol.getSymbolType());
}
newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
@@ -1392,19 +1450,18 @@ final class Attr extends NodeOperatorVisitor {
/**
* Move any properties from a global map into the scope of this method
- * @param functionNode the function node for which to init scope vars
+ * @param block the function node body for which to init scope vars
*/
- private void initFromPropertyMap(final FunctionNode functionNode) {
+ private void initFromPropertyMap(final Block block) {
// For a script, add scope symbols as defined in the property map
- assert functionNode.isProgram();
final PropertyMap map = Context.getGlobalMap();
for (final Property property : map.getProperties()) {
final String key = property.getKey();
- final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null);
+ final Symbol symbol = defineSymbol(block, key, IS_GLOBAL, null);
newType(symbol, Type.OBJECT);
- LOG.info("Added global symbol from property map " + symbol);
+ LOG.info("Added global symbol from property map ", symbol);
}
}
@@ -1412,7 +1469,7 @@ final class Attr extends NodeOperatorVisitor {
final Symbol symbol = node.getSymbol();
- LOG.info("Ensure type not unknown for: " + symbol);
+ LOG.info("Ensure type not unknown for: ", symbol);
/*
* Note that not just unknowns, but params need to be blown
@@ -1452,7 +1509,7 @@ final class Attr extends NodeOperatorVisitor {
}
private Symbol exceptionSymbol() {
- return newInternal(getCurrentFunctionNode().uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
+ return newInternal(getLexicalContext().getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class));
}
/**
@@ -1512,15 +1569,15 @@ final class Attr extends NodeOperatorVisitor {
}
Type from = node.getType();
if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
- LOG.fine("Had to post pass widen '" + node + "' " + Debug.id(node) + " from " + node.getType() + " to " + to);
+ LOG.fine("Had to post pass widen '", node, "' " + Debug.id(node), " from ", node.getType(), " to ", to);
newType(node.getSymbol(), to);
changed.add(node);
}
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- return node.isLazy() ? null : node;
+ public boolean enterFunctionNode(final FunctionNode node) {
+ return !node.isLazy();
}
/**
@@ -1574,7 +1631,7 @@ final class Attr extends NodeOperatorVisitor {
} else {
type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
}
- newTemporary(type, binaryNode);
+ ensureSymbol(type, binaryNode);
newType(lhs.getSymbol(), type);
end(binaryNode);
return binaryNode;
@@ -1589,32 +1646,25 @@ final class Attr extends NodeOperatorVisitor {
final Node lhs = binaryNode.lhs();
newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
- newTemporary(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
+ ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
- ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode);
+ ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode);
end(binaryNode);
return binaryNode;
}
- private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
- if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
- return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName());
- }
- return false;
- }
-
- private static Symbol newTemporary(final FunctionNode functionNode, final Type type, final Node node) {
- LOG.info("New TEMPORARY added to " + functionNode.getName() + " type=" + type);
- return functionNode.newTemporary(type, node);
+ private Symbol ensureSymbol(final FunctionNode functionNode, final Type type, final Node node) {
+ LOG.info("New TEMPORARY added to ", functionNode.getName(), " type=", type);
+ return functionNode.ensureSymbol(getLexicalContext().getCurrentBlock(), type, node);
}
- private Symbol newTemporary(final Type type, final Node node) {
- return newTemporary(getCurrentFunctionNode(), type, node);
+ private Symbol ensureSymbol(final Type type, final Node node) {
+ return ensureSymbol(getLexicalContext().getCurrentFunction(), type, node);
}
private Symbol newInternal(final String name, final Type type) {
- final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null);
+ final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL, null);
iter.setType(type); // NASHORN-73
return iter;
}
@@ -1624,40 +1674,51 @@ final class Attr extends NodeOperatorVisitor {
symbol.setType(type);
if (symbol.getSymbolType() != oldType) {
- LOG.info("New TYPE " + type + " for " + symbol + " (was " + oldType + ")");
+ LOG.info("New TYPE ", type, " for ", symbol," (was ", oldType, ")");
}
if (symbol.isParam()) {
symbol.setType(type);
- LOG.info("Param type change " + symbol);
+ LOG.info("Param type change ", symbol);
}
}
- private void clearLocalDefs() {
- localDefs = new HashSet<>();
+ private void pushLocalsFunction() {
+ localDefs.push(new HashSet<String>());
+ localUses.push(new HashSet<String>());
+ }
+
+ private void pushLocalsBlock() {
+ localDefs.push(localDefs.isEmpty() ? new HashSet<String>() : new HashSet<>(localDefs.peek()));
+ localUses.push(localUses.isEmpty() ? new HashSet<String>() : new HashSet<>(localUses.peek()));
+ }
+
+ private void popLocals() {
+ localDefs.pop();
+ localUses.pop();
}
private boolean isLocalDef(final String name) {
- return localDefs.contains(name);
+ return localDefs.peek().contains(name);
}
private void addLocalDef(final String name) {
- LOG.info("Adding local def of symbol: '" + name + "'");
- localDefs.add(name);
+ LOG.info("Adding local def of symbol: '", name, "'");
+ localDefs.peek().add(name);
}
private void removeLocalDef(final String name) {
- LOG.info("Removing local def of symbol: '" + name + "'");
- localDefs.remove(name);
+ LOG.info("Removing local def of symbol: '", name, "'");
+ localDefs.peek().remove(name);
}
- private void clearLocalUses() {
- localUses = new HashSet<>();
+ private boolean isLocalUse(final String name) {
+ return localUses.peek().contains(name);
}
private void addLocalUse(final String name) {
- LOG.info("Adding local use of symbol: '" + name + "'");
- localUses.add(name);
+ LOG.info("Adding local use of symbol: '", name, "'");
+ localUses.peek().add(name);
}
/**
@@ -1665,30 +1726,28 @@ final class Attr extends NodeOperatorVisitor {
* This is done when the function contains unevaluated black boxes such as
* lazy sub-function nodes that have not been compiled.
*
- * @param functionNode function node in whose scope symbols should conservatively be made objects
+ * @param body body for the function node we are leaving
*/
- private static void objectifySymbols(final FunctionNode functionNode) {
- functionNode.accept(new NodeVisitor() {
+ private static void objectifySymbols(final Block body) {
+ body.accept(new NodeVisitor() {
private void toObject(final Block block) {
for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) {
final Symbol symbol = iter.next();
- newType(symbol, Type.OBJECT);
+ if (!symbol.isTemp()) {
+ newType(symbol, Type.OBJECT);
+ }
}
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
toObject(block);
- return block;
+ return true;
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- toObject(node);
- if (node.isLazy()) {
- return null;
- }
- return node;
+ public boolean enterFunctionNode(final FunctionNode node) {
+ return false;
}
});
}
@@ -1702,11 +1761,11 @@ final class Attr extends NodeOperatorVisitor {
return cn.substring(lastDot + 1);
}
- private Node start(final Node node) {
+ private boolean start(final Node node) {
return start(node, true);
}
- private Node start(final Node node, final boolean printNode) {
+ private boolean start(final Node node, final boolean printNode) {
if (DEBUG) {
final StringBuilder sb = new StringBuilder();
@@ -1715,13 +1774,13 @@ final class Attr extends NodeOperatorVisitor {
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
- append(getCurrentFunctionNode().getName()).
+ append(getLexicalContext().getCurrentFunction().getName()).
append("'");
- LOG.info(sb.toString());
+ LOG.info(sb);
LOG.indent();
}
- return node;
+ return true;
}
private Node end(final Node node) {
@@ -1737,7 +1796,7 @@ final class Attr extends NodeOperatorVisitor {
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
- append(getCurrentFunctionNode().getName());
+ append(getLexicalContext().getCurrentFunction().getName());
if (node.getSymbol() == null) {
sb.append(" <NO SYMBOL>");
@@ -1746,7 +1805,7 @@ final class Attr extends NodeOperatorVisitor {
}
LOG.unindent();
- LOG.info(sb.toString());
+ LOG.info(sb);
}
return node;
diff --git a/src/jdk/nashorn/internal/codegen/ClassEmitter.java b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
index 7ca7f994..2d379183 100644
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
@@ -58,12 +58,14 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
+
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -219,14 +221,14 @@ public class ClassEmitter implements Emitter {
private void defineCommonStatics(final boolean strictMode) {
// source - used to store the source data (text) for this script. Shared across
// compile units. Set externally by the compiler.
- field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.tag(), Source.class);
+ field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
// constants - used to the constants array for this script. Shared across
// compile units. Set externally by the compiler.
- field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.tag(), Object[].class);
+ field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
// strictMode - was this script compiled in strict mode. Set externally by the compiler.
- field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.tag(), boolean.class, strictMode);
+ field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
}
/**
@@ -238,9 +240,9 @@ public class ClassEmitter implements Emitter {
if (constantMethodNeeded.contains(String.class)) {
// $getString - get the ith entry from the constants table and cast to String.
- final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.tag(), String.class, int.class);
+ final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
getStringMethod.begin();
- getStringMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
+ getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
.checkcast(String.class)
@@ -250,7 +252,7 @@ public class ClassEmitter implements Emitter {
if (constantMethodNeeded.contains(PropertyMap.class)) {
// $getMap - get the ith entry from the constants table and cast to PropertyMap.
- final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class);
+ final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
getMapMethod.begin();
getMapMethod.loadConstants()
.load(Type.INT, 0)
@@ -260,7 +262,7 @@ public class ClassEmitter implements Emitter {
getMapMethod.end();
// $setMap - overwrite an existing map.
- final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class);
+ final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
setMapMethod.begin();
setMapMethod.loadConstants()
.load(Type.INT, 0)
@@ -289,7 +291,7 @@ public class ClassEmitter implements Emitter {
final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
getArrayMethod.begin();
- getArrayMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
+ getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
.checkcast(cls)
@@ -307,7 +309,7 @@ public class ClassEmitter implements Emitter {
*/
static String getArrayMethodName(final Class<?> cls) {
assert cls.isArray();
- return GET_ARRAY_PREFIX.tag() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.tag();
+ return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
}
/**
@@ -409,6 +411,10 @@ public class ClassEmitter implements Emitter {
methodsStarted.remove(method);
}
+ SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
+ return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode);
+ }
+
/**
* Add a new method to the class - defaults to public method
*
@@ -433,7 +439,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving this method
*/
MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
- return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, methodDescriptor(rtype, ptypes), null, null));
+ return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
}
/**
@@ -484,7 +490,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <clinit>
*/
MethodEmitter clinit() {
- return method(EnumSet.of(Flag.STATIC), CLINIT.tag(), void.class);
+ return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
}
/**
@@ -493,7 +499,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>()V
*/
MethodEmitter init() {
- return method(INIT.tag(), void.class);
+ return method(INIT.symbolName(), void.class);
}
/**
@@ -503,7 +509,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>()V
*/
MethodEmitter init(final Class<?>... ptypes) {
- return method(INIT.tag(), void.class, ptypes);
+ return method(INIT.symbolName(), void.class, ptypes);
}
/**
@@ -515,7 +521,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>(...)V
*/
MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
- return method(flags, INIT.tag(), void.class, ptypes);
+ return method(flags, INIT.symbolName(), void.class, ptypes);
}
/**
@@ -628,4 +634,9 @@ public class ClassEmitter implements Emitter {
return v;
}
}
+
+ private MethodVisitor methodVisitor(EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
+ return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
+ }
+
}
diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index 1d09e9c9..5d4b8f98 100644
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -27,14 +27,18 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
-import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF;
import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
@@ -48,8 +52,10 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Deque;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
@@ -67,11 +73,11 @@ import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BreakNode;
+import jdk.nashorn.internal.ir.BreakableNode;
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.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -81,10 +87,12 @@ import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
+import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.PropertyNode;
@@ -107,8 +115,10 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Lexer.RegexToken;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ECMAException;
+import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
@@ -157,14 +167,29 @@ final class CodeGenerator extends NodeOperatorVisitor {
/** How many regexp fields have been emitted */
private int regexFieldCount;
- /** Used for temporary signaling between enterCallNode and enterFunctionNode to handle the special case of calling
- * a just-defined anonymous function expression. */
- private boolean functionNodeIsCallee;
-
/** Map of shared scope call sites */
private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
- private final LexicalContext lexicalContext = new LexicalContext();
+ /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
+ private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
+
+ /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
+ private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
+
+ /** The discard stack - whenever we enter a discard node we keep track of its return value status -
+ * i.e. should we keep it or throw it away */
+ private final Deque<Node> discard = new ArrayDeque<>();
+
+ // A stack tracking the next free local variable slot in the blocks. There's one entry for every block
+ // currently on the lexical context stack.
+ private int[] nextFreeSlots = new int[16];
+ private int nextFreeSlotsSize = 0;
+
+ /** Current method emitter */
+ private MethodEmitter method;
+
+ /** Current compile unit */
+ private CompileUnit unit;
/** When should we stop caching regexp expressions in fields to limit bytecode size? */
private static final int MAX_REGEX_FIELDS = 2 * 1024;
@@ -177,6 +202,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param compiler
*/
CodeGenerator(final Compiler compiler) {
+ super(new DynamicScopeTrackingLexicalContext());
this.compiler = compiler;
this.callSiteFlags = compiler.getEnv()._callsite_flags;
}
@@ -188,7 +214,37 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @return the correct flags for a call site in the current function
*/
int getCallSiteFlags() {
- return getCurrentFunctionNode().isStrictMode() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
+ return getLexicalContext().getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
+ }
+
+ private void pushMethodEmitter(final MethodEmitter newMethod) {
+ methodEmitters.push(newMethod);
+ this.method = newMethod;
+ }
+
+ private void popMethodEmitter(final MethodEmitter oldMethod) {
+ assert methodEmitters.peek() == oldMethod;
+ methodEmitters.pop();
+ if (!methodEmitters.isEmpty()) {
+ this.method = methodEmitters.peek();
+ } else {
+ this.method = null;
+ }
+ }
+
+ private void push(final CompileUnit newUnit) {
+ compileUnits.push(newUnit);
+ this.unit = newUnit;
+ }
+
+ private void pop(final CompileUnit oldUnit) {
+ assert compileUnits.peek() == oldUnit;
+ compileUnits.pop();
+ if (!compileUnits.isEmpty()) {
+ this.unit = compileUnits.peek();
+ } else {
+ this.unit = null;
+ }
}
/**
@@ -217,7 +273,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
final int flags = CALLSITE_SCOPE | getCallSiteFlags();
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
if (isFastScope(symbol)) {
// Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
@@ -231,27 +287,103 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
/**
+ * A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new
+ * variables introduced into them at run time - a with block or a function directly containing an eval call.
+ */
+ private static class DynamicScopeTrackingLexicalContext extends LexicalContext {
+ int dynamicScopeCount = 0;
+
+ @Override
+ public <T extends LexicalContextNode> T push(T node) {
+ if(isDynamicScopeBoundary(node)) {
+ ++dynamicScopeCount;
+ }
+ return super.push(node);
+ }
+
+ @Override
+ public <T extends LexicalContextNode> T pop(T node) {
+ final T popped = super.pop(node);
+ if(isDynamicScopeBoundary(popped)) {
+ --dynamicScopeCount;
+ }
+ return popped;
+ }
+
+ private boolean isDynamicScopeBoundary(LexicalContextNode node) {
+ if(node instanceof Block) {
+ // Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture
+ // processing of WithNode.expression too, but it should be unaffected.
+ return !isEmpty() && peek() instanceof WithNode;
+ } else if(node instanceof FunctionNode) {
+ // Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new
+ // variable into the function's scope), and it isn't strict (as evals in strict functions get an
+ // isolated scope).
+ return isFunctionDynamicScope((FunctionNode)node);
+ }
+ return false;
+ }
+ }
+
+ boolean inDynamicScope() {
+ return ((DynamicScopeTrackingLexicalContext)getLexicalContext()).dynamicScopeCount > 0;
+ }
+
+ static boolean isFunctionDynamicScope(FunctionNode fn) {
+ return fn.hasEval() && !fn.isStrict();
+ }
+
+ /**
* Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
*
* @param function function to check for fast scope
* @return true if fast scope
*/
private boolean isFastScope(final Symbol symbol) {
- if (!symbol.isScope() || !symbol.getBlock().needsScope()) {
+ if(!symbol.isScope()) {
return false;
}
- // Allow fast scope access if no function contains with or eval
- for(final Iterator<FunctionNode> it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) {
- final FunctionNode func = it.next();
- if (func.hasWith() || func.hasEval()) {
- return false;
+ final LexicalContext lc = getLexicalContext();
+ if(!inDynamicScope()) {
+ // If there's no with or eval in context, and the symbol is marked as scoped, it is fast scoped. Such a
+ // symbol must either be global, or its defining block must need scope.
+ assert symbol.isGlobal() || lc.getDefiningBlock(symbol).needsScope() : symbol.getName();
+ return true;
+ }
+ if(symbol.isGlobal()) {
+ // Shortcut: if there's a with or eval in context, globals can't be fast scoped
+ return false;
+ }
+ // Otherwise, check if there's a dynamic scope between use of the symbol and its definition
+ final String name = symbol.getName();
+ boolean previousWasBlock = false;
+ for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
+ final LexicalContextNode node = it.next();
+ if(node instanceof Block) {
+ // If this block defines the symbol, then we can fast scope the symbol.
+ final Block block = (Block)node;
+ if(block.getExistingSymbol(name) == symbol) {
+ assert block.needsScope();
+ return true;
+ }
+ previousWasBlock = true;
+ } else {
+ if((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && isFunctionDynamicScope((FunctionNode)node))) {
+ // If we hit a scope that can have symbols introduced into it at run time before finding the defining
+ // block, the symbol can't be fast scoped. A WithNode only counts if we've immediately seen a block
+ // before - its block. Otherwise, we are currently processing the WithNode's expression, and that's
+ // obviously not subjected to introducing new symbols.
+ return false;
+ }
+ previousWasBlock = false;
}
}
- return true;
+ // Should've found the symbol defined in a block
+ throw new AssertionError();
}
private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
- method.load(isFastScope(symbol) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
+ method.load(isFastScope(symbol) ? getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol) : -1);
final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
scopeCall.generateInvoke(method);
return method;
@@ -271,10 +403,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
int depth = 0;
- final Block definingBlock = symbol.getBlock();
- for(final Iterator<Block> blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) {
+ final String name = symbol.getName();
+ for(final Iterator<Block> blocks = getLexicalContext().getBlocks(startingBlock); blocks.hasNext();) {
final Block currentBlock = blocks.next();
- if (currentBlock == definingBlock) {
+ if (currentBlock.getExistingSymbol(name) == symbol) {
return depth;
}
if (currentBlock.needsScope()) {
@@ -285,9 +417,9 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
- final int depth = getScopeProtoDepth(getCurrentBlock(), symbol);
+ final int depth = getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol);
assert depth != -1;
- if(depth > 0) {
+ if (depth > 0) {
if (swap) {
method.swap();
}
@@ -328,46 +460,46 @@ final class CodeGenerator extends NodeOperatorVisitor {
*/
final CodeGenerator codegen = this;
- node.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ node.accept(new NodeVisitor() {
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
loadIdent(identNode);
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
if (!baseAlreadyOnStack) {
load(accessNode.getBase()).convert(Type.OBJECT);
}
assert method.peekType().isObject();
method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
if (!baseAlreadyOnStack) {
load(indexNode.getBase()).convert(Type.OBJECT);
load(indexNode.getIndex());
}
method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction());
- return null;
+ return false;
}
@Override
- public Node enterFunctionNode(FunctionNode functionNode) {
+ public boolean enterFunctionNode(FunctionNode functionNode) {
// function nodes will always leave a constructed function object on stack, no need to load the symbol
// separately as in enterDefault()
functionNode.accept(codegen);
- return null;
+ return false;
}
@Override
- public Node enterDefault(final Node otherNode) {
+ public boolean enterDefault(final Node otherNode) {
otherNode.accept(codegen); // generate code for whatever we are looking at.
method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
- return null;
+ return false;
}
});
@@ -375,14 +507,9 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
- if (accessNode.testResolved()) {
- return null;
- }
-
+ public boolean enterAccessNode(final AccessNode accessNode) {
load(accessNode);
-
- return null;
+ return false;
}
/**
@@ -407,7 +534,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
if (symbol.hasSlot() && !isInternal) {
- assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getCurrentFunctionNode();
+ assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getLexicalContext().getCurrentFunction();
if (symbol.getSymbolType().isNumber()) {
numbers.add(symbol);
} else if (symbol.getSymbolType().isObject()) {
@@ -441,22 +568,20 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param block block containing symbols.
*/
private void symbolInfo(final Block block) {
- for (final Symbol symbol : block.getFrame().getSymbols()) {
- method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
+ for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol symbol = iter.next();
+ if (symbol.hasSlot()) {
+ method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
+ }
}
}
@Override
- public Node enterBlock(final Block block) {
- if (block.testResolved()) {
- return null;
- }
- lexicalContext.push(block);
-
+ public boolean enterBlock(final Block block) {
method.label(block.getEntryLabel());
initLocals(block);
- return block;
+ return true;
}
@Override
@@ -464,10 +589,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(block.getBreakLabel());
symbolInfo(block);
- if (block.needsScope()) {
+ if (block.needsScope() && !block.isTerminal()) {
popBlockScope(block);
}
- lexicalContext.pop(block);
+ --nextFreeSlotsSize;
return block;
}
@@ -477,34 +602,30 @@ final class CodeGenerator extends NodeOperatorVisitor {
final Label skipLabel = new Label("skip_catch");
/* pop scope a la try-finally */
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
method.invoke(ScriptObject.GET_PROTO);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
method._goto(skipLabel);
method.label(exitLabel);
method._catch(recoveryLabel);
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
method.invoke(ScriptObject.GET_PROTO);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
method.athrow();
method.label(skipLabel);
method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class);
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
- if (breakNode.testResolved()) {
- return null;
- }
-
- for (int i = 0; i < breakNode.getScopeNestingLevel(); i++) {
+ public boolean enterBreakNode(final BreakNode breakNode) {
+ final BreakableNode breakFrom = getLexicalContext().getBreakable(breakNode.getLabel());
+ for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(breakFrom); i++) {
closeWith();
}
+ method.splitAwareGoto(getLexicalContext(), breakFrom.getBreakLabel());
- method.splitAwareGoto(breakNode.getTargetLabel());
-
- return null;
+ return false;
}
private int loadArgs(final List<Node> args) {
@@ -541,21 +662,17 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterCallNode(final CallNode callNode) {
- if (callNode.testResolved()) {
- return null;
- }
-
+ public boolean enterCallNode(final CallNode callNode) {
final List<Node> args = callNode.getArgs();
final Node function = callNode.getFunction();
- final Block currentBlock = getCurrentBlock();
+ final Block currentBlock = getLexicalContext().getCurrentBlock();
- function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ function.accept(new NodeVisitor() {
private void sharedScopeCall(final IdentNode identNode, final int flags) {
final Symbol symbol = identNode.getSymbol();
int scopeCallFlags = flags;
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
if (isFastScope(symbol)) {
method.load(getScopeProtoDepth(currentBlock, symbol));
scopeCallFlags |= CALLSITE_FAST_SCOPE;
@@ -591,7 +708,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
// We don't need ScriptFunction object for 'eval'
method.pop();
- method.loadScope(); // Load up self (scope).
+ method.loadCompilerConstant(SCOPE); // Load up self (scope).
final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
// load evaluated code
@@ -618,7 +735,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
final Symbol symbol = node.getSymbol();
if (symbol.isScope()) {
@@ -626,27 +743,27 @@ final class CodeGenerator extends NodeOperatorVisitor {
final int useCount = symbol.getUseCount();
// Threshold for generating shared scope callsite is lower for fast scope symbols because we know
- // we can dial in the correct scope. However, we als need to enable it for non-fast scopes to
+ // we can dial in the correct scope. However, we also need to enable it for non-fast scopes to
// support huge scripts like mandreel.js.
if (callNode.isEval()) {
evalCall(node, flags);
} else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
|| (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
- || callNode.inWithBlock()) {
+ || CodeGenerator.this.inDynamicScope()) {
scopeCall(node, flags);
} else {
sharedScopeCall(node, flags);
}
- assert method.peekType().equals(callNode.getType());
+ assert method.peekType().equals(callNode.getType()) : method.peekType() + "!=" + callNode.getType();
} else {
enterDefault(node);
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
load(node.getBase());
method.convert(Type.OBJECT);
method.dup();
@@ -655,35 +772,34 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags());
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
@Override
- public Node enterFunctionNode(final FunctionNode callee) {
+ public boolean enterFunctionNode(final FunctionNode origCallee) {
+ // NOTE: visiting the callee will leave a constructed ScriptFunction object on the stack if
+ // callee.needsCallee() == true
+ final FunctionNode callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
+
final boolean isVarArg = callee.isVarArg();
final int argCount = isVarArg ? -1 : callee.getParameters().size();
final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
- if (callee.needsCallee()) {
- newFunctionObject(callee);
- }
-
- if (callee.isStrictMode()) { // self is undefined
+ if (callee.isStrict()) { // self is undefined
method.loadUndefined(Type.OBJECT);
} else { // get global from scope (which is the self)
globalInstance();
}
loadArgs(args, signature, isVarArg, argCount);
+ assert callee.getCompileUnit() != null : "no compile unit for " + callee.getName() + " " + Debug.id(callee) + " " + callNode;
method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
- functionNodeIsCallee = true;
- callee.accept(CodeGenerator.this);
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
load(node.getBase());
method.convert(Type.OBJECT);
method.dup();
@@ -697,11 +813,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags());
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
@Override
- protected Node enterDefault(final Node node) {
+ protected boolean enterDefault(final Node node) {
// Load up function.
load(function);
method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions
@@ -709,58 +825,41 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
});
method.store(callNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- if (continueNode.testResolved()) {
- return null;
- }
-
- for (int i = 0; i < continueNode.getScopeNestingLevel(); i++) {
+ public boolean enterContinueNode(final ContinueNode continueNode) {
+ final LoopNode continueTo = getLexicalContext().getContinueTo(continueNode.getLabel());
+ for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(continueTo); i++) {
closeWith();
}
+ method.splitAwareGoto(getLexicalContext(), continueTo.getContinueLabel());
- method.splitAwareGoto(continueNode.getTargetLabel());
-
- return null;
- }
-
- @Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
+ return false;
}
@Override
- public Node enterEmptyNode(final EmptyNode emptyNode) {
- return null;
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
+ return false;
}
@Override
- public Node enterExecuteNode(final ExecuteNode executeNode) {
- if (executeNode.testResolved()) {
- return null;
- }
-
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
final Node expression = executeNode.getExpression();
expression.accept(this);
- return null;
+ return false;
}
@Override
- public Node enterForNode(final ForNode forNode) {
- if (forNode.testResolved()) {
- return null;
- }
-
+ public boolean enterForNode(final ForNode forNode) {
final Node test = forNode.getTest();
final Block body = forNode.getBody();
final Node modify = forNode.getModify();
@@ -790,6 +889,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
new Store<Node>(init) {
@Override
+ protected void storeNonDiscard() {
+ return;
+ }
+ @Override
protected void evaluate() {
method.load(iter);
method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
@@ -829,7 +932,19 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(breakLabel);
}
- return null;
+ return false;
+ }
+
+ private static int assignSlots(final Block block, final int firstSlot) {
+ int nextSlot = firstSlot;
+ for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol next = iter.next();
+ if (next.hasSlot()) {
+ next.setSlot(nextSlot);
+ nextSlot += next.slotCount();
+ }
+ }
+ return nextSlot;
}
/**
@@ -838,21 +953,26 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param block block with local vars.
*/
private void initLocals(final Block block) {
- final FunctionNode function = lexicalContext.getFunction(block);
- final boolean isFunctionNode = block == function;
-
- /*
- * Get the symbols from the frame and realign the frame so that all
- * slots get correct numbers. The slot numbering is not fixed until
- * after initLocals has been run
- */
- final Frame frame = block.getFrame();
- final List<Symbol> symbols = frame.getSymbols();
+ final boolean isFunctionBody = getLexicalContext().isFunctionBody();
- /* Fix the predefined slots so they have numbers >= 0, like varargs. */
- frame.realign();
+ final int nextFreeSlot;
+ if (isFunctionBody) {
+ // On entry to function, start with slot 0
+ nextFreeSlot = 0;
+ } else {
+ // Otherwise, continue from previous block's first free slot
+ nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
+ }
+ if(nextFreeSlotsSize == nextFreeSlots.length) {
+ final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
+ System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
+ nextFreeSlots = newNextFreeSlots;
+ }
+ nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
- if (isFunctionNode) {
+ final FunctionNode function = getLexicalContext().getCurrentFunction();
+ if (isFunctionBody) {
+ /* Fix the predefined slots so they have numbers >= 0, like varargs. */
if (function.needsParentScope()) {
initParentScope();
}
@@ -876,14 +996,18 @@ final class CodeGenerator extends NodeOperatorVisitor {
final List<String> nameList = new ArrayList<>();
final List<Symbol> locals = new ArrayList<>();
-
// Initalize symbols and values
final List<Symbol> newSymbols = new ArrayList<>();
final List<Symbol> values = new ArrayList<>();
final boolean hasArguments = function.needsArguments();
- for (final Symbol symbol : symbols) {
- if (symbol.isInternal() || symbol.isThis()) {
+
+ final Iterator<Symbol> symbols = block.symbolIterator();
+
+ while (symbols.hasNext()) {
+ final Symbol symbol = symbols.next();
+
+ if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
}
@@ -907,9 +1031,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}
- /* Correct slot numbering again */
- frame.realign();
-
// we may have locals that need to be initialized
initSymbols(locals);
@@ -931,7 +1052,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
@Override
protected void loadScope(MethodEmitter m) {
if(function.needsParentScope()) {
- m.loadScope();
+ m.loadCompilerConstant(SCOPE);
} else {
m.loadNull();
}
@@ -940,118 +1061,102 @@ final class CodeGenerator extends NodeOperatorVisitor {
foc.makeObject(method);
// runScript(): merge scope into global
- if (isFunctionNode && function.isProgram()) {
+ if (isFunctionBody && function.isProgram()) {
method.invoke(ScriptRuntime.MERGE_SCOPE);
}
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
} else {
// Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
// we need to assign them separately here.
int nextParam = 0;
- if (isFunctionNode && function.isVarArg()) {
+ if (isFunctionBody && function.isVarArg()) {
for (final IdentNode param : function.getParameters()) {
param.getSymbol().setFieldIndex(nextParam++);
}
}
+
+ final Iterator<Symbol> iter = block.symbolIterator();
+ final List<Symbol> symbols = new ArrayList<>();
+ while (iter.hasNext()) {
+ symbols.add(iter.next());
+ }
initSymbols(symbols);
}
// Debugging: print symbols? @see --print-symbols flag
- printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
+ printSymbols(block, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
}
private void initArguments(final FunctionNode function) {
- method.loadVarArgs();
+ method.loadCompilerConstant(VARARGS);
if(function.needsCallee()) {
- method.loadCallee();
+ method.loadCompilerConstant(CALLEE);
} else {
// If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the
// caller.
- assert function.isStrictMode();
+ assert function.isStrict();
method.loadNull();
}
method.load(function.getParameters().size());
globalAllocateArguments();
- method.storeArguments();
+ method.storeCompilerConstant(ARGUMENTS);
}
private void initParentScope() {
- method.loadCallee();
+ method.loadCompilerConstant(CALLEE);
method.invoke(ScriptFunction.GET_SCOPE);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- final boolean isCallee = functionNodeIsCallee;
- functionNodeIsCallee = false;
-
- if (functionNode.testResolved()) {
- return null;
- }
-
- if(!(isCallee || functionNode == compiler.getFunctionNode())) {
- newFunctionObject(functionNode);
- }
-
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return null;
+ // Must do it now; can't postpone it until leaveFunctionNode()
+ newFunctionObject(functionNode, functionNode);
+ return false;
}
- LOG.info("=== BEGIN " + functionNode.getName());
- lexicalContext.push(functionNode);
+ LOG.info("=== BEGIN ", functionNode.getName());
- setCurrentCompileUnit(functionNode.getCompileUnit());
- assert getCurrentCompileUnit() != null;
+ assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode);
+ push(functionNode.getCompileUnit());
+ assert !compileUnits.isEmpty();
- setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode));
- functionNode.setMethodEmitter(method);
+ pushMethodEmitter(unit.getClassEmitter().method(functionNode));
// Mark end for variable tables.
method.begin();
- method.label(functionNode.getEntryLabel());
- initLocals(functionNode);
- functionNode.setState(CompilationState.EMITTED);
-
- return functionNode;
+ return true;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
- // Mark end for variable tables.
- method.label(functionNode.getBreakLabel());
-
- if (!functionNode.needsScope()) {
- method.markerVariable(LEAF.tag(), functionNode.getEntryLabel(), functionNode.getBreakLabel());
- }
-
- symbolInfo(functionNode);
try {
method.end(); // wrap up this method
+ pop(functionNode.getCompileUnit());
+ popMethodEmitter(method);
+ LOG.info("=== END ", functionNode.getName());
+
+ final FunctionNode newFunctionNode = functionNode.setState(getLexicalContext(), CompilationState.EMITTED);
+
+ newFunctionObject(newFunctionNode, functionNode);
+ return newFunctionNode;
} catch (final Throwable t) {
Context.printStackTrace(t);
final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName());
e.initCause(t);
throw e;
}
-
- lexicalContext.pop(functionNode);
- LOG.info("=== END " + functionNode.getName());
- return functionNode;
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
- return null;
+ public boolean enterIdentNode(final IdentNode identNode) {
+ return false;
}
@Override
- public Node enterIfNode(final IfNode ifNode) {
- if (ifNode.testResolved()) {
- return null;
- }
-
+ public boolean enterIfNode(final IfNode ifNode) {
final Node test = ifNode.getTest();
final Block pass = ifNode.getPass();
final Block fail = ifNode.getFail();
@@ -1082,30 +1187,21 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(afterLabel);
}
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
- if (indexNode.testResolved()) {
- return null;
- }
-
+ public boolean enterIndexNode(final IndexNode indexNode) {
load(indexNode);
-
- return null;
+ return false;
}
@Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
- if (lineNumberNode.testResolved()) {
- return null;
- }
-
- final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")");
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ final Label label = new Label((String)null);
method.label(label);
method.lineNumber(lineNumberNode.getLineNumber(), label);
- return null;
+ return false;
}
/**
@@ -1131,43 +1227,43 @@ final class CodeGenerator extends NodeOperatorVisitor {
final Type elementType = arrayType.getElementType();
if (units != null) {
- final CompileUnit savedCompileUnit = getCurrentCompileUnit();
- final MethodEmitter savedMethod = getCurrentMethodEmitter();
+ final MethodEmitter savedMethod = method;
- try {
- for (final ArrayUnit unit : units) {
- setCurrentCompileUnit(unit.getCompileUnit());
+ for (final ArrayUnit arrayUnit : units) {
+ push(arrayUnit.getCompileUnit());
- final String className = getCurrentCompileUnit().getUnitClassName();
- final String name = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag());
- final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
+ final String className = unit.getUnitClassName();
+ final String name = getLexicalContext().getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName());
+ final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
- setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature));
- method.setFunctionNode(getCurrentFunctionNode());
- method.begin();
+ final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
+ pushMethodEmitter(me);
- fixScopeSlot();
+ method.setFunctionNode(getLexicalContext().getCurrentFunction());
+ method.begin();
- method.load(arrayType, SPLIT_ARRAY_ARG.slot());
+ fixScopeSlot();
- for (int i = unit.getLo(); i < unit.getHi(); i++) {
- storeElement(nodes, elementType, postsets[i]);
- }
-
- method._return();
- method.end();
+ method.load(arrayType, SPLIT_ARRAY_ARG.slot());
- savedMethod.loadThis();
- savedMethod.swap();
- savedMethod.loadCallee();
- savedMethod.swap();
- savedMethod.loadScope();
- savedMethod.swap();
- savedMethod.invokestatic(className, name, signature);
+ for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) {
+ storeElement(nodes, elementType, postsets[i]);
}
- } finally {
- setCurrentCompileUnit(savedCompileUnit);
- setCurrentMethodEmitter(savedMethod);
+
+ method._return();
+ method.end();
+ popMethodEmitter(me);
+
+ assert method == savedMethod;
+ method.loadCompilerConstant(THIS);
+ method.swap();
+ method.loadCompilerConstant(CALLEE);
+ method.swap();
+ method.loadCompilerConstant(SCOPE);
+ method.swap();
+ method.invokestatic(className, name, signature);
+
+ pop(unit);
}
return method;
@@ -1217,12 +1313,12 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param string string to load
*/
void loadConstant(final String string) {
- final String unitClassName = getCurrentCompileUnit().getUnitClassName();
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String unitClassName = unit.getUnitClassName();
+ final ClassEmitter classEmitter = unit.getClassEmitter();
final int index = compiler.getConstantData().add(string);
method.load(index);
- method.invokestatic(unitClassName, GET_STRING.tag(), methodDescriptor(String.class, int.class));
+ method.invokestatic(unitClassName, GET_STRING.symbolName(), methodDescriptor(String.class, int.class));
classEmitter.needGetConstantMethod(String.class);
}
@@ -1233,14 +1329,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param object object to load
*/
void loadConstant(final Object object) {
- final String unitClassName = getCurrentCompileUnit().getUnitClassName();
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String unitClassName = unit.getUnitClassName();
+ final ClassEmitter classEmitter = unit.getClassEmitter();
final int index = compiler.getConstantData().add(object);
final Class<?> cls = object.getClass();
if (cls == PropertyMap.class) {
method.load(index);
- method.invokestatic(unitClassName, GET_MAP.tag(), methodDescriptor(PropertyMap.class, int.class));
+ method.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
classEmitter.needGetConstantMethod(PropertyMap.class);
} else if (cls.isArray()) {
method.load(index);
@@ -1303,14 +1399,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
return loadRegexToken(regexToken);
}
// emit field
- final String regexName = getCurrentFunctionNode().uniqueName(REGEX_PREFIX.tag());
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String regexName = getLexicalContext().getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName());
+ final ClassEmitter classEmitter = unit.getClassEmitter();
classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
regexFieldCount++;
// get field, if null create new regex, finally clone regex object
- method.getStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
+ method.getStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
method.dup();
final Label cachedLabel = new Label("cached");
method.ifnonnull(cachedLabel);
@@ -1318,7 +1414,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.pop();
loadRegexToken(regexToken);
method.dup();
- method.putStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
+ method.putStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
method.label(cachedLabel);
globalRegExpCopy();
@@ -1328,18 +1424,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
assert literalNode.getSymbol() != null : literalNode + " has no symbol";
load(literalNode).store(literalNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterObjectNode(final ObjectNode objectNode) {
- if (objectNode.testResolved()) {
- return null;
- }
-
+ public boolean enterObjectNode(final ObjectNode objectNode) {
final List<Node> elements = objectNode.getElements();
final int size = elements.size();
@@ -1404,14 +1496,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (!hasGettersSetters) {
method.store(objectNode.getSymbol());
- return null;
+ return false;
}
for (final Node element : elements) {
final PropertyNode propertyNode = (PropertyNode)element;
final Object key = propertyNode.getKey();
- final FunctionNode getter = (FunctionNode)propertyNode.getGetter();
- final FunctionNode setter = (FunctionNode)propertyNode.getSetter();
+ final FunctionNode getter = propertyNode.getGetter();
+ final FunctionNode setter = propertyNode.getSetter();
if (getter == null && setter == null) {
continue;
@@ -1436,35 +1528,25 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(objectNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- if (returnNode.testResolved()) {
- return null;
- }
-
- // Set the split return flag in the scope if this is a split method fragment.
- if (method.getSplitNode() != null) {
- assert method.getSplitNode().hasReturn() : "unexpected return in split node";
+ public boolean enterReturnNode(final ReturnNode returnNode) {
+ method.registerReturn();
- method.loadScope();
- method.checkcast(Scope.class);
- method.load(0);
- method.invoke(Scope.SET_SPLIT_STATE);
- }
+ final Type returnType = getLexicalContext().getCurrentFunction().getReturnType();
final Node expression = returnNode.getExpression();
if (expression != null) {
load(expression);
} else {
- method.loadUndefined(getCurrentFunctionNode().getReturnType());
+ method.loadUndefined(returnType);
}
- method._return(getCurrentFunctionNode().getReturnType());
+ method._return(returnType);
- return null;
+ return false;
}
private static boolean isNullLiteral(final Node node) {
@@ -1542,19 +1624,20 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
assert args.size() == 2;
- final Node lhs = args.get(0);
- final Node rhs = args.get(1);
-
final Type returnType = node.getType();
- load(lhs);
- load(rhs);
+
+ load(args.get(0));
+ load(args.get(1));
Request finalRequest = request;
+ //if the request is a comparison, i.e. one that can be reversed
+ //it keeps its semantic, but make sure that the object comes in
+ //last
final Request reverse = Request.reverse(request);
- if (method.peekType().isObject() && reverse != null) {
- if (!method.peekType(1).isObject()) {
- method.swap();
+ if (method.peekType().isObject() && reverse != null) { //rhs is object
+ if (!method.peekType(1).isObject()) { //lhs is not object
+ method.swap(); //prefer object as lhs
finalRequest = reverse;
}
}
@@ -1581,11 +1664,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
- if (runtimeNode.testResolved()) {
- return null;
- }
-
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
/*
* First check if this should be something other than a runtime node
* AccessSpecializer might have changed the type
@@ -1624,7 +1703,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.add();
method.convert(type);
method.store(symbol);
- return null;
+ return false;
default:
// it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar
// assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state";
@@ -1636,11 +1715,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
final List<Node> args = runtimeNode.getArgs();
if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) {
- return null;
+ return false;
}
if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
- return null;
+ return false;
}
for (final Node arg : runtimeNode.getArgs()) {
@@ -1658,129 +1737,146 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.convert(runtimeNode.getType());
method.store(runtimeNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterSplitNode(final SplitNode splitNode) {
- if (splitNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSplitNode(final SplitNode splitNode) {
final CompileUnit splitCompileUnit = splitNode.getCompileUnit();
- final FunctionNode fn = getCurrentFunctionNode();
+ final FunctionNode fn = getLexicalContext().getCurrentFunction();
final String className = splitCompileUnit.getUnitClassName();
final String name = splitNode.getName();
- final Class<?> rtype = fn.getReturnType().getTypeClass();
- final boolean needsArguments = fn.needsArguments();
- final Class<?>[] ptypes = needsArguments ?
+ final Class<?> rtype = fn.getReturnType().getTypeClass();
+ final boolean needsArguments = fn.needsArguments();
+ final Class<?>[] ptypes = needsArguments ?
new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class, Object.class} :
new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class};
- setCurrentCompileUnit(splitCompileUnit);
- splitNode.setCompileUnit(splitCompileUnit);
+ final MethodEmitter caller = method;
+ push(splitCompileUnit);
final Call splitCall = staticCallNoLookup(
className,
name,
methodDescriptor(rtype, ptypes));
- setCurrentMethodEmitter(
- splitCompileUnit.getClassEmitter().method(
- EnumSet.of(Flag.PUBLIC, Flag.STATIC),
- name,
- rtype,
- ptypes));
+ final MethodEmitter splitEmitter =
+ splitCompileUnit.getClassEmitter().method(
+ splitNode,
+ name,
+ rtype,
+ ptypes);
+
+ pushMethodEmitter(splitEmitter);
method.setFunctionNode(fn);
- method.setSplitNode(splitNode);
- splitNode.setMethodEmitter(method);
- final MethodEmitter caller = splitNode.getCaller();
- if(fn.needsCallee()) {
- caller.loadCallee();
+ if (fn.needsCallee()) {
+ caller.loadCompilerConstant(CALLEE);
} else {
caller.loadNull();
}
- caller.loadThis();
- caller.loadScope();
+ caller.loadCompilerConstant(THIS);
+ caller.loadCompilerConstant(SCOPE);
if (needsArguments) {
- caller.loadArguments();
+ caller.loadCompilerConstant(ARGUMENTS);
}
caller.invoke(splitCall);
- caller.storeResult();
+ caller.storeCompilerConstant(RETURN);
method.begin();
method.loadUndefined(fn.getReturnType());
- method.storeResult();
+ method.storeCompilerConstant(RETURN);
fixScopeSlot();
- return splitNode;
+ return true;
}
private void fixScopeSlot() {
- if (getCurrentFunctionNode().getScopeNode().getSymbol().getSlot() != SCOPE.slot()) {
+ if (getLexicalContext().getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
// TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method)
method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
}
}
@Override
public Node leaveSplitNode(final SplitNode splitNode) {
+ assert method instanceof SplitMethodEmitter;
+ final boolean hasReturn = method.hasReturn();
+ final List<Label> targets = method.getExternalTargets();
+
try {
// Wrap up this method.
- method.loadResult();
- method._return(getCurrentFunctionNode().getReturnType());
+
+ method.loadCompilerConstant(RETURN);
+ method._return(getLexicalContext().getCurrentFunction().getReturnType());
method.end();
+
+ pop(splitNode.getCompileUnit());
+ popMethodEmitter(method);
+
} catch (final Throwable t) {
Context.printStackTrace(t);
- final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentFunctionNode().getSource().getName());
+ final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getLexicalContext().getCurrentFunction().getSource().getName());
e.initCause(t);
throw e;
}
// Handle return from split method if there was one.
- final MethodEmitter caller = splitNode.getCaller();
- final List<Label> targets = splitNode.getExternalTargets();
- final int targetCount = targets.size();
+ final MethodEmitter caller = method;
+ final int targetCount = targets.size();
+
+ //no external jump targets or return in switch node
+ if (!hasReturn && targets.isEmpty()) {
+ return splitNode;
+ }
+
+ caller.loadCompilerConstant(SCOPE);
+ caller.checkcast(Scope.class);
+ caller.invoke(Scope.GET_SPLIT_STATE);
- if (splitNode.hasReturn() || targetCount > 0) {
+ final Label breakLabel = new Label("no_split_state");
+ // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
- caller.loadScope();
- caller.checkcast(Scope.class);
- caller.invoke(Scope.GET_SPLIT_STATE);
+ //the common case is that we don't need a switch
+ if (targetCount == 0) {
+ assert hasReturn;
+ caller.ifne(breakLabel);
+ //has to be zero
+ caller.label(new Label("split_return"));
+ method.loadCompilerConstant(RETURN);
+ caller._return(getLexicalContext().getCurrentFunction().getReturnType());
+ caller.label(breakLabel);
+ } else {
+ assert !targets.isEmpty();
- // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
- final Label breakLabel = new Label("no_split_state");
- final int low = splitNode.hasReturn() ? 0 : 1;
- final int labelCount = targetCount + 1 - low;
- final Label[] labels = new Label[labelCount];
+ final int low = hasReturn ? 0 : 1;
+ final int labelCount = targetCount + 1 - low;
+ final Label[] labels = new Label[labelCount];
for (int i = 0; i < labelCount; i++) {
- labels[i] = new Label("split_state_" + i);
+ labels[i] = new Label(i == 0 ? "split_return" : "split_" + targets.get(i - 1));
}
-
caller.tableswitch(low, targetCount, breakLabel, labels);
for (int i = low; i <= targetCount; i++) {
caller.label(labels[i - low]);
if (i == 0) {
- caller.loadResult();
- caller._return(getCurrentFunctionNode().getReturnType());
+ caller.loadCompilerConstant(RETURN);
+ caller._return(getLexicalContext().getCurrentFunction().getReturnType());
} else {
// Clear split state.
- caller.loadScope();
+ caller.loadCompilerConstant(SCOPE);
caller.checkcast(Scope.class);
caller.load(-1);
caller.invoke(Scope.SET_SPLIT_STATE);
- caller.splitAwareGoto(targets.get(i - 1));
+ caller.splitAwareGoto(getLexicalContext(), targets.get(i - 1));
}
}
-
caller.label(breakLabel);
}
@@ -1788,11 +1884,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- if (switchNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
final Node expression = switchNode.getExpression();
final Symbol tag = switchNode.getTag();
final boolean allInteger = tag.getSymbolType().isInteger();
@@ -1810,7 +1902,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (cases.isEmpty()) {
method.label(breakLabel);
- return null;
+ return false;
}
if (allInteger) {
@@ -1916,15 +2008,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(breakLabel);
}
- return null;
+ return false;
}
@Override
- public Node enterThrowNode(final ThrowNode throwNode) {
- if (throwNode.testResolved()) {
- return null;
- }
-
+ public boolean enterThrowNode(final ThrowNode throwNode) {
method._new(ECMAException.class).dup();
final Node expression = throwNode.getExpression();
@@ -1943,15 +2031,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.athrow();
- return null;
+ return false;
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
- if (tryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterTryNode(final TryNode tryNode) {
final Block body = tryNode.getBody();
final List<Block> catchBlocks = tryNode.getCatchBlocks();
final Symbol symbol = tryNode.getException();
@@ -1974,74 +2058,68 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(symbol);
for (int i = 0; i < catchBlocks.size(); i++) {
- final Block saveBlock = getCurrentBlock();
final Block catchBlock = catchBlocks.get(i);
- setCurrentBlock(catchBlock);
-
- try {
- enterBlock(catchBlock);
+ //TODO this is very ugly - try not to call enter/leave methods directly
+ //better to use the implicit lexical context scoping given by the visitor's
+ //accept method.
+ getLexicalContext().push(catchBlock);
+ enterBlock(catchBlock);
- final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0);
- final IdentNode exception = catchNode.getException();
- final Node exceptionCondition = catchNode.getExceptionCondition();
- final Block catchBody = catchNode.getBody();
+ final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0);
+ final IdentNode exception = catchNode.getException();
+ final Node exceptionCondition = catchNode.getExceptionCondition();
+ final Block catchBody = catchNode.getBody();
- if (catchNode.isSyntheticRethrow()) {
- // Generate catch body (inlined finally) and rethrow exception
- catchBody.accept(this);
- method.load(symbol).athrow();
- lexicalContext.pop(catchBlock);
- continue;
+ new Store<IdentNode>(exception) {
+ @Override
+ protected void storeNonDiscard() {
+ return;
+ }
+ @Override
+ protected void evaluate() {
+ /*
+ * If caught object is an instance of ECMAException, then
+ * bind obj.thrown to the script catch var. Or else bind the
+ * caught object itself to the script catch var.
+ */
+ final Label notEcmaException = new Label("no_ecma_exception");
+
+ method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
+ method.checkcast(ECMAException.class); //TODO is this necessary?
+ method.getField(ECMAException.THROWN);
+ method.label(notEcmaException);
}
+ }.store();
- new Store<IdentNode>(exception) {
- @Override
- protected void evaluate() {
- /*
- * If caught object is an instance of ECMAException, then
- * bind obj.thrown to the script catch var. Or else bind the
- * caught object itself to the script catch var.
- */
- final Label notEcmaException = new Label("no_ecma_exception");
-
- method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
- method.checkcast(ECMAException.class); //TODO is this necessary?
- method.getField(ECMAException.THROWN);
- method.label(notEcmaException);
- }
- }.store();
+ final Label next;
- final Label next;
+ if (exceptionCondition != null) {
+ next = new Label("next");
+ load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
+ } else {
+ next = null;
+ }
- if (exceptionCondition != null) {
- next = new Label("next");
- load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
- } else {
- next = null;
- }
+ catchBody.accept(this);
- catchBody.accept(this);
+ if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
+ method._goto(skip);
+ }
- if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
+ if (next != null) {
+ if (i + 1 == catchBlocks.size()) {
+ // no next catch block - rethrow if condition failed
method._goto(skip);
+ method.label(next);
+ method.load(symbol).athrow();
+ } else {
+ method.label(next);
}
-
- if (next != null) {
- if (i + 1 == catchBlocks.size()) {
- // no next catch block - rethrow if condition failed
- method._goto(skip);
- method.label(next);
- method.load(symbol).athrow();
- } else {
- method.label(next);
- }
- }
-
- leaveBlock(catchBlock);
- } finally {
- setCurrentBlock(saveBlock);
}
+
+ leaveBlock(catchBlock);
+ getLexicalContext().pop(catchBlock);
}
method.label(skip);
@@ -2049,15 +2127,15 @@ final class CodeGenerator extends NodeOperatorVisitor {
// Finally body is always inlined elsewhere so it doesn't need to be emitted
- return null;
+ return false;
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
final Node init = varNode.getInit();
- if (varNode.testResolved() || init == null) {
- return null;
+ if (init == null) {
+ return false;
}
final Symbol varSymbol = varNode.getSymbol();
@@ -2067,7 +2145,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
final boolean needsScope = varSymbol.isScope();
if (needsScope) {
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
}
load(init);
@@ -2087,22 +2165,18 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(varSymbol);
}
- return null;
+ return false;
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
- if (whileNode.testResolved()) {
- return null;
- }
-
+ public boolean enterWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
final Block body = whileNode.getBody();
final Label breakLabel = whileNode.getBreakLabel();
final Label continueLabel = whileNode.getContinueLabel();
final Label loopLabel = new Label("loop");
- if (!(whileNode instanceof DoWhileNode)) {
+ if (!whileNode.isDoWhile()) {
method._goto(continueLabel);
}
@@ -2114,90 +2188,95 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(breakLabel);
}
- return null;
+ return false;
}
private void closeWith() {
- method.loadScope();
- method.invoke(ScriptRuntime.CLOSE_WITH);
- method.storeScope();
+ if(method.hasScope()) {
+ method.loadCompilerConstant(SCOPE);
+ method.invoke(ScriptRuntime.CLOSE_WITH);
+ method.storeCompilerConstant(SCOPE);
+ }
}
@Override
- public Node enterWithNode(final WithNode withNode) {
- if (withNode.testResolved()) {
- return null;
- }
-
+ public boolean enterWithNode(final WithNode withNode) {
final Node expression = withNode.getExpression();
final Node body = withNode.getBody();
- final Label tryLabel = new Label("with_try");
- final Label endLabel = new Label("with_end");
- final Label catchLabel = new Label("with_catch");
- final Label exitLabel = new Label("with_exit");
-
- method.label(tryLabel);
+ // It is possible to have a "pathological" case where the with block does not reference *any* identifiers. It's
+ // pointless, but legal. In that case, if nothing else in the method forced the assignment of a slot to the
+ // scope object, its' possible that it won't have a slot assigned. In this case we'll only evaluate expression
+ // for its side effect and visit the body, and not bother opening and closing a WithObject.
+ final boolean hasScope = method.hasScope();
+
+ final Label tryLabel;
+ if(hasScope) {
+ tryLabel = new Label("with_try");
+ method.label(tryLabel);
+ method.loadCompilerConstant(SCOPE);
+ } else {
+ tryLabel = null;
+ }
- method.loadScope();
load(expression);
-
assert expression.getType().isObject() : "with expression needs to be object: " + expression;
- method.invoke(ScriptRuntime.OPEN_WITH);
- method.storeScope();
+ if(hasScope) {
+ // Construct a WithObject if we have a scope
+ method.invoke(ScriptRuntime.OPEN_WITH);
+ method.storeCompilerConstant(SCOPE);
+ } else {
+ // We just loaded the expression for its side effect; discard it
+ method.pop();
+ }
+
+ // Always process body
body.accept(this);
- if (!body.isTerminal()) {
- closeWith();
- method._goto(exitLabel);
- }
+ if(hasScope) {
+ // Ensure we always close the WithObject
+ final Label endLabel = new Label("with_end");
+ final Label catchLabel = new Label("with_catch");
+ final Label exitLabel = new Label("with_exit");
- method.label(endLabel);
+ if (!body.isTerminal()) {
+ closeWith();
+ method._goto(exitLabel);
+ }
- method._catch(catchLabel);
- closeWith();
- method.athrow();
+ method.label(endLabel);
- method.label(exitLabel);
+ method._catch(catchLabel);
+ closeWith();
+ method.athrow();
- method._try(tryLabel, endLabel, catchLabel);
+ method.label(exitLabel);
- return null;
+ method._try(tryLabel, endLabel, catchLabel);
+ }
+ return false;
}
@Override
- public Node enterADD(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterADD(final UnaryNode unaryNode) {
load(unaryNode.rhs());
assert unaryNode.rhs().getType().isNumber();
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterBIT_NOT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterBIT_NOT(final UnaryNode unaryNode) {
load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol());
-
- return null;
+ return false;
}
// do this better with convert calls to method. TODO
@Override
- public Node enterCONVERT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterCONVERT(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
final Type to = unaryNode.getType();
@@ -2230,15 +2309,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterDECINC(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterDECINC(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
final Type type = unaryNode.getType();
final TokenType tokenType = unaryNode.tokenType();
@@ -2282,32 +2357,28 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterDISCARD(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterDISCARD(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
+ // System.err.println("**** Enter discard " + unaryNode);
+ discard.push(rhs);
load(rhs);
- if (rhs.shouldDiscard()) {
+ if (discard.peek() == rhs) {
+ assert !rhs.isAssignment();
method.pop();
+ discard.pop();
}
-
- return null;
+ // System.err.println("**** Leave discard " + unaryNode);
+ return false;
}
@Override
- public Node enterNEW(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterNEW(final UnaryNode unaryNode) {
final CallNode callNode = (CallNode)unaryNode.rhs();
final List<Node> args = callNode.getArgs();
@@ -2317,15 +2388,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicNew(1 + loadArgs(args), getCallSiteFlags());
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterNOT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterNOT(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
load(rhs);
@@ -2342,18 +2409,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(afterLabel);
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterSUB(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSUB(final UnaryNode unaryNode) {
load(unaryNode.rhs()).neg().store(unaryNode.getSymbol());
- return null;
+ return false;
}
private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) {
@@ -2366,11 +2429,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterADD(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterADD(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2384,14 +2443,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(binaryNode.getSymbol());
}
- return null;
+ return false;
}
- private Node enterAND_OR(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ private boolean enterAND_OR(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2410,20 +2465,16 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(skip);
method.store(binaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterAND(final BinaryNode binaryNode) {
+ public boolean enterAND(final BinaryNode binaryNode) {
return enterAND_OR(binaryNode);
}
@Override
- public Node enterASSIGN(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2442,7 +2493,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
/**
@@ -2473,14 +2524,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
this.opType = opType;
}
- @Override
- public void store() {
- if (assignNode.testResolved()) {
- return;
- }
- super.store();
- }
-
protected abstract void op();
@Override
@@ -2493,35 +2536,43 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
assert RuntimeNode.Request.ADD.canSpecialize();
+ final Type lhsType = binaryNode.lhs().getType();
+ final Type rhsType = binaryNode.rhs().getType();
final boolean specialize = binaryNode.getType() == Type.OBJECT;
new AssignOp(binaryNode) {
- @Override
- protected boolean isSelfModifying() {
- return !specialize;
- }
@Override
protected void op() {
- method.add();
+ if (specialize) {
+ method.dynamicRuntimeCall(
+ new SpecializedRuntimeNode(
+ Request.ADD,
+ new Type[] {
+ lhsType,
+ rhsType,
+ },
+ Type.OBJECT).getInitialName(),
+ Type.OBJECT,
+ Request.ADD);
+ } else {
+ method.add();
+ }
}
@Override
protected void evaluate() {
- if (specialize && specializationCheck(Request.ADD, assignNode, Arrays.asList(assignNode.lhs(), assignNode.rhs()))) {
- return;
- }
super.evaluate();
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2529,11 +2580,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2541,11 +2592,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2553,11 +2604,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2565,11 +2616,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2577,11 +2628,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2589,11 +2640,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2601,11 +2652,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2613,24 +2664,24 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
method.shr();
- method.convert(Type.LONG).load(0xffff_ffffL).and();
+ method.convert(Type.LONG).load(JSType.MAX_UINT).and();
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2638,7 +2689,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
/**
@@ -2649,9 +2700,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
protected abstract void op();
protected void evaluate(final BinaryNode node) {
- if (node.testResolved()) {
- return;
- }
load(node.lhs());
load(node.rhs());
op();
@@ -2660,7 +2708,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterBIT_AND(final BinaryNode binaryNode) {
+ public boolean enterBIT_AND(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2668,11 +2716,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterBIT_OR(final BinaryNode binaryNode) {
+ public boolean enterBIT_OR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2680,11 +2728,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterBIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterBIT_XOR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2692,14 +2740,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- private Node enterComma(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ private boolean enterComma(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2707,21 +2751,21 @@ final class CodeGenerator extends NodeOperatorVisitor {
load(rhs);
method.store(binaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
+ public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
return enterComma(binaryNode);
}
@Override
- public Node enterCOMMALEFT(final BinaryNode binaryNode) {
+ public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
return enterComma(binaryNode);
}
@Override
- public Node enterDIV(final BinaryNode binaryNode) {
+ public boolean enterDIV(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2729,10 +2773,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- private Node enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
+ private boolean enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
final Type lhsType = lhs.getType();
final Type rhsType = rhs.getType();
@@ -2758,48 +2802,45 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.convert(type);
method.store(symbol);
- return null;
+ return false;
}
- private Node enterCmp(final BinaryNode binaryNode, final Condition cond) {
- if (binaryNode.testResolved()) {
- return null;
- }
+ private boolean enterCmp(final BinaryNode binaryNode, final Condition cond) {
return enterCmp(binaryNode.lhs(), binaryNode.rhs(), cond, binaryNode.getType(), binaryNode.getSymbol());
}
@Override
- public Node enterEQ(final BinaryNode binaryNode) {
+ public boolean enterEQ(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.EQ);
}
@Override
- public Node enterEQ_STRICT(final BinaryNode binaryNode) {
+ public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.EQ);
}
@Override
- public Node enterGE(final BinaryNode binaryNode) {
+ public boolean enterGE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.GE);
}
@Override
- public Node enterGT(final BinaryNode binaryNode) {
+ public boolean enterGT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.GT);
}
@Override
- public Node enterLE(final BinaryNode binaryNode) {
+ public boolean enterLE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.LE);
}
@Override
- public Node enterLT(final BinaryNode binaryNode) {
+ public boolean enterLT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.LT);
}
@Override
- public Node enterMOD(final BinaryNode binaryNode) {
+ public boolean enterMOD(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2807,11 +2848,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterMUL(final BinaryNode binaryNode) {
+ public boolean enterMUL(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2819,26 +2860,26 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterNE(final BinaryNode binaryNode) {
+ public boolean enterNE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.NE);
}
@Override
- public Node enterNE_STRICT(final BinaryNode binaryNode) {
+ public boolean enterNE_STRICT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.NE);
}
@Override
- public Node enterOR(final BinaryNode binaryNode) {
+ public boolean enterOR(final BinaryNode binaryNode) {
return enterAND_OR(binaryNode);
}
@Override
- public Node enterSAR(final BinaryNode binaryNode) {
+ public boolean enterSAR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2846,11 +2887,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSHL(final BinaryNode binaryNode) {
+ public boolean enterSHL(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2858,24 +2899,24 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSHR(final BinaryNode binaryNode) {
+ public boolean enterSHR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
method.shr();
- method.convert(Type.LONG).load(0xffff_ffffL).and();
+ method.convert(Type.LONG).load(JSType.MAX_UINT).and();
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSUB(final BinaryNode binaryNode) {
+ public boolean enterSUB(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2883,18 +2924,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- /*
- * Ternary visits.
- */
@Override
- public Node enterTernaryNode(final TernaryNode ternaryNode) {
- if (ternaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterTernaryNode(final TernaryNode ternaryNode) {
final Node lhs = ternaryNode.lhs();
final Node rhs = ternaryNode.rhs();
final Node third = ternaryNode.third();
@@ -2926,7 +2960,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(exitLabel);
method.store(symbol);
- return null;
+ return false;
}
/**
@@ -2955,7 +2989,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
- scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
+ scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
@@ -2974,7 +3008,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
- scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
+ scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
@@ -3037,9 +3071,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
/** The target node to store to, e.g. x */
private final Node target;
- /** Should the result always be discarded, no matter what? */
- private final boolean alwaysDiscard;
-
/** How deep on the stack do the arguments go if this generates an indy call */
private int depth;
@@ -3055,7 +3086,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
protected Store(final T assignNode, final Node target) {
this.assignNode = assignNode;
this.target = target;
- this.alwaysDiscard = assignNode == target;
}
/**
@@ -3077,21 +3107,21 @@ final class CodeGenerator extends NodeOperatorVisitor {
private void prologue() {
final Symbol targetSymbol = target.getSymbol();
- final Symbol scopeSymbol = getCurrentFunctionNode().getScopeNode().getSymbol();
+ final Symbol scopeSymbol = getLexicalContext().getCurrentFunction().compilerConstant(SCOPE);
/**
* This loads the parts of the target, e.g base and index. they are kept
* on the stack throughout the store and used at the end to execute it
*/
- target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ target.accept(new NodeVisitor() {
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
if (targetSymbol.isScope()) {
method.load(scopeSymbol);
depth++;
}
- return null;
+ return false;
}
private void enterBaseNode() {
@@ -3109,13 +3139,13 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
enterBaseNode();
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
enterBaseNode();
final Node index = node.getIndex();
@@ -3131,14 +3161,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dup(1);
}
- return null;
+ return false;
}
});
}
private Symbol quickSymbol(final Type type) {
- return quickSymbol(type, QUICK_PREFIX.tag());
+ return quickSymbol(type, QUICK_PREFIX.symbolName());
}
/**
@@ -3151,22 +3181,28 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @return the quick symbol
*/
private Symbol quickSymbol(final Type type, final String prefix) {
- final String name = getCurrentFunctionNode().uniqueName(prefix);
- final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null);
+ final String name = getLexicalContext().getCurrentFunction().uniqueName(prefix);
+ final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL);
symbol.setType(type);
- symbol.setSlot(getCurrentBlock().getFrame().getSlotCount());
+ final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
+ nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
+ symbol.setSlot(quickSlot);
return symbol;
}
// store the result that "lives on" after the op, e.g. "i" in i++ postfix.
protected void storeNonDiscard() {
- if (assignNode.shouldDiscard() || alwaysDiscard) {
- assignNode.setDiscard(false);
+ if (discard.peek() == assignNode) {
+ assert assignNode.isAssignment();
+ discard.pop();
return;
}
+ //System.err.println("Store with out discard that shouldn't just return " + assignNode);
+ //new Throwable().printStackTrace();
+
final Symbol symbol = assignNode.getSymbol();
if (symbol.hasSlot()) {
method.dup().store(symbol);
@@ -3191,22 +3227,22 @@ final class CodeGenerator extends NodeOperatorVisitor {
*/
method.convert(target.getType());
- target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ target.accept(new NodeVisitor() {
@Override
- protected Node enterDefault(Node node) {
+ protected boolean enterDefault(Node node) {
throw new AssertionError("Unexpected node " + node + " in store epilogue");
}
@Override
- public Node enterUnaryNode(final UnaryNode node) {
- if(node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
+ public boolean enterUnaryNode(final UnaryNode node) {
+ if (node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
method.convert(node.rhs().getType());
}
- return node;
+ return true;
}
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
final Symbol symbol = node.getSymbol();
assert symbol != null;
if (symbol.isScope()) {
@@ -3218,20 +3254,20 @@ final class CodeGenerator extends NodeOperatorVisitor {
} else {
method.store(symbol);
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
method.dynamicSetIndex(getCallSiteFlags());
- return null;
+ return false;
}
});
@@ -3250,10 +3286,23 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.load(quick);
}
}
-
}
- private void newFunctionObject(final FunctionNode functionNode) {
+ private void newFunctionObject(final FunctionNode functionNode, final FunctionNode originalFunctionNode) {
+ final LexicalContext lc = getLexicalContext();
+ assert lc.peek() == functionNode;
+ // We don't emit a ScriptFunction on stack for:
+ // 1. the outermost compiled function (as there's no code being generated in its outer context that'd need it
+ // as a callee), and
+ // 2. for functions that are immediately called upon definition and they don't need a callee, e.g. (function(){})().
+ // Such immediately-called functions are invoked using INVOKESTATIC (see enterFunctionNode() of the embedded
+ // visitor of enterCallNode() for details), and if they don't need a callee, they don't have it on their
+ // static method's parameter list.
+ if(lc.getOutermostFunction() == functionNode ||
+ (!functionNode.needsCallee()) && lc.isFunctionDefinedInCurrentCall(originalFunctionNode)) {
+ return;
+ }
+
final boolean isLazy = functionNode.isLazy();
new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
@@ -3265,7 +3314,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
if (isLazy || functionNode.needsParentScope()) {
- m.loadScope();
+ m.loadCompilerConstant(SCOPE);
} else {
m.loadNull();
}
diff --git a/src/jdk/nashorn/internal/codegen/CompilationPhase.java b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
index 8b905f87..884a68bf 100644
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
@@ -15,6 +15,7 @@ import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
@@ -29,8 +30,8 @@ import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.Timing;
/**
- * A compilation phase is a step in the processes of turning a JavaScript FunctionNode
- * into bytecode. It has an optional return value.
+ * A compilation phase is a step in the processes of turning a JavaScript
+ * FunctionNode into bytecode. It has an optional return value.
*/
enum CompilationPhase {
@@ -41,77 +42,87 @@ enum CompilationPhase {
*/
LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn0) {
/*
- * For lazy compilation, we might be given a node previously marked as lazy
- * to compile as the outermost function node in the compiler. Unmark it
- * so it can be compiled and not cause recursion. Make sure the return type
- * is unknown so it can be correctly deduced. Return types are always
- * Objects in Lazy nodes as we haven't got a change to generate code for
- * them and decude its parameter specialization
+ * For lazy compilation, we might be given a node previously marked
+ * as lazy to compile as the outermost function node in the
+ * compiler. Unmark it so it can be compiled and not cause
+ * recursion. Make sure the return type is unknown so it can be
+ * correctly deduced. Return types are always Objects in Lazy nodes
+ * as we haven't got a change to generate code for them and decude
+ * its parameter specialization
*
- * TODO: in the future specializations from a callsite will be passed here
- * so we can generate a better non-lazy version of a function from a trampoline
+ * TODO: in the future specializations from a callsite will be
+ * passed here so we can generate a better non-lazy version of a
+ * function from a trampoline
*/
- //compute the signature from the callsite - todo - now just clone object params
+
final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
- outermostFunctionNode.setIsLazy(false);
- outermostFunctionNode.setReturnType(Type.UNKNOWN);
+ assert outermostFunctionNode == fn0;
final Set<FunctionNode> neverLazy = new HashSet<>();
- final Set<FunctionNode> lazy = new HashSet<>();
+ final Set<FunctionNode> lazy = new HashSet<>();
+
+ FunctionNode newFunctionNode = outermostFunctionNode;
- outermostFunctionNode.accept(new NodeVisitor() {
- // self references are done with invokestatic and thus cannot have trampolines - never lazy
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() {
+ // self references are done with invokestatic and thus cannot
+ // have trampolines - never lazy
@Override
- public Node enterCallNode(final CallNode node) {
+ public boolean enterCallNode(final CallNode node) {
final Node callee = node.getFunction();
if (callee instanceof FunctionNode) {
neverLazy.add(((FunctionNode)callee));
- return null;
+ return false;
}
- return node;
+ return true;
}
+ //any function that isn't the outermost one must be marked as lazy
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- if (node == outermostFunctionNode) {
- return node;
- }
+ public boolean enterFunctionNode(final FunctionNode node) {
assert compiler.isLazy();
lazy.add(node);
-
- //also needs scope, potentially needs arguments etc etc
-
- return node;
+ return true;
}
});
+ //at least one method is non lazy - the outermost one
+ neverLazy.add(newFunctionNode);
+
for (final FunctionNode node : neverLazy) {
- Compiler.LOG.fine("Marking " + node.getName() + " as non lazy, as it's a self reference");
- node.setIsLazy(false);
+ Compiler.LOG.fine(
+ "Marking ",
+ node.getName(),
+ " as non lazy, as it's a self reference");
lazy.remove(node);
}
- outermostFunctionNode.accept(new NodeOperatorVisitor() {
- private final LexicalContext lexicalContext = new LexicalContext();
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeOperatorVisitor() {
@Override
- public Node enterFunctionNode(FunctionNode functionNode) {
- lexicalContext.push(functionNode);
- if(lazy.contains(functionNode)) {
- Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy");
- functionNode.setIsLazy(true);
- lexicalContext.getParentFunction(functionNode).setHasLazyChildren();
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ final LexicalContext lc = getLexicalContext();
+ if (lazy.contains(functionNode)) {
+ Compiler.LOG.fine(
+ "Marking ",
+ functionNode.getName(),
+ " as lazy");
+ final FunctionNode parent = lc.getParentFunction(functionNode);
+ assert parent != null;
+ lc.setFlag(parent, FunctionNode.HAS_LAZY_CHILDREN);
+ lc.setFlag(parent.getBody(), Block.NEEDS_SCOPE);
+ lc.setFlag(functionNode, FunctionNode.IS_LAZY);
+ return functionNode;
}
- return functionNode;
- }
- @Override
- public Node leaveFunctionNode(FunctionNode functionNode) {
- lexicalContext.pop(functionNode);
- return functionNode;
+
+ return functionNode.
+ clearFlag(lc, FunctionNode.IS_LAZY).
+ setReturnType(lc, Type.UNKNOWN);
}
});
+
+ return newFunctionNode;
}
@Override
@@ -121,13 +132,13 @@ enum CompilationPhase {
},
/*
- * Constant folding pass
- * Simple constant folding that will make elementary constructs go away
+ * Constant folding pass Simple constant folding that will make elementary
+ * constructs go away
*/
CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new FoldConstants());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new FoldConstants());
}
@Override
@@ -137,18 +148,16 @@ enum CompilationPhase {
},
/*
- * Lower (Control flow pass)
- * Finalizes the control flow. Clones blocks for finally constructs and
- * similar things. Establishes termination criteria for nodes
- * Guarantee return instructions to method making sure control flow
- * cannot fall off the end. Replacing high level nodes with lower such
- * as runtime nodes where applicable.
- *
+ * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
+ * finally constructs and similar things. Establishes termination criteria
+ * for nodes Guarantee return instructions to method making sure control
+ * flow cannot fall off the end. Replacing high level nodes with lower such
+ * as runtime nodes where applicable.
*/
LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new Lower());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new Lower());
}
@Override
@@ -158,13 +167,27 @@ enum CompilationPhase {
},
/*
- * Attribution
- * Assign symbols and types to all nodes.
+ * Attribution Assign symbols and types to all nodes.
*/
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new Attr());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)initReturnTypes(fn).accept(new Attr());
+ }
+
+ /**
+ * Pessimistically set all lazy functions' return types to Object
+ * @param functionNode node where to start iterating
+ */
+ private FunctionNode initReturnTypes(final FunctionNode functionNode) {
+ return (FunctionNode)functionNode.accept(new NodeVisitor() {
+ @Override
+ public Node leaveFunctionNode(final FunctionNode node) {
+ return node.isLazy() ?
+ node.setReturnType(getLexicalContext(), Type.OBJECT) :
+ node.setReturnType(getLexicalContext(), Type.UNKNOWN);
+ }
+ });
}
@Override
@@ -174,25 +197,35 @@ enum CompilationPhase {
},
/*
- * Splitter
- * Split the AST into several compile units based on a size heuristic
- * Splitter needs attributed AST for weight calculations (e.g. is
- * a + b a ScriptRuntime.ADD with call overhead or a dadd with much
- * less). Split IR can lead to scope information being changed.
+ * Splitter Split the AST into several compile units based on a size
+ * heuristic Splitter needs attributed AST for weight calculations (e.g. is
+ * a + b a ScriptRuntime.ADD with call overhead or a dadd with much less).
+ * Split IR can lead to scope information being changed.
*/
SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
- new Splitter(compiler, fn, outermostCompileUnit).split();
+ final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
- assert fn.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + fn.getCompileUnit() + ") != " + outermostCompileUnit;
+ assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
- if (fn.isStrictMode()) {
+ if (newFunctionNode.isStrict()) {
assert compiler.getStrictMode();
compiler.setStrictMode(true);
}
+
+ /*
+ newFunctionNode.accept(new NodeVisitor() {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ assert functionNode.getCompileUnit() != null : functionNode.getName() + " " + Debug.id(functionNode) + " has no compile unit";
+ return true;
+ }
+ });*/
+
+ return newFunctionNode;
}
@Override
@@ -204,30 +237,32 @@ enum CompilationPhase {
/*
* FinalizeTypes
*
- * This pass finalizes the types for nodes. If Attr created wider types than
- * known during the first pass, convert nodes are inserted or access nodes
- * are specialized where scope accesses.
+ * This pass finalizes the types for nodes. If Attr created wider types than
+ * known during the first pass, convert nodes are inserted or access nodes
+ * are specialized where scope accesses.
*
- * Runtime nodes may be removed and primitivized or reintroduced depending
- * on information that was established in Attr.
+ * Runtime nodes may be removed and primitivized or reintroduced depending
+ * on information that was established in Attr.
*
* Contract: all variables must have slot assignments and scope assignments
* before type finalization.
*/
TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
- fn.accept(new FinalizeTypes());
+ final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
if (env._print_lower_ast) {
- env.getErr().println(new ASTWriter(fn));
+ env.getErr().println(new ASTWriter(newFunctionNode));
}
if (env._print_lower_parse) {
- env.getErr().println(new PrintVisitor(fn));
- }
+ env.getErr().println(new PrintVisitor(newFunctionNode));
+ }
+
+ return newFunctionNode;
}
@Override
@@ -239,31 +274,21 @@ enum CompilationPhase {
/*
* Bytecode generation:
*
- * Generate the byte code class(es) resulting from the compiled FunctionNode
+ * Generate the byte code class(es) resulting from the compiled FunctionNode
*/
BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
+ FunctionNode newFunctionNode = fn;
try {
final CodeGenerator codegen = new CodeGenerator(compiler);
- fn.accept(codegen);
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
codegen.generateScopeCalls();
- fn.accept(new NodeOperatorVisitor() {
- @Override
- public Node enterFunctionNode(FunctionNode functionNode) {
- if(functionNode.isLazy()) {
- functionNode.resetResolved();
- return null;
- }
- return fn;
- }
- });
-
} catch (final VerifyError e) {
if (env._verify_code || env._print_code) {
- env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
+ env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
if (env._dump_on_error) {
e.printStackTrace(env.getErr());
}
@@ -283,25 +308,25 @@ enum CompilationPhase {
compiler.addClass(className, bytecode);
- //should could be printed to stderr for generate class?
+ // should could be printed to stderr for generate class?
if (env._print_code) {
final StringBuilder sb = new StringBuilder();
- sb.append("class: " + className).
- append('\n').
- append(ClassEmitter.disassemble(bytecode)).
- append("=====");
+ sb.append("class: " + className).append('\n')
+ .append(ClassEmitter.disassemble(bytecode))
+ .append("=====");
env.getErr().println(sb);
}
- //should we verify the generated code?
+ // should we verify the generated code?
if (env._verify_code) {
compiler.getCodeInstaller().verify(bytecode);
}
- //should code be dumped to disk - only valid in compile_only mode?
+ // should code be dumped to disk - only valid in compile_only
+ // mode?
if (env._dest_dir != null && env._compile_only) {
final String fileName = className.replace('.', File.separatorChar) + ".class";
- final int index = fileName.lastIndexOf(File.separatorChar);
+ final int index = fileName.lastIndexOf(File.separatorChar);
if (index != -1) {
final File dir = new File(fileName.substring(0, index));
@@ -314,11 +339,18 @@ enum CompilationPhase {
fos.write(bytecode);
}
} catch (final IOException e) {
- Compiler.LOG.warning("Skipping class dump for " + className + ": " + ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
+ Compiler.LOG.warning("Skipping class dump for ",
+ className,
+ ": ",
+ ECMAErrors.getMessage(
+ "io.error.cant.write",
+ dir.toString()));
}
}
}
}
+
+ return newFunctionNode;
}
@Override
@@ -340,26 +372,28 @@ enum CompilationPhase {
return functionNode.hasState(pre);
}
- protected void begin(final FunctionNode functionNode) {
+ protected FunctionNode begin(final FunctionNode functionNode) {
if (pre != null) {
- //check that everything in pre is present
+ // check that everything in pre is present
for (final CompilationState state : pre) {
assert functionNode.hasState(state);
}
- //check that nothing else is present
+ // check that nothing else is present
for (final CompilationState state : CompilationState.values()) {
assert !(functionNode.hasState(state) && !pre.contains(state));
}
}
startTime = System.currentTimeMillis();
+ return functionNode;
}
- protected void end(final FunctionNode functionNode) {
+ protected FunctionNode end(final FunctionNode functionNode) {
endTime = System.currentTimeMillis();
Timing.accumulateTime(toString(), endTime - startTime);
isFinished = true;
+ return functionNode;
}
boolean isFinished() {
@@ -374,15 +408,13 @@ enum CompilationPhase {
return endTime;
}
- abstract void transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
+ abstract FunctionNode transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
- final void apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
+ final FunctionNode apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
if (!isApplicable(functionNode)) {
- throw new CompilationException("compile phase not applicable: " + this);
+ throw new CompilationException("compile phase not applicable: " + this + " to " + functionNode.getName() + " state=" + functionNode.getState());
}
- begin(functionNode);
- transform(compiler, functionNode);
- end(functionNode);
+ return end(transform(compiler, begin(functionNode)));
}
}
diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java
index 397f39a5..b1e47d02 100644
--- a/src/jdk/nashorn/internal/codegen/Compiler.java
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java
@@ -25,12 +25,16 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import java.io.File;
import java.lang.reflect.Field;
@@ -46,13 +50,12 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
+
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
-import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
@@ -80,8 +83,6 @@ public final class Compiler {
private final ConstantData constantData;
- private final FunctionNode functionNode;
-
private final CompilationSequence sequence;
private final ScriptEnvironment env;
@@ -90,6 +91,8 @@ public final class Compiler {
private boolean strict;
+ private FunctionNode functionNode;
+
private CodeInstaller<ScriptEnvironment> installer;
/** logger for compiler, trampolines, splits and related code generation events
@@ -103,8 +106,12 @@ public final class Compiler {
* during a compile.
*/
private static String[] RESERVED_NAMES = {
- SCOPE.tag(),
- THIS.tag()
+ SCOPE.symbolName(),
+ THIS.symbolName(),
+ RETURN.symbolName(),
+ CALLEE.symbolName(),
+ VARARGS.symbolName(),
+ ARGUMENTS.symbolName()
};
/**
@@ -186,7 +193,7 @@ public final class Compiler {
private static String lazyTag(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return '$' + LAZY.tag() + '$' + functionNode.getName();
+ return '$' + LAZY.symbolName() + '$' + functionNode.getName();
}
return "";
}
@@ -205,13 +212,13 @@ public final class Compiler {
this.functionNode = functionNode;
this.sequence = sequence;
this.installer = installer;
- this.strict = strict || functionNode.isStrictMode();
+ this.strict = strict || functionNode.isStrict();
this.constantData = new ConstantData();
this.compileUnits = new HashSet<>();
this.bytecode = new HashMap<>();
final StringBuilder sb = new StringBuilder();
- sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag() + lazyTag(functionNode))).
+ sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
append('$').
append(safeSourceName(functionNode.getSource()));
@@ -253,9 +260,9 @@ public final class Compiler {
* Execute the compilation this Compiler was created with
* @params param types if known, for specialization
* @throws CompilationException if something goes wrong
- * @return this compiler, for possible chaining
+ * @return function node that results from code transforms
*/
- public Compiler compile() throws CompilationException {
+ public FunctionNode compile() throws CompilationException {
return compile(null);
}
@@ -263,9 +270,9 @@ public final class Compiler {
* Execute the compilation this Compiler was created with
* @param paramTypes param types if known, for specialization
* @throws CompilationException if something goes wrong
- * @return this compiler, for possible chaining
+ * @return function node that results from code transforms
*/
- public Compiler compile(final Class<?> paramTypes) throws CompilationException {
+ public FunctionNode compile(final Class<?> paramTypes) throws CompilationException {
for (final String reservedName : RESERVED_NAMES) {
functionNode.uniqueName(reservedName);
}
@@ -276,7 +283,7 @@ public final class Compiler {
long time = 0L;
for (final CompilationPhase phase : sequence) {
- phase.apply(this, functionNode);
+ this.functionNode = phase.apply(this, functionNode);
final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
time += duration;
@@ -295,7 +302,7 @@ public final class Compiler {
append(" ms ");
}
- LOG.fine(sb.toString());
+ LOG.fine(sb);
}
}
@@ -311,14 +318,14 @@ public final class Compiler {
append(" ms");
}
- LOG.info(sb.toString());
+ LOG.info(sb);
}
- return this;
+ return functionNode;
}
private Class<?> install(final String className, final byte[] code) {
- LOG.fine("Installing class " + className);
+ LOG.fine("Installing class ", className);
final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
@@ -330,8 +337,8 @@ public final class Compiler {
@Override
public Void run() throws Exception {
//use reflection to write source and constants table to installed classes
- final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
- final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
+ final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
+ final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
sourceField.setAccessible(true);
constantsField.setAccessible(true);
sourceField.set(null, source);
@@ -380,17 +387,6 @@ public final class Compiler {
unit.setCode(installedClasses.get(unit.getUnitClassName()));
}
- functionNode.accept(new NodeVisitor() {
- @Override
- public Node enterFunctionNode(final FunctionNode node) {
- if (node.isLazy()) {
- return null;
- }
- node.setState(CompilationState.INSTALLED);
- return node;
- }
- });
-
final StringBuilder sb;
if (LOG.isEnabled()) {
sb = new StringBuilder();
@@ -416,7 +412,7 @@ public final class Compiler {
}
if (sb != null) {
- LOG.info(sb.toString());
+ LOG.info(sb);
}
return rootClass;
@@ -495,7 +491,7 @@ public final class Compiler {
private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
compileUnits.add(compileUnit);
- LOG.fine("Added compile unit " + compileUnit);
+ LOG.fine("Added compile unit ", compileUnit);
return compileUnit;
}
diff --git a/src/jdk/nashorn/internal/codegen/CompilerConstants.java b/src/jdk/nashorn/internal/codegen/CompilerConstants.java
index 1d978d96..dfc9198c 100644
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java
+++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java
@@ -52,9 +52,6 @@ public enum CompilerConstants {
/** lazy prefix for classes of jitted methods */
LAZY("Lazy"),
- /** leaf tag used for functions that require no scope */
- LEAF("__leaf__"),
-
/** constructor name */
INIT("<init>"),
@@ -90,55 +87,55 @@ public enum CompilerConstants {
THIS("this"),
/** this debugger symbol */
- THIS_DEBUGGER("__this__"),
+ THIS_DEBUGGER(":this"),
/** scope name, type and slot */
- SCOPE("__scope__", ScriptObject.class, 2),
+ SCOPE(":scope", ScriptObject.class, 2),
/** the return value variable name were intermediate results are stored for scripts */
- SCRIPT_RETURN("__return__"),
+ RETURN(":return"),
/** the callee value variable when necessary */
- CALLEE("__callee__", ScriptFunction.class),
+ CALLEE(":callee", ScriptFunction.class),
/** the varargs variable when necessary */
- VARARGS("__varargs__"),
+ VARARGS(":varargs"),
/** the arguments vector when necessary and the slot */
ARGUMENTS("arguments", Object.class, 2),
/** prefix for iterators for for (x in ...) */
- ITERATOR_PREFIX("$iter"),
+ ITERATOR_PREFIX(":iter"),
/** prefix for tag variable used for switch evaluation */
- SWITCH_TAG_PREFIX("$tag"),
+ SWITCH_TAG_PREFIX(":tag"),
/** prefix for all exceptions */
- EXCEPTION_PREFIX("$exception"),
+ EXCEPTION_PREFIX(":exception"),
/** prefix for quick slots generated in Store */
- QUICK_PREFIX("$quick"),
+ QUICK_PREFIX(":quick"),
/** prefix for temporary variables */
- TEMP_PREFIX("$temp"),
+ TEMP_PREFIX(":temp"),
/** prefix for literals */
- LITERAL_PREFIX("$lit"),
-
- /** prefix for map */
- MAP("$map", 1),
+ LITERAL_PREFIX(":lit"),
/** prefix for regexps */
- REGEX_PREFIX("$regex"),
+ REGEX_PREFIX(":regex"),
/** "this" used in non-static Java methods; always in slot 0 */
- JAVA_THIS("this", 0),
+ JAVA_THIS(null, 0),
+
+ /** Map parameter in scope object constructors; always in slot 1 */
+ INIT_MAP(null, 1),
- /** init scope */
- INIT_SCOPE("$scope", 2),
+ /** Parent scope parameter in scope object constructors; always in slot 2 */
+ INIT_SCOPE(null, 2),
- /** init arguments */
- INIT_ARGUMENTS("$arguments", 3),
+ /** Arguments parameter in scope object constructors; in slot 3 when present */
+ INIT_ARGUMENTS(null, 3),
/** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */
JS_OBJECT_PREFIX("JO"),
@@ -167,30 +164,30 @@ public enum CompilerConstants {
/** get array suffix */
GET_ARRAY_SUFFIX("$array");
- private final String tag;
+ private final String symbolName;
private final Class<?> type;
private final int slot;
private CompilerConstants() {
- this.tag = name();
+ this.symbolName = name();
this.type = null;
this.slot = -1;
}
- private CompilerConstants(final String tag) {
- this(tag, -1);
+ private CompilerConstants(final String symbolName) {
+ this(symbolName, -1);
}
- private CompilerConstants(final String tag, final int slot) {
- this(tag, null, slot);
+ private CompilerConstants(final String symbolName, final int slot) {
+ this(symbolName, null, slot);
}
- private CompilerConstants(final String tag, final Class<?> type) {
- this(tag, type, -1);
+ private CompilerConstants(final String symbolName, final Class<?> type) {
+ this(symbolName, type, -1);
}
- private CompilerConstants(final String tag, final Class<?> type, final int slot) {
- this.tag = tag;
+ private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
+ this.symbolName = symbolName;
this.type = type;
this.slot = slot;
}
@@ -202,8 +199,8 @@ public enum CompilerConstants {
*
* @return the tag
*/
- public final String tag() {
- return tag;
+ public final String symbolName() {
+ return symbolName;
}
/**
@@ -277,7 +274,7 @@ public enum CompilerConstants {
* @return Call representing void constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz) {
- return specialCallNoLookup(clazz, INIT.tag(), void.class);
+ return specialCallNoLookup(clazz, INIT.symbolName(), void.class);
}
/**
@@ -290,7 +287,7 @@ public enum CompilerConstants {
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final String className, final Class<?>... ptypes) {
- return specialCallNoLookup(className, INIT.tag(), methodDescriptor(void.class, ptypes));
+ return specialCallNoLookup(className, INIT.symbolName(), methodDescriptor(void.class, ptypes));
}
/**
@@ -303,7 +300,7 @@ public enum CompilerConstants {
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz, final Class<?>... ptypes) {
- return specialCallNoLookup(clazz, INIT.tag(), void.class, ptypes);
+ return specialCallNoLookup(clazz, INIT.symbolName(), void.class, ptypes);
}
/**
diff --git a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java
index 83e75cc7..57b8b384 100644
--- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java
+++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
@@ -86,7 +87,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
* @param method the method emitter to use
*/
protected void loadScope(final MethodEmitter method) {
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
}
/**
@@ -105,7 +106,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
loadScope(method);
if (hasArguments()) {
- method.loadArguments();
+ method.loadCompilerConstant(ARGUMENTS);
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
} else {
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));
diff --git a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
index cd185240..5f6a63dc 100644
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
+++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
@@ -25,7 +25,12 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
@@ -33,10 +38,8 @@ import jdk.nashorn.internal.ir.Assignment;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
-import jdk.nashorn.internal.ir.CallNode.EvalArgs;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -85,18 +88,11 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private static final DebugLogger LOG = new DebugLogger("finalize");
- private final LexicalContext lexicalContext = new LexicalContext();
-
FinalizeTypes() {
}
@Override
public Node leaveCallNode(final CallNode callNode) {
- final EvalArgs evalArgs = callNode.getEvalArgs();
- if (evalArgs != null) {
- evalArgs.setCode(evalArgs.getCode().accept(this));
- }
-
// AccessSpecializer - call return type may change the access for this location
final Node function = callNode.getFunction();
if (function instanceof FunctionNode) {
@@ -133,8 +129,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
- ((CallNode)unaryNode.rhs()).setIsNew();
- return unaryNode;
+ return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew());
}
@Override
@@ -254,7 +249,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null;
- final BinaryNode newBinaryNode = (BinaryNode)binaryNode.setRHS(discard(binaryNode.rhs()));
+ final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs()));
// AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
// in that case, update the node type as well
propagateType(newBinaryNode, newBinaryNode.lhs().getType());
@@ -354,41 +349,30 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
@Override
- public Node enterBlock(final Block block) {
- lexicalContext.push(block);
+ public boolean enterBlock(final Block block) {
updateSymbols(block);
- return block;
+ return true;
}
+ /*
@Override
- public Node leaveBlock(Block block) {
- lexicalContext.pop(block);
- return super.leaveBlock(block);
- }
+ public Node leaveBlock(final Block block) {
+ final LexicalContext lc = getLexicalContext();
+ return block;//.setFlag(lc, lc.getFlags(block));
+ }*/
@Override
public Node leaveCatchNode(final CatchNode catchNode) {
final Node exceptionCondition = catchNode.getExceptionCondition();
if (exceptionCondition != null) {
- catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
+ return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
}
return catchNode;
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
- }
-
- @Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- return leaveWhileNode(doWhileNode);
- }
-
- @Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
- executeNode.setExpression(discard(executeNode.getExpression()));
- return executeNode;
+ return executeNode.setExpression(discard(executeNode.getExpression()));
}
@Override
@@ -397,69 +381,54 @@ final class FinalizeTypes extends NodeOperatorVisitor {
final Node test = forNode.getTest();
final Node modify = forNode.getModify();
- if (forNode.isForIn()) {
- forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
- return forNode;
- }
+ final LexicalContext lc = getLexicalContext();
- if (init != null) {
- forNode.setInit(discard(init));
- }
-
- if (test != null) {
- forNode.setTest(convert(test, Type.BOOLEAN));
- } else {
- assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode();
- }
-
- if (modify != null) {
- forNode.setModify(discard(modify));
+ if (forNode.isForIn()) {
+ return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
}
+ assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction();
- return forNode;
+ return forNode.
+ setInit(lc, init == null ? null : discard(init)).
+ setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)).
+ setModify(lc, modify == null ? null : discard(modify));
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return null;
+ return false;
}
- lexicalContext.push(functionNode);
// If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
// this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
// need for the callee.
if (!functionNode.needsCallee()) {
- functionNode.getCalleeNode().getSymbol().setNeedsSlot(false);
+ functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
}
// Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its
// own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
// this phase.
- if (!(functionNode.needsScope() || functionNode.needsParentScope())) {
- functionNode.getScopeNode().getSymbol().setNeedsSlot(false);
+ if (!(functionNode.getBody().needsScope() || functionNode.needsParentScope())) {
+ functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
- updateSymbols(functionNode);
- functionNode.setState(CompilationState.FINALIZED);
-
- return functionNode;
+ return true;
}
@Override
- public Node leaveFunctionNode(FunctionNode functionNode) {
- lexicalContext.pop(functionNode);
- return super.leaveFunctionNode(functionNode);
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED);
}
@Override
public Node leaveIfNode(final IfNode ifNode) {
- ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
- return ifNode;
+ return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
}
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
if (literalNode instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
final Node[] array = arrayLiteralNode.getValue();
@@ -473,14 +442,14 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
}
- return null;
+ return false;
}
@Override
public Node leaveReturnNode(final ReturnNode returnNode) {
final Node expr = returnNode.getExpression();
if (expr != null) {
- returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
+ return returnNode.setExpression(convert(expr, getLexicalContext().getCurrentFunction().getReturnType()));
}
return returnNode;
}
@@ -496,21 +465,24 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
+ final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
+
+ if (allInteger) {
+ return switchNode;
+ }
+
final Node expression = switchNode.getExpression();
final List<CaseNode> cases = switchNode.getCases();
- final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
-
- if (!allInteger) {
- switchNode.setExpression(convert(expression, Type.OBJECT));
- for (final CaseNode caseNode : cases) {
- final Node test = caseNode.getTest();
- if (test != null) {
- caseNode.setTest(convert(test, Type.OBJECT));
- }
- }
+ final List<CaseNode> newCases = new ArrayList<>();
+
+ for (final CaseNode caseNode : cases) {
+ final Node test = caseNode.getTest();
+ newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode);
}
- return switchNode;
+ return switchNode.
+ setExpression(getLexicalContext(), convert(expression, Type.OBJECT)).
+ setCases(getLexicalContext(), newCases);
}
@Override
@@ -520,8 +492,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveThrowNode(final ThrowNode throwNode) {
- throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
- return throwNode;
+ return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
}
@Override
@@ -544,23 +515,24 @@ final class FinalizeTypes extends NodeOperatorVisitor {
public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
if (test != null) {
- whileNode.setTest(convert(test, Type.BOOLEAN));
+ return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN));
}
return whileNode;
}
@Override
public Node leaveWithNode(final WithNode withNode) {
- withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
- return withNode;
+ return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT));
}
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
- if (!symbol.isScope()) {
- LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
- }
- if (loseSlot && symbol.hasSlot()) {
- LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
+ if (LOG.isEnabled()) {
+ if (!symbol.isScope()) {
+ LOG.finest("updateSymbols: ", symbol, " => scope, because all vars in ", functionNode.getName(), " are in scope");
+ }
+ if (loseSlot && symbol.hasSlot()) {
+ LOG.finest("updateSymbols: ", symbol, " => no slot, because all vars in ", functionNode.getName(), " are in scope");
+ }
}
}
@@ -574,29 +546,28 @@ final class FinalizeTypes extends NodeOperatorVisitor {
return; // nothing to do
}
- final FunctionNode functionNode = lexicalContext.getFunction(block);
- assert !(block instanceof FunctionNode) || functionNode == block;
+ final LexicalContext lc = getLexicalContext();
+ final FunctionNode functionNode = lc.getFunction(block);
+ final boolean allVarsInScope = functionNode.allVarsInScope();
+ final boolean isVarArg = functionNode.isVarArg();
- final List<Symbol> symbols = block.getFrame().getSymbols();
- final boolean allVarsInScope = functionNode.allVarsInScope();
- final boolean isVarArg = functionNode.isVarArg();
-
- for (final Symbol symbol : symbols) {
- if (symbol.isInternal() || symbol.isThis()) {
+ for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol symbol = iter.next();
+ if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
}
if (symbol.isVar()) {
if (allVarsInScope || symbol.isScope()) {
updateSymbolsLog(functionNode, symbol, true);
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(false);
} else {
assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
}
} else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
updateSymbolsLog(functionNode, symbol, isVarArg);
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(!isVarArg);
}
}
@@ -636,11 +607,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
//fallthru
default:
if (newRuntimeNode || widest.isObject()) {
- final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request);
- if (finalized) {
- runtimeNode.setIsFinal();
- }
- return runtimeNode;
+ return new RuntimeNode(binaryNode, request).setIsFinal(finalized);
}
break;
}
@@ -667,7 +634,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
- return binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
+ Node b = binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
+ return b;
}
/**
@@ -683,28 +651,28 @@ final class FinalizeTypes extends NodeOperatorVisitor {
node.accept(new NodeVisitor() {
private void setCanBePrimitive(final Symbol symbol) {
- LOG.info("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
+ LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol));
symbol.setCanBePrimitive(to);
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
if (!exclude.contains(identNode)) {
setCanBePrimitive(identNode.getSymbol());
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
setCanBePrimitive(accessNode.getProperty().getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
- return indexNode;
+ return true;
}
});
}
@@ -785,12 +753,12 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private static <T extends Node> T setTypeOverride(final T node, final Type to) {
final Type from = node.getType();
if (!node.getType().equals(to)) {
- LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
+ LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to);
if (!to.isObject() && from.isObject()) {
setCanBePrimitive(node, to);
}
}
- LOG.info("Type override for lhs in '" + node + "' => " + to);
+ LOG.info("Type override for lhs in '", node, "' => ", to);
return ((TypeOverride<T>)node).setType(to);
}
@@ -814,8 +782,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private Node convert(final Node node, final Type to) {
assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
assert node != null : "node is null";
- assert node.getSymbol() != null : "node " + node + " has no symbol!";
- assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getCurrentFunctionNode();
+ assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction() + " " + node.getSource();
+ assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction();
final Type from = node.getType();
@@ -842,23 +810,23 @@ final class FinalizeTypes extends NodeOperatorVisitor {
resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
}
- LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
+ LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'");
+ final LexicalContext lc = getLexicalContext();
//This is the only place in this file that can create new temporaries
//FinalizeTypes may not introduce ANY node that is not a conversion.
- getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode);
- resultNode.copyTerminalFlags(node);
+ lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode);
+
+ assert !node.isTerminal();
return resultNode;
}
private static Node discard(final Node node) {
- node.setDiscard(true);
-
if (node.getSymbol() != null) {
final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node);
//discard never has a symbol in the discard node - then it would be a nop
- discard.copyTerminalFlags(node);
+ assert !node.isTerminal();
return discard;
}
@@ -883,7 +851,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
final Symbol symbol = node.getSymbol();
if (symbol.isTemp()) {
symbol.setTypeOverride(to);
- LOG.info("Type override for temporary in '" + node + "' => " + to);
+ LOG.info("Type override for temporary in '", node, "' => ", to);
}
}
diff --git a/src/jdk/nashorn/internal/codegen/FoldConstants.java b/src/jdk/nashorn/internal/codegen/FoldConstants.java
index fbc62644..03accb4b 100644
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java
+++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java
@@ -57,7 +57,7 @@ final class FoldConstants extends NodeVisitor {
public Node leaveUnaryNode(final UnaryNode unaryNode) {
final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
if (literalNode != null) {
- LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
+ LOG.info("Unary constant folded ", unaryNode, " to ", literalNode);
return literalNode;
}
return unaryNode;
@@ -67,24 +67,20 @@ final class FoldConstants extends NodeVisitor {
public Node leaveBinaryNode(final BinaryNode binaryNode) {
final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
if (literalNode != null) {
- LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
+ LOG.info("Binary constant folded ", binaryNode, " to ", literalNode);
return literalNode;
}
return binaryNode;
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- if (functionNode.isLazy()) {
- return null;
- }
- return functionNode;
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ return !functionNode.isLazy();
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
- functionNode.setState(CompilationState.CONSTANT_FOLDED);
- return functionNode;
+ return functionNode.setState(getLexicalContext(), CompilationState.CONSTANT_FOLDED);
}
@Override
@@ -251,7 +247,7 @@ final class FoldConstants extends NodeVisitor {
value = lhs.getNumber() - rhs.getNumber();
break;
case SHR:
- return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL);
+ return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & JSType.MAX_UINT);
case SAR:
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32());
case SHL:
diff --git a/src/jdk/nashorn/internal/codegen/Frame.java b/src/jdk/nashorn/internal/codegen/Frame.java
deleted file mode 100644
index b72456b2..00000000
--- a/src/jdk/nashorn/internal/codegen/Frame.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.nashorn.internal.codegen;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import jdk.nashorn.internal.ir.Symbol;
-
-/**
- * Tracks the variable area state.
- *
- */
-public final class Frame {
- /** Previous frame. */
- private Frame previous;
-
- /** Current variables. */
- private final ArrayList<Symbol> symbols;
-
- /** Number of slots in previous frame. */
- private int baseCount;
-
- /** Number of slots in this frame. */
- private int count;
-
- /**
- * Constructor.
- *
- * @param previous frame, the parent variable frame
- */
- public Frame(final Frame previous) {
- this.previous = previous;
- this.symbols = new ArrayList<>();
- this.baseCount = getBaseCount();
- this.count = 0;
- }
-
- /**
- * Copy constructor
- * @param frame
- * @param symbols
- */
- private Frame(final Frame frame, final List<Symbol> symbols) {
- this.previous = frame.getPrevious() == null ? null : new Frame(frame.getPrevious(), frame.getPrevious().getSymbols());
- this.symbols = new ArrayList<>(frame.getSymbols());
- this.baseCount = frame.getBaseCount();
- this.count = frame.getCount();
- }
-
- /**
- * Copy the frame
- *
- * @return a new frame with the identical contents
- */
- public Frame copy() {
- return new Frame(this, getSymbols());
- }
-
- /**
- * Add a new variable to the frame.
- * @param symbol Symbol representing variable.
- */
- public void addSymbol(final Symbol symbol) {
- final int slot = symbol.getSlot();
- if (slot < 0) {
- symbols.add(symbol);
- count += symbol.slotCount();
- }
- }
-
- /**
- * Realign slot numbering prior to code generation.
- * @return Number of slots in frame.
- */
- public int realign() {
- baseCount = getBaseCount();
- count = 0;
-
- for (final Symbol symbol : symbols) {
- if (symbol.hasSlot()) {
- symbol.setSlot(baseCount + count);
- count += symbol.slotCount();
- }
- }
-
- return count;
- }
-
- /**
- * Return the slot count of previous frames.
- * @return Number of slots in previous frames.
- */
- private int getBaseCount() {
- return previous != null ? previous.getSlotCount() : 0;
- }
-
- /**
- * Determine the number of slots to top of frame.
- * @return Number of slots in total.
- */
- public int getSlotCount() {
- return baseCount + count;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- Frame f = this;
- boolean hasPrev = false;
- int pos = 0;
-
- do {
- if (hasPrev) {
- sb.append("\n");
- }
-
- sb.append("#").
- append(pos++).
- append(" {baseCount:").
- append(baseCount).
- append(", ").
- append("count:").
- append(count).
- append("} ");
-
- for (final Symbol var : f.getSymbols()) {
- sb.append('[').
- append(var.toString()).
- append(' ').
- append(var.hashCode()).
- append("] ");
- }
-
- f = f.getPrevious();
- hasPrev = true;
- } while (f != null);
-
- return sb.toString();
- }
-
- /**
- * Get variable count for this frame
- * @return variable count
- */
- public int getCount() {
- return count;
- }
-
- /**
- * Get previous frame
- * @return previous frame
- */
- public Frame getPrevious() {
- return previous;
- }
-
- /**
- * Set previous frame
- * @param previous previous frame
- */
- public void setPrevious(final Frame previous) {
- this.previous = previous;
- }
-
- /**
- * Get symbols in frame
- * @return a list of symbols in this frame
- */
- public List<Symbol> getSymbols() {
- return Collections.unmodifiableList(symbols);
- }
- }
diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java
index 715ddc6f..d6209e4f 100644
--- a/src/jdk/nashorn/internal/codegen/Lower.java
+++ b/src/jdk/nashorn/internal/codegen/Lower.java
@@ -25,29 +25,21 @@
package jdk.nashorn.internal.codegen;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
-import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
-import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Deque;
-import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.BlockLexicalContext;
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.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -56,10 +48,10 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.LabelNode;
-import jdk.nashorn.internal.ir.LabeledNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LineNumberNode;
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.SwitchNode;
@@ -90,599 +82,415 @@ import jdk.nashorn.internal.runtime.Source;
final class Lower extends NodeOperatorVisitor {
- /**
- * Nesting level stack. Currently just used for loops to avoid the problem
- * with terminal bodies that end with throw/return but still do continues to
- * outer loops or same loop.
- */
- private final Deque<Node> nesting;
-
private static final DebugLogger LOG = new DebugLogger("lower");
- private Node lastStatement;
-
- private List<Node> statements;
-
- private LexicalContext lexicalContext = new LexicalContext();
-
/**
* Constructor.
*
* @param compiler the compiler
*/
Lower() {
- this.nesting = new ArrayDeque<>();
- this.statements = new ArrayList<>();
- }
+ super(new BlockLexicalContext() {
- @Override
- public Node enterBlock(final Block block) {
- final Node savedLastStatement = lastStatement;
- final List<Node> savedStatements = statements;
- lexicalContext.push(block);
- try {
- this.statements = new ArrayList<>();
- NodeVisitor visitor = this;
- for (final Node statement : block.getStatements()) {
- statement.accept(visitor);
- /*
- * This is slightly unsound, for example if we have a loop with
- * a guarded statement like if (x) continue in the body and the
- * body ends with TERMINAL, e.g. return; we removed the continue
- * before we had the loop stack, as all we cared about was a
- * return last in the loop.
- *
- * @see NASHORN-285
- */
- if (lastStatement != null && lastStatement.isTerminal()) {
- copyTerminal(block, lastStatement);
- visitor = new DeadCodeVarDeclarationVisitor();
+ @Override
+ public List<Node> popStatements() {
+ List<Node> newStatements = new ArrayList<>();
+ boolean terminated = false;
+
+ final List<Node> statements = super.popStatements();
+ for (final Node statement : statements) {
+ if (!terminated) {
+ newStatements.add(statement);
+ if (statement.isTerminal()) {
+ terminated = true;
+ }
+ } else {
+ if (statement instanceof VarNode) {
+ newStatements.add(((VarNode)statement).setInit(null));
+ }
+ }
}
+ return newStatements;
}
- block.setStatements(statements);
-
- } finally {
- this.statements = savedStatements;
- this.lastStatement = savedLastStatement;
- lexicalContext.pop(block);
- }
-
- return null;
+ });
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
- return enterBreakOrContinue(breakNode);
+ public boolean enterBlock(final Block block) {
+ final LexicalContext lc = getLexicalContext();
+ if (lc.isFunctionBody() && lc.getCurrentFunction().isProgram() && !lc.getCurrentFunction().hasDeclaredFunctions()) {
+ new ExecuteNode(block.getSource(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
+ }
+ return true;
}
@Override
- public Node enterCallNode(final CallNode callNode) {
- final Node function = markerFunction(callNode.getFunction());
- callNode.setFunction(function);
- checkEval(callNode); //check if this is an eval call and store the information
- return callNode;
- }
+ 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
- @Override
- public Node leaveCaseNode(final CaseNode caseNode) {
- caseNode.copyTerminalFlags(caseNode.getBody());
- return caseNode;
+ final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext();
+
+ Node last = lc.getLastStatement();
+
+ if (lc.isFunctionBody()) {
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
+ final boolean isProgram = currentFunction.isProgram();
+ final ReturnNode returnNode = new ReturnNode(
+ currentFunction.getSource(),
+ currentFunction.getToken(),
+ currentFunction.getFinish(),
+ isProgram ?
+ compilerConstant(RETURN) :
+ LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
+
+ last = returnNode.accept(this);
+ }
+
+ if (last != null && last.isTerminal()) {
+ return block.setIsTerminal(lc, true);
+ }
+
+ return block;
}
@Override
- public Node leaveCatchNode(final CatchNode catchNode) {
- catchNode.copyTerminalFlags(catchNode.getBody());
- addStatement(catchNode);
- return catchNode;
+ public boolean enterBreakNode(final BreakNode breakNode) {
+ addStatement(breakNode);
+ return false;
}
@Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- return enterBreakOrContinue(continueNode);
+ public Node leaveCallNode(final CallNode callNode) {
+ return checkEval(callNode.setFunction(markerFunction(callNode.getFunction())));
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
+ public Node leaveCatchNode(final CatchNode catchNode) {
+ return addStatement(catchNode);
}
@Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- return leaveWhileNode(doWhileNode);
+ public boolean enterContinueNode(final ContinueNode continueNode) {
+ addStatement(continueNode);
+ return false;
}
@Override
- public Node enterEmptyNode(final EmptyNode emptyNode) {
- return null;
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
+ return false;
}
@Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
final Node expr = executeNode.getExpression();
+ ExecuteNode node = executeNode;
+
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
- if (getCurrentFunctionNode().isProgram()) {
+ if (currentFunction.isProgram()) {
if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
- executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN),
- getCurrentFunctionNode().getResultNode(),
- expr));
+ node = executeNode.setExpression(
+ new BinaryNode(
+ executeNode.getSource(),
+ Token.recast(
+ executeNode.getToken(),
+ TokenType.ASSIGN),
+ compilerConstant(RETURN),
+ expr));
}
}
}
- copyTerminal(executeNode, executeNode.getExpression());
- addStatement(executeNode);
-
- return executeNode;
- }
-
- @Override
- public Node enterForNode(final ForNode forNode) {
- nest(forNode);
- return forNode;
+ return addStatement(node);
}
@Override
public Node leaveForNode(final ForNode forNode) {
- final Node test = forNode.getTest();
- final Block body = forNode.getBody();
-
- if (!forNode.isForIn() && test == null) {
- setHasGoto(forNode);
- }
-
- final boolean escapes = controlFlowEscapes(body);
- if (escapes) {
- setTerminal(body, false);
- }
-
- // pop the loop from the loop context
- unnest(forNode);
+ ForNode newForNode = forNode;
+ final Node test = forNode.getTest();
if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
- forNode.setTest(null);
- setHasGoto(forNode);
- setTerminal(forNode, !escapes);
+ newForNode = forNode.setTest(getLexicalContext(), null);
}
- addStatement(forNode);
-
- return forNode;
+ return addStatement(checkEscape(newForNode));
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- LOG.info("START FunctionNode: " + functionNode.getName());
-
- if (functionNode.isLazy()) {
- LOG.info("LAZY: " + functionNode.getName());
- return null;
- }
- lexicalContext.push(functionNode);
- initFunctionNode(functionNode);
-
- nest(functionNode);
-
- /*
- * As we are evaluating a nested structure, we need to store the
- * statement list for the surrounding block and restore it when the
- * function is done
- */
- final List<Node> savedStatements = statements;
- final Node savedLastStatement = lastStatement;
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ return !functionNode.isLazy();
+ }
- statements = new ArrayList<>();
- lastStatement = null;
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ LOG.info("END FunctionNode: ", functionNode.getName());
+ return functionNode.setState(getLexicalContext(), CompilationState.LOWERED);
+ }
- if (functionNode.needsSelfSymbol()) {
- //function needs to start with var funcIdent = __callee_;
- statements.add(functionNode.getSelfSymbolInit().accept(this));
- }
+ @Override
+ public Node leaveIfNode(final IfNode ifNode) {
+ return addStatement(ifNode);
+ }
- NodeVisitor visitor = this;
- try {
- //do the statements - this fills the block with code
- boolean needsInitialEvalResult = functionNode.isProgram();
- for (final Node statement : functionNode.getStatements()) {
- // If this function is a program, then insert an assignment to the initial eval result after all
- // function declarations.
- if(needsInitialEvalResult && !(statement instanceof LineNumberNode || (statement instanceof VarNode && ((VarNode)statement).isFunctionDeclaration()))) {
- addInitialEvalResult(functionNode);
- needsInitialEvalResult = false;
- }
- statement.accept(visitor);
- //If there are unused terminated endpoints in the function, we need
- // to add a "return undefined" in those places for correct semantics
- LOG.info("Checking lastStatement="+lastStatement+" for terminal flags");
- if (lastStatement != null && lastStatement.hasTerminalFlags()) {
- copyTerminal(functionNode, lastStatement);
- assert !needsInitialEvalResult;
- visitor = new DeadCodeVarDeclarationVisitor();
- }
- }
- if(needsInitialEvalResult) {
- addInitialEvalResult(functionNode);
- }
- functionNode.setStatements(statements);
+ @Override
+ public Node leaveLabelNode(final LabelNode labelNode) {
+ return addStatement(labelNode);
+ }
- if (!functionNode.isTerminal()) {
- guaranteeReturn(functionNode);
- }
- } finally {
- statements = savedStatements;
- lastStatement = savedLastStatement;
- }
+ @Override
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ addStatement(lineNumberNode); // don't put it in lastStatement cache
+ return false;
+ }
- LOG.info("END FunctionNode: " + functionNode.getName());
- unnest(functionNode);
- lexicalContext.pop(functionNode);
+ @Override
+ public Node leaveReturnNode(final ReturnNode returnNode) {
+ addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
+ return returnNode;
+ }
- functionNode.setState(CompilationState.LOWERED);
- return null;
+ @Override
+ public Node leaveSwitchNode(final SwitchNode switchNode) {
+ return addStatement(switchNode);
}
- /**
- * This visitor is used to go over statements after a terminal statement. Those statements are dead code, but the
- * var declarations in them still have the effect of declaring a local variable on the function level. Therefore,
- * they aren't really dead code and must be preserved. Note that they're only preserved as no-op declarations; their
- * initializers are wiped out as those are, in fact, dead code.
- */
- private class DeadCodeVarDeclarationVisitor extends NodeOperatorVisitor {
- DeadCodeVarDeclarationVisitor() {
- }
-
- @Override
- public Node enterVarNode(VarNode varNode) {
- // Can't ever see a function declaration, as this visitor is only ever used after a terminal statement was
- // encountered, and all function declarations precede any terminal statements.
- assert !varNode.isFunctionDeclaration();
- if(varNode.getInit() == null) {
- // No initializer, just pass it to Lower.
- return varNode.accept(Lower.this);
- }
- // Wipe out the initializer and then pass it to Lower.
- return varNode.setInit(null).accept(Lower.this);
- }
+ @Override
+ public Node leaveThrowNode(final ThrowNode throwNode) {
+ addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
+ return throwNode;
}
- private void addInitialEvalResult(final FunctionNode functionNode) {
- new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(),
- getInitialEvalResult(functionNode)).accept(this);
+ private static Node ensureUniqueLabelsIn(final Node node) {
+ return node.accept(new NodeVisitor() {
+ @Override
+ public Node leaveDefault(final Node labelledNode) {
+ return labelledNode.ensureUniqueLabels(getLexicalContext());
+ }
+ });
}
- /**
- * Result of initial result of evaluating a particular program, which is either the last function it declares, or
- * undefined if it doesn't declare any functions.
- * @param program
- * @return the initial result of evaluating the program
- */
- private static Node getInitialEvalResult(final FunctionNode program) {
- IdentNode lastFnName = null;
- for (final FunctionNode fn : program.getDeclaredFunctions()) {
- assert fn.isDeclared();
- final IdentNode fnName = fn.getIdent();
- if(fnName != null) {
- lastFnName = fnName;
+ private static List<Node> copyFinally(final Block finallyBody) {
+ final List<Node> newStatements = new ArrayList<>();
+ for (final Node statement : finallyBody.getStatements()) {
+ newStatements.add(ensureUniqueLabelsIn(statement));
+ if (statement.hasTerminalFlags()) {
+ return newStatements;
}
}
- return lastFnName != null ? new IdentNode(lastFnName) : LiteralNode.newInstance(program, ScriptRuntime.UNDEFINED);
+ return newStatements;
}
- @Override
- public Node enterIfNode(final IfNode ifNode) {
- return nest(ifNode);
- }
+ private Block catchAllBlock(final TryNode tryNode) {
+ final Source source = tryNode.getSource();
+ final long token = tryNode.getToken();
+ final int finish = tryNode.getFinish();
- @Override
- public Node leaveIfNode(final IfNode ifNode) {
- final Node pass = ifNode.getPass();
- final Node fail = ifNode.getFail();
+ final IdentNode exception = new IdentNode(source, token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all"));
- if (pass.isTerminal() && fail != null && fail.isTerminal()) {
- setTerminal(ifNode, true);
- }
+ final Block catchBody = new Block(source, token, finish, new ThrowNode(source, token, finish, new IdentNode(exception))).
+ setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal
- addStatement(ifNode);
- unnest(ifNode);
+ final CatchNode catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
+ final Block catchAllBlock = new Block(source, token, finish, catchAllNode);
- return ifNode;
+ //catchallblock -> catchallnode (catchnode) -> exception -> throw
+
+ return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower
}
- @Override
- public Node enterLabelNode(LabelNode labelNode) {
- final Block body = labelNode.getBody();
- body.accept(this);
- copyTerminal(labelNode, body);
- addStatement(labelNode);
- return null;
+ private IdentNode compilerConstant(final CompilerConstants cc) {
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ return new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
}
- @Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
- addStatement(lineNumberNode, false); // don't put it in lastStatement cache
- return null;
+ private static boolean isTerminal(final List<Node> statements) {
+ return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags();
}
- @Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- final TryNode tryNode = returnNode.getTryChain();
- final Node expr = returnNode.getExpression();
-
- if (tryNode != null) {
- //we are inside a try block - we don't necessarily have a result node yet. attr will do that.
- if (expr != null) {
- final Source source = getCurrentFunctionNode().getSource();
-
- //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
- final long token = returnNode.getToken();
- final Node resultNode = new IdentNode(getCurrentFunctionNode().getResultNode());
- final Node assignResult = new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expr);
-
- //add return_in_try = expr; to try block
- new ExecuteNode(source, token, Token.descPosition(token), assignResult).accept(this);
-
- //splice in the finally code, inlining it here
- if (copyFinally(tryNode, null)) {
- return null;
- }
+ /**
+ * Splice finally code into all endpoints of a trynode
+ * @param tryNode the try node
+ * @param list of rethrowing throw nodes from synthetic catch blocks
+ * @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) {
+ final Source source = tryNode.getSource();
+ final int finish = tryNode.getFinish();
- //make sure that the return node now returns 'return_in_try'
- returnNode.setExpression(resultNode);
- } else if (copyFinally(tryNode, null)) {
- return null;
- }
- } else if (expr != null) {
- returnNode.setExpression(expr.accept(this));
- }
+ assert tryNode.getFinallyBody() == null;
- addStatement(returnNode);
+ final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() {
+ final List<Node> insideTry = new ArrayList<>();
- return null;
- }
+ @Override
+ public boolean enterDefault(final Node node) {
+ insideTry.add(node);
+ return true;
+ }
- @Override
- public Node leaveReturnNode(final ReturnNode returnNode) {
- addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
- return returnNode;
- }
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ // do not enter function nodes - finally code should not be inlined into them
+ return false;
+ }
- @Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- nest(switchNode);
- return switchNode;
- }
+ @Override
+ public Node leaveThrowNode(final ThrowNode throwNode) {
+ if (rethrows.contains(throwNode)) {
+ final List<Node> newStatements = copyFinally(finallyBody);
+ if (!isTerminal(newStatements)) {
+ newStatements.add(throwNode);
+ }
+ return new Block(source, throwNode.getToken(), throwNode.getFinish(), newStatements);
+ }
+ return throwNode;
+ }
- @Override
- public Node leaveSwitchNode(final SwitchNode switchNode) {
- unnest(switchNode);
+ @Override
+ public Node leaveBreakNode(final BreakNode breakNode) {
+ return copy(breakNode, Lower.this.getLexicalContext().getBreakable(breakNode.getLabel()));
+ }
- final List<CaseNode> cases = switchNode.getCases();
- final CaseNode defaultCase = switchNode.getDefaultCase();
+ @Override
+ public Node leaveContinueNode(final ContinueNode continueNode) {
+ return copy(continueNode, Lower.this.getLexicalContext().getContinueTo(continueNode.getLabel()));
+ }
- boolean allTerminal = !cases.isEmpty();
- for (final CaseNode caseNode : switchNode.getCases()) {
- allTerminal &= caseNode.isTerminal();
- }
+ @Override
+ public Node leaveReturnNode(final ReturnNode returnNode) {
+ final Node expr = returnNode.getExpression();
+ final List<Node> newStatements = new ArrayList<>();
+
+ final Node 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 ExecuteNode(new BinaryNode(source, Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
+ } else {
+ resultNode = null;
+ }
- if (allTerminal && defaultCase != null && defaultCase.isTerminal()) {
- setTerminal(switchNode, true);
- }
+ newStatements.addAll(copyFinally(finallyBody));
+ if (!isTerminal(newStatements)) {
+ newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
+ }
- addStatement(switchNode);
+ return new ExecuteNode(new Block(source, returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements));
+ }
- return switchNode;
- }
+ private Node copy(final Node endpoint, final Node targetNode) {
+ if (!insideTry.contains(targetNode)) {
+ final List<Node> newStatements = copyFinally(finallyBody);
+ if (!isTerminal(newStatements)) {
+ newStatements.add(endpoint);
+ }
+ return new ExecuteNode(new Block(source, endpoint.getToken(), finish, newStatements));
+ }
+ return endpoint;
+ }
+ });
- @Override
- public Node leaveThrowNode(final ThrowNode throwNode) {
- addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
- return throwNode;
+ addStatement(newTryNode);
+ for (final Node statement : finallyBody.getStatements()) {
+ addStatement(statement);
+ }
+
+ return newTryNode;
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
- final Block finallyBody = tryNode.getFinallyBody();
- final long token = tryNode.getToken();
- final int finish = tryNode.getFinish();
-
- nest(tryNode);
+ public Node leaveTryNode(final TryNode tryNode) {
+ final Block finallyBody = tryNode.getFinallyBody();
if (finallyBody == null) {
- //do nothing if no finally exists
- return tryNode;
+ return addStatement(tryNode);
}
/*
- * We have a finally clause.
+ * create a new trynode
+ * if we have catches:
*
- * Transform to do finally tail duplication as follows:
+ * try try
+ * x try
+ * catch x
+ * y catch
+ * finally z y
+ * catchall
+ * rethrow
*
- * <pre>
- * try {
- * try_body
- * } catch e1 {
- * catchbody_1
- * }
- * ...
- * } catch en {
- * catchbody_n
- * } finally {
- * finally_body
- * }
+ * otheriwse
*
- * (where e1 ... en are optional)
+ * try try
+ * x x
+ * finally catchall
+ * y rethrow
*
- * turns into
*
- * try {
- * try {
- * try_body
- * } catch e1 {
- * catchbody1
- * //nothing inlined explicitly here, return, break other
- * //terminals may inline the finally body
- * ...
- * } catch en {
- * catchbody2
- * //nothing inlined explicitly here, return, break other
- * //terminals may inline the finally body
- * }
- * } catch all ex {
- * finally_body_inlined
- * rethrow ex
- * }
- * finally_body_inlined
- * </pre>
+ * now splice in finally code wherever needed
*
- * If tries are catches are terminal, visitors for return, break &
- * continue will handle the tail duplications. Throw needs to be
- * treated specially with the catchall as described in the above
- * ASCII art.
- *
- * If the try isn't terminal we do the finally_body_inlined at the
- * end. If the try is terminated with continue/break/return the
- * existing visitor logic will inline the finally before that
- * operation. if the try is terminated with a throw, the catches e1
- * ... en will have a chance to process the exception. If the
- * appropriate catch e1..en is non terminal we fall through to the
- * last finally_body_inlined. if the catch e1...en IS terminal with
- * continue/break/return existing visitor logic will fix it. If they
- * are terminal with another throw it goes to the catchall and the
- * finally_body_inlined marked (*) will fix it before rethrowing
- * whatever problem there was for identical semantic.
*/
- final Source source = getCurrentFunctionNode().getSource();
-
- // if try node does not contain a catch we can skip creation of a new
- // try node and just append our synthetic catch to the existing try node.
- if (!tryNode.getCatchBlocks().isEmpty()) {
- // insert an intermediate try-catch* node, where we move the body and all catch blocks.
- // the original try node become a try-finally container for the new try-catch* node.
- // because we don't clone (to avoid deep copy), we have to fix the block chain in the end.
- final TryNode innerTryNode;
- innerTryNode = new TryNode(source, token, finish, tryNode.getNext());
- innerTryNode.setBody(tryNode.getBody());
- innerTryNode.setCatchBlocks(tryNode.getCatchBlocks());
-
- // set outer tryNode's body to innerTryNode
- final Block outerBody;
- outerBody = new Block(source, token, finish);
- outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
- tryNode.setBody(outerBody);
- tryNode.setCatchBlocks(null);
- }
-
- // create a catch-all that inlines finally and rethrows
-
- final Block catchBlock = new Block(source, token, finish);
- //this catch block should get define symbol
+ TryNode newTryNode;
- final Block catchBody = new Block(source, token, finish);
- final Node catchAllFinally = finallyBody.copy();
+ final Block catchAll = catchAllBlock(tryNode);
- catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
- setTerminal(catchBody, true);
-
- final CatchNode catchAllNode;
- final IdentNode exception;
-
- exception = new IdentNode(source, token, finish, getCurrentFunctionNode().uniqueName("catch_all"));
- catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
- catchAllNode.setIsSyntheticRethrow();
+ final List<ThrowNode> rethrows = new ArrayList<>();
+ catchAll.accept(new NodeVisitor() {
+ @Override
+ public boolean enterThrowNode(final ThrowNode throwNode) {
+ rethrows.add(throwNode);
+ return true;
+ }
+ });
+ assert rethrows.size() == 1;
- catchBlock.addStatement(catchAllNode);
+ if (tryNode.getCatchBlocks().isEmpty()) {
+ newTryNode = tryNode.setFinallyBody(null);
+ } else {
+ Block outerBody = new Block(tryNode.getSource(), tryNode.getToken(), tryNode.getFinish(), new ArrayList<Node>(Arrays.asList(tryNode.setFinallyBody(null))));
+ newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
+ }
- // replace all catches of outer tryNode with the catch-all
- tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock)));
+ newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null);
/*
- * We leave the finally block for the original try in place for now
- * so that children visitations will work. It is removed and placed
- * afterwards in the else case below, after all children are visited
+ * 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 tryNode;
- }
-
- @Override
- public Node leaveTryNode(final TryNode tryNode) {
- final Block finallyBody = tryNode.getFinallyBody();
-
- boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
-
- for (final Block catchBlock : tryNode.getCatchBlocks()) {
- allTerminal &= catchBlock.isTerminal();
- }
-
- tryNode.setIsTerminal(allTerminal);
-
- addStatement(tryNode);
- unnest(tryNode);
-
- // if finally body is present, place it after the tryNode
- if (finallyBody != null) {
- tryNode.setFinallyBody(null);
- addStatement(finallyBody);
- }
-
- return tryNode;
+ return spliceFinally(newTryNode, rethrows, finallyBody);
}
@Override
public Node leaveVarNode(final VarNode varNode) {
addStatement(varNode);
+ if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && getLexicalContext().getCurrentFunction().isProgram()) {
+ new ExecuteNode(varNode.getSource(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
+ }
return varNode;
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
- return nest(whileNode);
- }
-
- @Override
public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
+ final Block body = whileNode.getBody();
- if (test == null) {
- setHasGoto(whileNode);
- }
-
- final Block body = whileNode.getBody();
- final boolean escapes = controlFlowEscapes(body);
- if (escapes) {
- setTerminal(body, false);
+ if (conservativeAlwaysTrue(test)) {
+ //turn it into a for node without a test.
+ final ForNode forNode = (ForNode)new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
+ getLexicalContext().replace(whileNode, forNode);
+ return forNode;
}
- Node node = whileNode;
-
- if (body.isTerminal()) {
- if (whileNode instanceof DoWhileNode) {
- setTerminal(whileNode, true);
- } else if (conservativeAlwaysTrue(test)) {
- node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
- ((ForNode)node).setBody(body);
- node.accept(this);
- setTerminal(node, !escapes);
- }
- }
-
- // pop the loop from the loop context
- unnest(whileNode);
- addStatement(node);
-
- return node;
+ return addStatement(checkEscape(whileNode));
}
@Override
public Node leaveWithNode(final WithNode withNode) {
- if (withNode.getBody().isTerminal()) {
- setTerminal(withNode, true);
- }
- addStatement(withNode);
-
- return withNode;
+ return addStatement(withNode);
}
@Override
@@ -741,23 +549,25 @@ final class Lower extends NodeOperatorVisitor {
*
* @param callNode call node to check if it's an eval
*/
- private void checkEval(final CallNode callNode) {
+ private CallNode checkEval(final CallNode callNode) {
if (callNode.getFunction() instanceof IdentNode) {
final List<Node> args = callNode.getArgs();
final IdentNode callee = (IdentNode)callNode.getFunction();
// 'eval' call with at least one argument
- if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
- final CallNode.EvalArgs evalArgs =
+ if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) {
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
+ return callNode.setEvalArgs(
new CallNode.EvalArgs(
- args.get(0).copy().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
- getCurrentFunctionNode().getThisNode(),
+ ensureUniqueLabelsIn(args.get(0)).accept(this),
+ compilerConstant(THIS),
evalLocation(callee),
- getCurrentFunctionNode().isStrictMode());
- callNode.setEvalArgs(evalArgs);
+ currentFunction.isStrict()));
}
}
+
+ return callNode;
}
private static boolean conservativeAlwaysTrue(final Node node) {
@@ -773,7 +583,7 @@ final class Lower extends NodeOperatorVisitor {
* @param loopBody the loop body to check
* @return true if control flow may escape the loop
*/
- private boolean controlFlowEscapes(final Node loopBody) {
+ private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) {
final List<Node> escapes = new ArrayList<>();
loopBody.accept(new NodeVisitor() {
@@ -786,7 +596,7 @@ final class Lower extends NodeOperatorVisitor {
@Override
public Node leaveContinueNode(final ContinueNode node) {
// all inner loops have been popped.
- if (nesting.contains(node.getTargetNode())) {
+ if (lex.contains(lex.getContinueTo(node.getLabel()))) {
escapes.add(node);
}
return node;
@@ -796,135 +606,23 @@ final class Lower extends NodeOperatorVisitor {
return !escapes.isEmpty();
}
- private void guaranteeReturn(final FunctionNode functionNode) {
- Node resultNode;
-
- if (functionNode.isProgram()) {
- resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr
- } else {
- if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) {
- return; //already in place or not needed, as it should be for a non-undefined returning function
- }
- resultNode = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
- }
-
- //create a return statement
- final Node returnNode = new ReturnNode(functionNode.getSource(), functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
- returnNode.accept(this);
- }
-
-
- private Node nest(final Node node) {
- LOG.info("Nesting: " + node);
- LOG.indent();
- nesting.push(node);
- return node;
- }
-
- private void unnest(final Node node) {
- LOG.unindent();
- assert nesting.getFirst() == node : "inconsistent nesting order : " + nesting.getFirst() + " != " + node;
- LOG.info("Unnesting: " + nesting);
- nesting.pop();
- }
-
- private static void setTerminal(final Node node, final boolean isTerminal) {
- LOG.info("terminal = " + isTerminal + " for " + node);
- node.setIsTerminal(isTerminal);
- }
-
- private static void setHasGoto(final Node node) { //, final boolean hasGoto) {
- LOG.info("hasGoto = true for " + node);
- node.setHasGoto();
- }
-
- private static void copyTerminal(final Node node, final Node sourceNode) {
- LOG.info("copy terminal flags " + sourceNode + " -> " + node);
- node.copyTerminalFlags(sourceNode);
- }
-
- private void addStatement(final Node statement, final boolean storeInLastStatement) {
- LOG.info("add statement = " + statement + " (lastStatement = " + lastStatement + ")");
- statements.add(statement);
- if (storeInLastStatement) {
- lastStatement = statement;
- }
- }
-
- private void addStatement(final Node statement) {
- addStatement(statement, true);
- }
-
- /**
- * Determine if Try block is inside target block.
- *
- * @param tryNode Try node to test.
- * @param target Target block.
- *
- * @return true if try block is inside the target, false otherwise.
- */
- private boolean isNestedTry(final TryNode tryNode, final Block target) {
- for(Iterator<Block> blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) {
- final Block block = blocks.next();
- if(block == target) {
- return false;
- }
- if(tryNode.isChildBlock(block)) {
- return true;
- }
+ private LoopNode checkEscape(final LoopNode loopNode) {
+ final LexicalContext lc = getLexicalContext();
+ final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
+ if (escapes) {
+ return loopNode.
+ setBody(lc, loopNode.getBody().setIsTerminal(lc, false)).
+ setControlFlowEscapes(lc, escapes);
}
- return false;
+ return loopNode;
}
- /**
- * Clones the body of the try finallys up to the target block.
- *
- * @param node first try node in the chain.
- * @param targetNode target block of the break/continue statement or null for return
- *
- * @return true if terminates.
- */
- private boolean copyFinally(final TryNode node, final Node targetNode) {
- Block target = null;
-
- if (targetNode instanceof Block) {
- target = (Block)targetNode;
- }
-
- for (TryNode tryNode = node; tryNode != null; tryNode = tryNode.getNext()) {
- if (target != null && !isNestedTry(tryNode, target)) {
- return false;
- }
-
- Block finallyBody = tryNode.getFinallyBody();
- if (finallyBody == null) {
- continue;
- }
-
- finallyBody = (Block)finallyBody.copy();
- final boolean hasTerminalFlags = finallyBody.hasTerminalFlags();
-
- new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
- if (hasTerminalFlags) {
- getCurrentBlock().copyTerminalFlags(finallyBody);
- return true;
- }
- }
-
- return false;
- }
-
- private Node enterBreakOrContinue(final LabeledNode labeledNode) {
- final TryNode tryNode = labeledNode.getTryChain();
- if (tryNode != null && copyFinally(tryNode, labeledNode.getTargetNode())) {
- return null;
- }
- addStatement(labeledNode);
- return null;
+ private Node addStatement(final Node statement) {
+ ((BlockLexicalContext)getLexicalContext()).appendStatement(statement);
+ return statement;
}
-
/**
* An internal expression has a symbol that is tagged internal. Check if
* this is such a node
@@ -939,40 +637,21 @@ final class Lower extends NodeOperatorVisitor {
/**
* Is this an assignment to the special variable that hosts scripting eval
- * results?
+ * results, i.e. __return__?
*
* @param expression expression to check whether it is $evalresult = X
* @return true if an assignment to eval result, false otherwise
*/
- private boolean isEvalResultAssignment(final Node expression) {
+ private static boolean isEvalResultAssignment(final Node expression) {
Node e = expression;
- if (e.tokenType() == TokenType.DISCARD) {
- e = ((UnaryNode)expression).rhs();
- }
- final Node resultNode = getCurrentFunctionNode().getResultNode();
- return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode);
- }
-
- /**
- * Prepare special function nodes.
- * TODO : only create those that are needed.
- * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant
- */
- private static void initFunctionNode(final FunctionNode functionNode) {
- final Source source = functionNode.getSource();
- final long token = functionNode.getToken();
- final int finish = functionNode.getFinish();
-
- functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
- functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
- functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
- functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
- if (functionNode.isVarArg()) {
- functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
- if (functionNode.needsArguments()) {
- functionNode.setArgumentsNode(new IdentNode(source, token, finish, ARGUMENTS.tag()));
+ assert e.tokenType() != TokenType.DISCARD; //there are no discards this early anymore
+ if (e instanceof BinaryNode) {
+ final Node lhs = ((BinaryNode)e).lhs();
+ if (lhs instanceof IdentNode) {
+ return ((IdentNode)lhs).getName().equals(RETURN.symbolName());
}
}
+ return false;
}
}
diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
index ae40ed33..4fbb57a6 100644
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
@@ -53,9 +53,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
@@ -67,6 +70,7 @@ import java.lang.reflect.Array;
import java.util.ArrayDeque;
import java.util.EnumSet;
import java.util.Iterator;
+import java.util.List;
import jdk.internal.dynalink.support.NameCodec;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
@@ -79,14 +83,14 @@ import jdk.nashorn.internal.codegen.types.NumericType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.RuntimeNode;
-import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.ArgumentSetter;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -116,10 +120,10 @@ public class MethodEmitter implements Emitter {
private final ClassEmitter classEmitter;
/** FunctionNode representing this method, or null if none exists */
- private FunctionNode functionNode;
+ protected FunctionNode functionNode;
- /** SplitNode representing the current split, or null if none exists */
- private SplitNode splitNode;
+ /** Check whether this emitter ever has a function return point */
+ private boolean hasReturn;
/** The script environment */
private final ScriptEnvironment env;
@@ -203,7 +207,7 @@ public class MethodEmitter implements Emitter {
@Override
public String toString() {
- return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + stack;
+ return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this);
}
/**
@@ -476,8 +480,8 @@ public class MethodEmitter implements Emitter {
String name = symbol.getName();
- if (name.equals(THIS.tag())) {
- name = THIS_DEBUGGER.tag();
+ if (name.equals(THIS.symbolName())) {
+ name = THIS_DEBUGGER.symbolName();
}
method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot());
@@ -654,7 +658,7 @@ public class MethodEmitter implements Emitter {
* @return this method emitter
*/
MethodEmitter loadConstants() {
- getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor());
+ getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor());
assert peekType().isArray() : peekType();
return this;
}
@@ -669,7 +673,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter loadUndefined(final Type type) {
- debug("load undefined " + type);
+ debug("load undefined ", type);
pushType(type.loadUndefined(method));
return this;
}
@@ -681,7 +685,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter loadEmpty(final Type type) {
- debug("load empty " + type);
+ debug("load empty ", type);
pushType(type.loadEmpty(method));
return this;
}
@@ -814,7 +818,7 @@ public class MethodEmitter implements Emitter {
}
/**
- * Push an local variable to the stack. If the symbol representing
+ * Push a local variable to the stack. If the symbol representing
* the local variable doesn't have a slot, this is a NOP
*
* @param symbol the symbol representing the local variable.
@@ -835,13 +839,13 @@ public class MethodEmitter implements Emitter {
if (functionNode.needsArguments()) {
// ScriptObject.getArgument(int) on arguments
debug("load symbol", symbol.getName(), " arguments index=", index);
- loadArguments();
+ loadCompilerConstant(ARGUMENTS);
load(index);
ScriptObject.GET_ARGUMENT.invoke(this);
} else {
// array load from __varargs__
debug("load symbol", symbol.getName(), " array index=", index);
- loadVarArgs();
+ loadCompilerConstant(VARARGS);
load(symbol.getFieldIndex());
arrayload();
}
@@ -870,48 +874,13 @@ public class MethodEmitter implements Emitter {
if(functionNode == null) {
return slot == CompilerConstants.JAVA_THIS.slot();
}
- final int thisSlot = functionNode.getThisNode().getSymbol().getSlot();
+ final int thisSlot = compilerConstant(THIS).getSlot();
assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
return slot == thisSlot;
}
/**
- * Push the this object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadThis() {
- load(functionNode.getThisNode().getSymbol());
- return this;
- }
-
- /**
- * Push the scope object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadScope() {
- if (peekType() == Type.SCOPE) {
- dup();
- return this;
- }
- load(functionNode.getScopeNode().getSymbol());
- return this;
- }
-
- /**
- * Push the return object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadResult() {
- load(functionNode.getResultNode().getSymbol());
- return this;
- }
-
-
- /**
* Push a method handle to the stack
*
* @param className class name
@@ -927,62 +896,33 @@ public class MethodEmitter implements Emitter {
return this;
}
- /**
- * Push the varargs object to the stack
- *
- * @return the method emitter
- */
- MethodEmitter loadVarArgs() {
- debug("load var args " + functionNode.getVarArgsNode().getSymbol());
- return load(functionNode.getVarArgsNode().getSymbol());
+ private Symbol compilerConstant(final CompilerConstants cc) {
+ return functionNode.getBody().getExistingSymbol(cc.symbolName());
}
/**
- * Push the arguments array to the stack
- *
- * @return the method emitter
+ * True if this method has a slot allocated for the scope variable (meaning, something in the method actually needs
+ * the scope).
+ * @return if this method has a slot allocated for the scope variable.
*/
- MethodEmitter loadArguments() {
- debug("load arguments ", functionNode.getArgumentsNode().getSymbol());
- assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0;
- return load(functionNode.getArgumentsNode().getSymbol());
+ boolean hasScope() {
+ return compilerConstant(SCOPE).hasSlot();
}
- /**
- * Push the callee object to the stack
- *
- * @return the method emitter
- */
- MethodEmitter loadCallee() {
- final Symbol calleeSymbol = functionNode.getCalleeNode().getSymbol();
- debug("load callee ", calleeSymbol);
- assert calleeSymbol.getSlot() == 0 : "callee has wrong slot " + calleeSymbol.getSlot() + " in " + functionNode.getName();
-
- return load(calleeSymbol);
- }
-
- /**
- * Pop the scope from the stack and store it in its predefined slot
- */
- void storeScope() {
- debug("store scope");
- store(functionNode.getScopeNode().getSymbol());
+ MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
+ final Symbol symbol = compilerConstant(cc);
+ if (cc == SCOPE && peekType() == Type.SCOPE) {
+ dup();
+ return this;
+ }
+ debug("load compiler constant ", symbol);
+ return load(symbol);
}
- /**
- * Pop the return from the stack and store it in its predefined slot
- */
- void storeResult() {
- debug("store result");
- store(functionNode.getResultNode().getSymbol());
- }
-
- /**
- * Pop the arguments array from the stack and store it in its predefined slot
- */
- void storeArguments() {
- debug("store arguments");
- store(functionNode.getArgumentsNode().getSymbol());
+ void storeCompilerConstant(final CompilerConstants cc) {
+ final Symbol symbol = compilerConstant(cc);
+ debug("store compiler constant ", symbol);
+ store(symbol);
}
/**
@@ -1030,13 +970,13 @@ public class MethodEmitter implements Emitter {
final int index = symbol.getFieldIndex();
if (functionNode.needsArguments()) {
debug("store symbol", symbol.getName(), " arguments index=", index);
- loadArguments();
+ loadCompilerConstant(ARGUMENTS);
load(index);
ArgumentSetter.SET_ARGUMENT.invoke(this);
} else {
// varargs without arguments object - just do array store to __varargs__
debug("store symbol", symbol.getName(), " array index=", index);
- loadVarArgs();
+ loadCompilerConstant(VARARGS);
load(index);
ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
}
@@ -1144,7 +1084,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter newarray(final ArrayType arrayType) {
- debug("newarray ", "arrayType=" + arrayType);
+ debug("newarray ", "arrayType=", arrayType);
popType(Type.INT); //LENGTH
pushType(arrayType.newarray(method));
return this;
@@ -1223,7 +1163,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter invokespecial(final String className, final String methodName, final String methodDescriptor) {
- debug("invokespecial", className + "." + methodName + methodDescriptor);
+ debug("invokespecial", className, ".", methodName, methodDescriptor);
return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true);
}
@@ -1237,7 +1177,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter invokevirtual(final String className, final String methodName, final String methodDescriptor) {
- debug("invokevirtual", className + "." + methodName + methodDescriptor + " " + stack);
+ debug("invokevirtual", className, ".", methodName, methodDescriptor, " ", stack);
return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true);
}
@@ -1251,7 +1191,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor) {
- debug("invokestatic", className + "." + methodName + methodDescriptor);
+ debug("invokestatic", className, ".", methodName, methodDescriptor);
invoke(INVOKESTATIC, className, methodName, methodDescriptor, false);
return this;
}
@@ -1284,7 +1224,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter invokeinterface(final String className, final String methodName, final String methodDescriptor) {
- debug("invokeinterface", className + "." + methodName + methodDescriptor);
+ debug("invokeinterface", className, ".", methodName, methodDescriptor);
return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true);
}
@@ -1336,15 +1276,20 @@ public class MethodEmitter implements Emitter {
*/
void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) {
if (peekType().isCategory2()) {
- debug("[ld]cmp isCmpG=" + isCmpG);
+ debug("[ld]cmp isCmpG=", isCmpG);
pushType(get2n().cmp(method, isCmpG));
jump(Condition.toUnary(cond), trueLabel, 1);
} else {
- debug("if" + cond);
+ debug("if", cond);
jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2);
}
}
+ MethodEmitter registerReturn() {
+ this.hasReturn = true;
+ return this;
+ }
+
/**
* Perform a non void return, popping the type from the stack
*
@@ -1385,22 +1330,7 @@ public class MethodEmitter implements Emitter {
*
* @param label destination label
*/
- void splitAwareGoto(final Label label) {
-
- if (splitNode != null) {
- final int index = splitNode.getExternalTargets().indexOf(label);
-
- if (index > -1) {
- loadScope();
- checkcast(Scope.class);
- load(index + 1);
- invoke(Scope.SET_SPLIT_STATE);
- loadUndefined(Type.OBJECT);
- _return(functionNode.getReturnType());
- return;
- }
- }
-
+ void splitAwareGoto(final LexicalContext lc, final Label label) {
_goto(label);
}
@@ -1595,7 +1525,7 @@ public class MethodEmitter implements Emitter {
*/
private void mergeStackTo(final Label label) {
final ArrayDeque<Type> labelStack = label.getStack();
- //debug(labelStack == null ? " >> Control flow - first visit " + label : " >> Control flow - JOIN with " + labelStack + " at " + label);
+ //debug(labelStack == null ? " >> Control flow - first visit ", label : " >> Control flow - JOIN with ", labelStack, " at ", label);
if (labelStack == null) {
assert stack != null;
label.setStack(stack.clone());
@@ -1788,7 +1718,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicNew(final int argCount, final int flags) {
- debug("dynamic_new", "argcount=" + argCount);
+ debug("dynamic_new", "argcount=", argCount);
final String signature = getDynamicSignature(Type.OBJECT, argCount);
method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
pushType(Type.OBJECT); //TODO fix result type
@@ -1805,7 +1735,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) {
- debug("dynamic_call", "args=" + argCount, "returnType=" + returnType);
+ debug("dynamic_call", "args=", argCount, "returnType=", returnType);
final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target)
debug(" signature", signature);
method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags);
@@ -1824,7 +1754,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) {
- debug("dynamic_runtime_call", name, "args=" + request.getArity(), "returnType=" + returnType);
+ debug("dynamic_runtime_call", name, "args=", request.getArity(), "returnType=", returnType);
final String signature = getDynamicSignature(returnType, request.getArity());
debug(" signature", signature);
method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP);
@@ -1895,7 +1825,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
- debug("dynamic_get_index", peekType(1) + "[" + peekType() + "]");
+ debug("dynamic_get_index", peekType(1), "[", peekType(), "]");
Type resultType = result;
if (result.isBoolean()) {
@@ -1931,7 +1861,7 @@ public class MethodEmitter implements Emitter {
* @param flags call site flags for setter
*/
void dynamicSetIndex(final int flags) {
- debug("dynamic_set_index", peekType(2) + "[" + peekType(1) + "] =", peekType());
+ debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
Type value = peekType();
if (value.isObject() || value.isBoolean()) {
@@ -2031,7 +1961,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) {
- debug("getfield", "receiver=" + peekType(), className + "." + fieldName + fieldDescriptor);
+ debug("getfield", "receiver=", peekType(), className, ".", fieldName, fieldDescriptor);
final Type receiver = popType();
assert receiver.isObject();
method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor);
@@ -2049,7 +1979,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) {
- debug("getstatic", className + "." + fieldName + "." + fieldDescriptor);
+ debug("getstatic", className, ".", fieldName, ".", fieldDescriptor);
method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor);
pushType(fieldType(fieldDescriptor));
return this;
@@ -2063,7 +1993,7 @@ public class MethodEmitter implements Emitter {
* @param fieldDescriptor field descriptor
*/
void putField(final String className, final String fieldName, final String fieldDescriptor) {
- debug("putfield", "receiver=" + peekType(1), "value=" + peekType());
+ debug("putfield", "receiver=", peekType(1), "value=", peekType());
popType(fieldType(fieldDescriptor));
popType(Type.OBJECT);
method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor);
@@ -2077,7 +2007,7 @@ public class MethodEmitter implements Emitter {
* @param fieldDescriptor field descriptor
*/
void putStatic(final String className, final String fieldName, final String fieldDescriptor) {
- debug("putfield", "value=" + peekType());
+ debug("putfield", "value=", peekType());
popType(fieldType(fieldDescriptor));
method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor);
}
@@ -2237,7 +2167,7 @@ public class MethodEmitter implements Emitter {
}
if (env != null) { //early bootstrap code doesn't have inited context yet
- LOG.info(sb.toString());
+ LOG.info(sb);
if (DEBUG_TRACE_LINE == linePrefix) {
new Throwable().printStackTrace(LOG.getOutputStream());
}
@@ -2254,21 +2184,12 @@ public class MethodEmitter implements Emitter {
this.functionNode = functionNode;
}
- /**
- * Get the split node for this method emitter, if this is code
- * generation due to splitting large methods
- *
- * @return split node
- */
- SplitNode getSplitNode() {
- return splitNode;
+ boolean hasReturn() {
+ return hasReturn;
}
- /**
- * Set the split node for this method emitter
- * @param splitNode split node
- */
- void setSplitNode(final SplitNode splitNode) {
- this.splitNode = splitNode;
+ List<Label> getExternalTargets() {
+ return null;
}
+
}
diff --git a/src/jdk/nashorn/internal/codegen/Namespace.java b/src/jdk/nashorn/internal/codegen/Namespace.java
index 02fdb942..5de2fdf8 100644
--- a/src/jdk/nashorn/internal/codegen/Namespace.java
+++ b/src/jdk/nashorn/internal/codegen/Namespace.java
@@ -53,7 +53,7 @@ public class Namespace {
*/
public Namespace(final Namespace parent) {
this.parent = parent;
- directory = new HashMap<>();
+ this.directory = new HashMap<>();
}
/**
@@ -65,10 +65,6 @@ public class Namespace {
return parent;
}
- private HashMap<String, Integer> getDirectory() {
- return directory;
- }
-
/**
* Create a uniqueName name in the namespace in the form base$n where n varies
* .
@@ -78,7 +74,7 @@ public class Namespace {
*/
public String uniqueName(final String base) {
for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) {
- final HashMap<String, Integer> namespaceDirectory = namespace.getDirectory();
+ final HashMap<String, Integer> namespaceDirectory = namespace.directory;
final Integer counter = namespaceDirectory.get(base);
if (counter != null) {
diff --git a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java
index 6e032e2c..b7514526 100644
--- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java
@@ -28,10 +28,10 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.lookup.Lookup.MH;
@@ -204,8 +204,8 @@ public final class ObjectClassGenerator {
* @return The class name.
*/
public static String getClassName(final int fieldCount) {
- return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount :
- SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag();
+ return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount :
+ SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName();
}
/**
@@ -218,7 +218,23 @@ public final class ObjectClassGenerator {
* @return The class name.
*/
public static String getClassName(final int fieldCount, final int paramCount) {
- return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount + SCOPE_MARKER + paramCount;
+ return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount;
+ }
+
+ /**
+ * Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
+ * {@link #getClassName(int)} or {@link #getClassName(int, int)}.
+ * @param clazz the JavaScript scope class.
+ * @return the number of fields in the scope class.
+ */
+ public static int getFieldCount(Class<?> clazz) {
+ final String name = clazz.getSimpleName();
+ final String prefix = JS_OBJECT_PREFIX.symbolName();
+ if(prefix.equals(name)) {
+ return 0;
+ }
+ final int scopeMarker = name.indexOf(SCOPE_MARKER);
+ return Integer.parseInt(scopeMarker == -1 ? name.substring(prefix.length()) : name.substring(prefix.length(), scopeMarker));
}
/**
@@ -387,7 +403,7 @@ public final class ObjectClassGenerator {
final MethodEmitter init = classEmitter.init(PropertyMap.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.invoke(constructorNoLookup(ScriptObject.class, PropertyMap.class));
return init;
@@ -402,7 +418,7 @@ public final class ObjectClassGenerator {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class));
@@ -418,7 +434,7 @@ public final class ObjectClassGenerator {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, Object.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.load(Type.OBJECT, INIT_ARGUMENTS.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, Object.class));
@@ -449,7 +465,7 @@ public final class ObjectClassGenerator {
* @param className Name of JavaScript class.
*/
private static void newAllocate(final ClassEmitter classEmitter, final String className) {
- final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.tag(), ScriptObject.class, PropertyMap.class);
+ final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
allocate.begin();
allocate._new(className);
allocate.dup();
diff --git a/src/jdk/nashorn/internal/codegen/ObjectCreator.java b/src/jdk/nashorn/internal/codegen/ObjectCreator.java
index e4e7b7da..a9c494cc 100644
--- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java
+++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java
@@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.PropertyMap;
public abstract class ObjectCreator {
/** Compile unit for this ObjectCreator, see CompileUnit */
- protected final CompileUnit compileUnit;
+ //protected final CompileUnit compileUnit;
/** List of keys to initiate in this ObjectCreator */
protected final List<String> keys;
@@ -66,7 +66,6 @@ public abstract class ObjectCreator {
*/
protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean hasArguments) {
this.codegen = codegen;
- this.compileUnit = codegen.getCurrentCompileUnit();
this.keys = keys;
this.symbols = symbols;
this.isScope = isScope;
diff --git a/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
new file mode 100644
index 00000000..5fa4486a
--- /dev/null
+++ b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.runtime.Scope;
+
+/**
+ * Emitter used for splitting methods. Needs to keep track of if there are jump targets
+ * outside the current split node. All external jump targets encountered at method
+ * emission are logged, and {@code CodeGenerator#leaveSplitNode(SplitNode)} creates
+ * an appropriate jump table when the SplitNode has been iterated through
+ */
+public class SplitMethodEmitter extends MethodEmitter {
+
+ private final SplitNode splitNode;
+
+ private final List<Label> externalTargets = new ArrayList<>();
+
+ SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, SplitNode splitNode) {
+ super(classEmitter, mv);
+ this.splitNode = splitNode;
+ }
+
+ @Override
+ void splitAwareGoto(final LexicalContext lc, final Label label) {
+ assert splitNode != null;
+ final int index = findExternalTarget(lc, label);
+ if (index >= 0) {
+ loadCompilerConstant(SCOPE);
+ checkcast(Scope.class);
+ load(index + 1);
+ invoke(Scope.SET_SPLIT_STATE);
+ loadUndefined(Type.OBJECT);
+ _return(functionNode.getReturnType());
+ return;
+ }
+ super.splitAwareGoto(lc, label);
+ }
+
+ private int findExternalTarget(final LexicalContext lc, final Label label) {
+ final int index = externalTargets.indexOf(label);
+
+ if (index >= 0) {
+ return index;
+ }
+
+ if (lc.isExternalTarget(splitNode, label)) {
+ externalTargets.add(label);
+ return externalTargets.size() - 1;
+ }
+ return -1;
+ }
+
+ @Override
+ MethodEmitter registerReturn() {
+ super.registerReturn();
+ loadCompilerConstant(SCOPE);
+ checkcast(Scope.class);
+ load(0);
+ invoke(Scope.SET_SPLIT_STATE);
+ return this;
+ }
+
+ @Override
+ final List<Label> getExternalTargets() {
+ return externalTargets;
+ }
+}
diff --git a/src/jdk/nashorn/internal/codegen/Splitter.java b/src/jdk/nashorn/internal/codegen/Splitter.java
index f9a84f91..f482e4df 100644
--- a/src/jdk/nashorn/internal/codegen/Splitter.java
+++ b/src/jdk/nashorn/internal/codegen/Splitter.java
@@ -28,29 +28,18 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
import java.util.ArrayList;
-import java.util.Deque;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.ir.Block;
-import jdk.nashorn.internal.ir.BreakNode;
-import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
-import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
-import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.SplitNode;
-import jdk.nashorn.internal.ir.SwitchNode;
-import jdk.nashorn.internal.ir.WhileNode;
-import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.Source;
@@ -64,7 +53,7 @@ final class Splitter extends NodeVisitor {
private final Compiler compiler;
/** IR to be broken down. */
- private final FunctionNode functionNode;
+ private FunctionNode outermost;
/** Compile unit for the main script. */
private final CompileUnit outermostCompileUnit;
@@ -72,8 +61,6 @@ final class Splitter extends NodeVisitor {
/** Cache for calculated block weights. */
private final Map<Node, Long> weightCache = new HashMap<>();
- private final LexicalContext lexicalContext = new LexicalContext();
-
/** Weight threshold for when to start a split. */
public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
@@ -88,70 +75,92 @@ final class Splitter extends NodeVisitor {
*/
public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
this.compiler = compiler;
- this.functionNode = functionNode;
+ this.outermost = functionNode;
this.outermostCompileUnit = outermostCompileUnit;
}
/**
* Execute the split
*/
- void split() {
+ FunctionNode split(final FunctionNode fn) {
+ FunctionNode functionNode = fn;
+
if (functionNode.isLazy()) {
- LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy");
- return;
+ LOG.finest("Postponing split of '", functionNode.getName(), "' as it's lazy");
+ return functionNode;
}
- LOG.finest("Initiating split of '" + functionNode.getName() + "'");
+ LOG.finest("Initiating split of '", functionNode.getName(), "'");
+
+ final LexicalContext lc = getLexicalContext();
long weight = WeighNodes.weigh(functionNode);
+ final boolean top = compiler.getFunctionNode() == outermost;
if (weight >= SPLIT_THRESHOLD) {
- LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
-
- functionNode.accept(this);
+ LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
+ functionNode = (FunctionNode)functionNode.accept(this);
if (functionNode.isSplit()) {
// Weight has changed so weigh again, this time using block weight cache
weight = WeighNodes.weigh(functionNode, weightCache);
+ functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(lc));
}
if (weight >= SPLIT_THRESHOLD) {
- weight = splitBlock(functionNode, functionNode);
- }
-
- if (functionNode.isSplit()) {
- functionNode.accept(new SplitFlowAnalyzer());
+ functionNode = functionNode.setBody(lc, splitBlock(functionNode.getBody(), functionNode));
+ weight = WeighNodes.weigh(functionNode.getBody(), weightCache);
}
}
- assert functionNode.getCompileUnit() == null : "compile unit already set";
+ assert functionNode.getCompileUnit() == null : "compile unit already set for " + functionNode.getName();
- if (compiler.getFunctionNode() == functionNode) { //functionNode.isScript()) {
+ if (top) {
assert outermostCompileUnit != null : "outermost compile unit is null";
-
- functionNode.setCompileUnit(outermostCompileUnit);
+ functionNode = functionNode.setCompileUnit(lc, outermostCompileUnit);
outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
} else {
- functionNode.setCompileUnit(findUnit(weight));
+ functionNode = functionNode.setCompileUnit(lc, findUnit(weight));
}
- // Recursively split nested functions
- functionNode.accept(new NodeOperatorVisitor() {
+ final Block body = functionNode.getBody();
+ final List<FunctionNode> dc = directChildren(functionNode);
+
+ final Block newBody = (Block)body.accept(new NodeVisitor() {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode nestedFunction) {
+ return dc.contains(nestedFunction);
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode nestedFunction) {
+ FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
+ getLexicalContext().replace(nestedFunction, split);
+ return split;
+ }
+ });
+ functionNode = functionNode.setBody(lc, newBody);
+
+ assert functionNode.getCompileUnit() != null;
+
+ return functionNode.setState(lc, CompilationState.SPLIT);
+ }
+
+ private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
+ final List<FunctionNode> dc = new ArrayList<>();
+ functionNode.accept(new NodeVisitor() {
@Override
- public Node enterFunctionNode(FunctionNode function) {
- if(function == functionNode) {
- // Don't process outermost function (it was already processed) but descend into it to find nested
- // functions.
- return function;
+ public boolean enterFunctionNode(final FunctionNode child) {
+ if (child == functionNode) {
+ return true;
}
- // Process a nested function
- new Splitter(compiler, function, outermostCompileUnit).split();
- // Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions.
- return null;
+ if (getLexicalContext().getParentFunction(child) == functionNode) {
+ dc.add(child);
+ }
+ return false;
}
});
-
- functionNode.setState(CompilationState.SPLIT);
+ return dc;
}
/**
@@ -170,8 +179,8 @@ final class Splitter extends NodeVisitor {
*
* @return new weight for the resulting block.
*/
- private long splitBlock(final Block block, final FunctionNode function) {
- functionNode.setIsSplit();
+ private Block splitBlock(final Block block, final FunctionNode function) {
+ getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
final List<Node> splits = new ArrayList<>();
List<Node> statements = new ArrayList<>();
@@ -186,7 +195,6 @@ final class Splitter extends NodeVisitor {
statements = new ArrayList<>();
statementsWeight = 0;
}
-
}
if (statement.isTerminal()) {
@@ -201,9 +209,7 @@ final class Splitter extends NodeVisitor {
splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
}
- block.setStatements(splits);
-
- return WeighNodes.weigh(block, weightCache);
+ return block.setStatements(getLexicalContext(), splits);
}
/**
@@ -218,51 +224,44 @@ final class Splitter extends NodeVisitor {
final Source source = parent.getSource();
final long token = parent.getToken();
final int finish = parent.getFinish();
- final String name = function.uniqueName(SPLIT_PREFIX.tag());
+ final String name = function.uniqueName(SPLIT_PREFIX.symbolName());
- final Block newBlock = new Block(source, token, finish);
- newBlock.setFrame(new Frame(parent.getFrame()));
- newBlock.setStatements(statements);
+ final Block newBlock = new Block(source, token, finish, statements);
- final SplitNode splitNode = new SplitNode(name, functionNode, newBlock);
-
- splitNode.setCompileUnit(compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
-
- return splitNode;
+ return new SplitNode(name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
if (block.isCatchBlock()) {
- return null;
+ return false;
}
- lexicalContext.push(block);
final long weight = WeighNodes.weigh(block, weightCache);
if (weight < SPLIT_THRESHOLD) {
weightCache.put(block, weight);
- lexicalContext.pop(block);
- return null;
+ return false;
}
- return block;
+ return true;
}
@Override
public Node leaveBlock(final Block block) {
assert !block.isCatchBlock();
+ Block newBlock = block;
+
// Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
// been split already, so weigh again before splitting.
long weight = WeighNodes.weigh(block, weightCache);
if (weight >= SPLIT_THRESHOLD) {
- weight = splitBlock(block, lexicalContext.getFunction(block));
+ newBlock = splitBlock(block, getLexicalContext().getFunction(block));
+ weight = WeighNodes.weigh(newBlock, weightCache);
}
- weightCache.put(block, weight);
-
- lexicalContext.pop(block);
- return block;
+ weightCache.put(newBlock, weight);
+ return newBlock;
}
@SuppressWarnings("rawtypes")
@@ -274,7 +273,7 @@ final class Splitter extends NodeVisitor {
return literal;
}
- functionNode.setIsSplit();
+ getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
if (literal instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
@@ -312,123 +311,12 @@ final class Splitter extends NodeVisitor {
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- if(node == functionNode && !node.isLazy()) {
- lexicalContext.push(node);
- node.visitStatements(this);
- lexicalContext.pop(node);
- }
- return null;
- }
-
- static class SplitFlowAnalyzer extends NodeVisitor {
-
- /** Stack of visited Split nodes, deepest node first. */
- private final Deque<SplitNode> splitStack;
-
- /** Map of possible jump targets to containing split node */
- private final Map<Node,SplitNode> targetNodes = new HashMap<>();
-
- SplitFlowAnalyzer() {
- this.splitStack = new LinkedList<>();
- }
-
- @Override
- public Node enterLabelNode(final LabelNode labelNode) {
- registerJumpTarget(labelNode.getBreakNode());
- registerJumpTarget(labelNode.getContinueNode());
- return labelNode;
- }
-
- @Override
- public Node enterWhileNode(final WhileNode whileNode) {
- registerJumpTarget(whileNode);
- return whileNode;
- }
-
- @Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- registerJumpTarget(doWhileNode);
- return doWhileNode;
- }
-
- @Override
- public Node enterForNode(final ForNode forNode) {
- registerJumpTarget(forNode);
- return forNode;
- }
-
- @Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- registerJumpTarget(switchNode);
- return switchNode;
- }
-
- @Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- for (final SplitNode split : splitStack) {
- split.setHasReturn(true);
- }
- return returnNode;
- }
-
- @Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel());
- return continueNode;
- }
-
- @Override
- public Node enterBreakNode(final BreakNode breakNode) {
- searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel());
- return breakNode;
- }
-
- @Override
- public Node enterSplitNode(final SplitNode splitNode) {
- splitStack.addFirst(splitNode);
- return splitNode;
- }
-
- @Override
- public Node leaveSplitNode(final SplitNode splitNode) {
- assert splitNode == splitStack.peekFirst();
- splitStack.removeFirst();
- return splitNode;
- }
-
- /**
- * Register the split node containing a potential jump target.
- * @param targetNode a potential target node.
- */
- private void registerJumpTarget(final Node targetNode) {
- final SplitNode splitNode = splitStack.peekFirst();
- if (splitNode != null) {
- targetNodes.put(targetNode, splitNode);
- }
- }
-
- /**
- * Check if a jump target is outside the current split node and its parent split nodes.
- * @param targetNode the jump target node.
- * @param targetLabel the jump target label.
- */
- private void searchJumpTarget(final Node targetNode, final Label targetLabel) {
-
- final SplitNode targetSplit = targetNodes.get(targetNode);
- // Note that targetSplit may be null, indicating that targetNode is in top level method.
- // In this case we have to add the external jump target to all split nodes.
-
- for (final SplitNode split : splitStack) {
- if (split == targetSplit) {
- break;
- }
- final List<Label> externalTargets = split.getExternalTargets();
- if (!externalTargets.contains(targetLabel)) {
- split.addExternalTarget(targetLabel);
- }
- }
+ public boolean enterFunctionNode(final FunctionNode node) {
+ //only go into the function node for this splitter. any subfunctions are rejected
+ if (node == outermost && !node.isLazy()) {
+ return true;
}
+ return false;
}
}
diff --git a/src/jdk/nashorn/internal/codegen/WeighNodes.java b/src/jdk/nashorn/internal/codegen/WeighNodes.java
index 18bd9552..002cc902 100644
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java
+++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java
@@ -35,7 +35,6 @@ import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -101,7 +100,7 @@ final class WeighNodes extends NodeOperatorVisitor {
* @param weightCache cache of already calculated block weights
*/
private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
- super(null, null);
+ super();
this.topFunction = topFunction;
this.weightCache = weightCache;
}
@@ -123,13 +122,13 @@ final class WeighNodes extends NodeOperatorVisitor {
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
if (weightCache != null && weightCache.containsKey(block)) {
weight += weightCache.get(block);
- return null;
+ return false;
}
- return block;
+ return true;
}
@Override
@@ -157,12 +156,6 @@ final class WeighNodes extends NodeOperatorVisitor {
}
@Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- weight += LOOP_WEIGHT;
- return doWhileNode;
- }
-
- @Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
return executeNode;
}
@@ -174,15 +167,15 @@ final class WeighNodes extends NodeOperatorVisitor {
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- if(functionNode == topFunction) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ if (functionNode == topFunction) {
// the function being weighted; descend into its statements
- functionNode.visitStatements(this);
- } else {
- // just a reference to inner function from outer function
- weight += FUNC_EXPR_WEIGHT;
+ return true;
+// functionNode.visitStatements(this);
}
- return null;
+ // just a reference to inner function from outer function
+ weight += FUNC_EXPR_WEIGHT;
+ return false;
}
@Override
@@ -205,7 +198,7 @@ final class WeighNodes extends NodeOperatorVisitor {
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
weight += LITERAL_WEIGHT;
if (literalNode instanceof ArrayLiteralNode) {
@@ -224,10 +217,10 @@ final class WeighNodes extends NodeOperatorVisitor {
}
}
- return null;
+ return false;
}
- return literalNode;
+ return true;
}
@Override
@@ -249,9 +242,9 @@ final class WeighNodes extends NodeOperatorVisitor {
}
@Override
- public Node enterSplitNode(final SplitNode splitNode) {
+ public boolean enterSplitNode(final SplitNode splitNode) {
weight += SPLIT_WEIGHT;
- return null;
+ return false;
}
@Override
diff --git a/src/jdk/nashorn/internal/codegen/types/BooleanType.java b/src/jdk/nashorn/internal/codegen/types/BooleanType.java
index c27c0584..4331cb55 100644
--- a/src/jdk/nashorn/internal/codegen/types/BooleanType.java
+++ b/src/jdk/nashorn/internal/codegen/types/BooleanType.java
@@ -93,12 +93,6 @@ public final class BooleanType extends Type {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public void _return(final MethodVisitor method) {
method.visitInsn(IRETURN);
}
diff --git a/src/jdk/nashorn/internal/codegen/types/IntType.java b/src/jdk/nashorn/internal/codegen/types/IntType.java
index 9b859ddd..c282d109 100644
--- a/src/jdk/nashorn/internal/codegen/types/IntType.java
+++ b/src/jdk/nashorn/internal/codegen/types/IntType.java
@@ -241,12 +241,6 @@ class IntType extends BitwiseType {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
assert false : "unsupported operation";
return null;
diff --git a/src/jdk/nashorn/internal/codegen/types/LongType.java b/src/jdk/nashorn/internal/codegen/types/LongType.java
index d3e00b67..b57ea6d3 100644
--- a/src/jdk/nashorn/internal/codegen/types/LongType.java
+++ b/src/jdk/nashorn/internal/codegen/types/LongType.java
@@ -217,12 +217,6 @@ class LongType extends BitwiseType {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
return cmp(method);
}
diff --git a/src/jdk/nashorn/internal/codegen/types/NumberType.java b/src/jdk/nashorn/internal/codegen/types/NumberType.java
index 4dc62c52..6a280b79 100644
--- a/src/jdk/nashorn/internal/codegen/types/NumberType.java
+++ b/src/jdk/nashorn/internal/codegen/types/NumberType.java
@@ -89,12 +89,6 @@ class NumberType extends NumericType {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type ldc(final MethodVisitor method, final Object c) {
assert c instanceof Double;
diff --git a/src/jdk/nashorn/internal/codegen/types/Type.java b/src/jdk/nashorn/internal/codegen/types/Type.java
index 77558835..747793e8 100644
--- a/src/jdk/nashorn/internal/codegen/types/Type.java
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java
@@ -616,6 +616,12 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
return this;
}
+ @Override
+ public Type loadEmpty(final MethodVisitor method) {
+ assert false : "unsupported operation";
+ return null;
+ }
+
/**
* Superclass logic for pop for all types
*
@@ -663,7 +669,6 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
method.visitInsn(SWAP);
}
}
-
}
/**
@@ -841,12 +846,6 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type convert(final MethodVisitor method, final Type to) {
assert false : "unsupported operation";
return null;