Mercurial > people > rkennke > jdk9-shenandoah-final > nashorn
changeset 210:3a209cbd1d8f
8010701: Immutable nodes - final iteration
Reviewed-by: sundar, hannesw, jlaskey
line wrap: on
line diff
--- a/bin/verbose_octane.sh Fri Apr 19 18:23:00 2013 +0530 +++ b/bin/verbose_octane.sh Fri Apr 19 16:11:16 2013 +0200 @@ -26,7 +26,7 @@ ITERS=7 fi NASHORN_JAR=dist/nashorn.jar -JVM_FLAGS="-XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}" +JVM_FLAGS="-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}" JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}" OCTANE_ARGS="--verbose --iterations ${ITERS}"
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Fri Apr 19 16:11:16 2013 +0200 @@ -397,10 +397,7 @@ } setContextVariables(ctxt); - final Object val = ctxt.getAttribute(ScriptEngine.FILENAME); - final String fileName = (val != null) ? val.toString() : "<eval>"; - Object res = ScriptRuntime.apply(script, ctxtGlobal); - return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal)); + return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal)); } catch (final Exception e) { throwAsScriptException(e); throw new AssertionError("should not reach here");
--- a/src/jdk/nashorn/internal/codegen/Attr.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/Attr.java Fri Apr 19 16:11:16 2013 +0200 @@ -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,20 @@ 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.IdentityHashMap; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; +import java.util.Map; 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 +66,7 @@ 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; @@ -86,7 +91,6 @@ import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.PropertyMap; -import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; /** @@ -105,21 +109,24 @@ */ 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 final Map<Symbol, FunctionNode> selfSymbolToFunction = new IdentityHashMap<>(); private static final DebugLogger LOG = new DebugLogger("attr"); private static final boolean DEBUG = LOG.isEnabled(); @@ -128,10 +135,13 @@ * 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,217 +152,44 @@ @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); - - final Set<String> savedLocalDefs = localDefs; - final Set<String> savedLocalUses = localUses; - - block.setFrame(getCurrentFunctionNode().pushFrame()); - - 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; - - getCurrentFunctionNode().popFrame(); - } - - end(block); - - lexicalContext.pop(block); - return null; - } - - @Override - public Node enterCallNode(final CallNode callNode) { - start(callNode); - - callNode.getFunction().accept(this); - - 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); + private void enterFunctionBody() { - final EvalArgs evalArgs = callNode.getEvalArgs(); - if (evalArgs != null) { - evalArgs.setCode(evalArgs.getCode().accept(this)); - - final IdentNode thisNode = new IdentNode(getCurrentFunctionNode().getThisNode()); - assert thisNode.getSymbol() != null; //should copy attributed symbol and that's it - evalArgs.setThis(thisNode); - } - - newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later - - end(callNode); - - return null; - } - - @Override - public Node enterCatchNode(final CatchNode catchNode) { - final IdentNode exception = catchNode.getException(); - final Block block = getCurrentBlock(); - - start(catchNode); - - // define block-local exception variable - final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception); - newType(def, Type.OBJECT); - addLocalDef(exception.getName()); - - return catchNode; - } - - /** - * Declare the definition of a new symbol. - * - * @param name Name of symbol. - * @param symbolFlags Symbol flags. - * @param node Defining Node. - * - * @return Symbol for given name or null for redefinition. - */ - private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) { - int flags = symbolFlags; - Symbol symbol = findSymbol(block, name); // Locate symbol. - - if ((flags & KINDMASK) == IS_GLOBAL) { - flags |= IS_SCOPE; + final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); + final Block body = getLexicalContext().getCurrentBlock(); + initCallee(body); + initThis(body); + if (functionNode.isVarArg()) { + initVarArg(body, functionNode.needsArguments()); } - final FunctionNode function = lexicalContext.getFunction(block); - if (symbol != null) { - // Symbol was already defined. Check if it needs to be redefined. - if ((flags & KINDMASK) == IS_PARAM) { - if (!isLocal(function, symbol)) { - // Not defined in this function. Create a new definition. - symbol = null; - } else if (symbol.isParam()) { - // Duplicate parameter. Null return will force an error. - assert false : "duplicate parameter"; - return null; - } - } 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 { - // Not defined in this function. Create a new definition. - if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { - symbol = null; - } - } - } - } - - if (symbol == null) { - // If not found, then create a new one. - Block symbolBlock; - - // Determine where to create it. - if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { - symbolBlock = block; - } else { - symbolBlock = function; - } - - // Create and add to appropriate block. - symbol = new Symbol(name, flags, node, symbolBlock); - symbolBlock.putSymbol(name, symbol); - - if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { - symbolBlock.getFrame().addSymbol(symbol); - symbol.setNeedsSlot(true); - } - } else if (symbol.less(flags)) { - symbol.setFlags(flags); - } - - if (node != null) { - node.setSymbol(symbol); - } - - return symbol; - } - - @Override - public Node 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)); - } - } + initParameters(functionNode, body); + initScope(body); + initReturn(body); if (functionNode.isProgram()) { - initFromPropertyMap(functionNode); + initFromPropertyMap(body); } // Add function name as local symbol if (!functionNode.isDeclared() && !functionNode.isProgram()) { - if(functionNode.getSymbol() != null) { + 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); + if (functionNode.isAnonymous()) { + selfSymbol = ensureSymbol(functionNode, Type.OBJECT, functionNode); } else { - selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode); + selfSymbol = defineSymbol(body, functionNode.getIdent().getName(), IS_VAR | IS_FUNCTION_SELF, functionNode); newType(selfSymbol, Type.OBJECT); - selfSymbol.setNode(functionNode); + selfSymbolToFunction.put(selfSymbol, functionNode); } } @@ -373,73 +210,243 @@ * @see NASHORN-73 */ - 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() { + body.accept(new NodeOperatorVisitor() { @Override - public Node enterFunctionNode(FunctionNode nestedFn) { - // Don't descend into nested functions - return nestedFn == functionNode ? nestedFn : null; + public boolean enterFunctionNode(final FunctionNode nestedFn) { + return false; } + @Override - public Node enterVarNode(VarNode varNode) { - if(varNode.isStatement() && !varNode.isFunctionDeclaration()) { + public boolean enterVarNode(final VarNode varNode) { + + // any declared symbols that aren't visited need to be typed as well, hence the list + + if (varNode.isStatement()) { + 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))); + final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident)); + functionNode.addDeclaredSymbol(symbol); + if (varNode.isFunctionDeclaration()) { + newType(symbol, FunctionNode.FUNCTION_TYPE); + } } - return null; + return false; } }); + } - visitFunctionStatements(functionNode); + @Override + public boolean enterBlock(final Block block) { + start(block); + + if (getLexicalContext().isFunctionBody()) { + enterFunctionBody(); + } + pushLocalsBlock(); + + return true; + } + + @Override + public Node leaveBlock(final Block block) { + popLocals(); + return end(block); + } + + @Override + public Node leaveCallNode(final CallNode callNode) { + ensureSymbol(callNode.getType(), callNode); + return end(callNode); + } + + @Override + public boolean enterCallNode(final CallNode callNode) { + return start(callNode); + } + + @Override + public boolean enterCatchNode(final CatchNode catchNode) { + final IdentNode exception = catchNode.getException(); + final Block block = getLexicalContext().getCurrentBlock(); + + start(catchNode); + + // define block-local exception variable + final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception); + newType(def, Type.OBJECT); + addLocalDef(exception.getName()); + + return true; + } + + /** + * Declare the definition of a new symbol. + * + * @param name Name of symbol. + * @param symbolFlags Symbol flags. + * @param node Defining Node. + * + * @return Symbol for given name or null for redefinition. + */ + private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) { + int flags = symbolFlags; + Symbol symbol = findSymbol(block, name); // Locate symbol. + + if ((flags & KINDMASK) == IS_GLOBAL) { + flags |= IS_SCOPE; + } + + 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) { + if (!isLocal(function, symbol)) { + // Not defined in this function. Create a new definition. + symbol = null; + } else if (symbol.isParam()) { + // Duplicate parameter. Null return will force an error. + assert false : "duplicate parameter"; + return null; + } + } else if ((flags & KINDMASK) == IS_VAR) { + if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) { + // Always create a new definition. + symbol = null; + } else { + // Not defined in this function. Create a new definition. + if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { + symbol = null; + } + } + } + } + + if (symbol == null) { + // If not found, then create a new one. + Block symbolBlock; + + // Determine where to create it. + if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { + symbolBlock = block; //internal vars are always defined in the block closest to them + } else { + symbolBlock = getLexicalContext().getFunctionBody(function); + } + + // Create and add to appropriate block. + symbol = new Symbol(name, flags); + symbolBlock.putSymbol(name, symbol); + + if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { + symbol.setNeedsSlot(true); + } + } else if (symbol.less(flags)) { + symbol.setFlags(flags); + } + + if (node != null) { + node.setSymbol(symbol); + } + + return symbol; + } + + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + start(functionNode, false); + + 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.isLazy()) { + LOG.info("LAZY: ", functionNode.getName(), " => Promoting to OBJECT"); + ensureSymbol(getLexicalContext().getCurrentFunction(), Type.OBJECT, functionNode); + end(functionNode); + return false; + } + + returnTypes.push(functionNode.getReturnType()); + pushLocalsFunction(); + return true; + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + FunctionNode newFunctionNode = 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); + + LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName()); + + final List<Node> newStatements = new ArrayList<>(); + newStatements.add(selfInit); + assert callee.getSymbol() != null && callee.getSymbol().hasSlot(); + + final IdentNode name = selfInit.getName(); + final Symbol nameSymbol = body.getExistingSymbol(name.getName()); + + assert nameSymbol != null; + + name.setSymbol(nameSymbol); + selfInit.setSymbol(nameSymbol); + + newStatements.addAll(body.getStatements()); + newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements)); } - functionNode.popFrame(); - - functionNode.setState(CompilationState.ATTR); - - end(functionNode, false); - lexicalContext.pop(functionNode); + if (returnTypes.peek().isUnknown()) { + LOG.info("Unknown return type promoted to object"); + newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT); + } + final Type returnType = returnTypes.pop(); + newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType); + newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR); - return null; - } + popLocals(); - 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)); - } - functionNode.setStatements(newStatements); + end(newFunctionNode, false); + + return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode)); } @Override @@ -450,7 +457,7 @@ } @Override - public Node enterIdentNode(final IdentNode identNode) { + public boolean enterIdentNode(final IdentNode identNode) { final String name = identNode.getName(); start(identNode); @@ -458,31 +465,28 @@ 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); @@ -492,25 +496,19 @@ 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 (!isLocal(lc.getCurrentFunction(), symbol) && !symbol.isScope()) { + Symbol.setSymbolIsScope(lc, 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,7 +517,37 @@ end(identNode); - return null; + return false; + } + + private void setBlockScope(final String name, final Symbol symbol) { + assert symbol != null; + if (symbol.isGlobal()) { + setUsesGlobalSymbol(); + return; + } + + 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); + } + } } /** @@ -528,35 +556,12 @@ * @see #needsParentScope() */ private void setUsesGlobalSymbol() { - for(final Iterator<FunctionNode> fns = lexicalContext.getFunctions(); fns.hasNext();) { - fns.next().setUsesAncestorScope(); + for (final Iterator<FunctionNode> fns = getLexicalContext().getFunctions(); fns.hasNext();) { + getLexicalContext().setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE); } } /** - * 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); - - if (symbol == null) { - // If not found, declare as a free var. - symbol = defineSymbol(block, name, IS_GLOBAL, node); - } else { - node.setSymbol(symbol); - } - - return symbol; - } - - - /** * Search for symbol in the lexical context starting from the given block. * @param name Symbol name. * @return Found symbol or null if not found. @@ -564,7 +569,7 @@ 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 +582,13 @@ @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 +609,33 @@ 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 +648,10 @@ 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 +663,29 @@ 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 +693,11 @@ 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 +714,25 @@ } @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 +752,7 @@ 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 +769,14 @@ @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 +784,29 @@ @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 +814,7 @@ 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 +842,62 @@ 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 +909,20 @@ @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 +939,7 @@ ensureTypeNotUnknown(lhs); ensureTypeNotUnknown(rhs); - newTemporary(Type.widest(lhs.getType(), rhs.getType()), binaryNode); + ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode); end(binaryNode); @@ -912,7 +948,7 @@ @Override public Node leaveAND(final BinaryNode binaryNode) { - newTemporary(Type.OBJECT, binaryNode); + ensureSymbol(Type.OBJECT, binaryNode); end(binaryNode); return binaryNode; } @@ -921,39 +957,40 @@ * 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 IdentNode ident = (IdentNode)lhs; - final String name = ident.getName(); + final LexicalContext lc = getLexicalContext(); + final Block block = lc.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 if (!isLocal(lc.getCurrentFunction(), symbol)) { + Symbol.setSymbolIsScope(lc, 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 +1000,7 @@ } @Override - public Node enterASSIGN_ADD(final BinaryNode binaryNode) { + public boolean enterASSIGN_ADD(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -978,7 +1015,7 @@ } @Override - public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) { + public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -988,7 +1025,7 @@ } @Override - public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) { + public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -998,7 +1035,7 @@ } @Override - public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { + public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1008,7 +1045,7 @@ } @Override - public Node enterASSIGN_DIV(final BinaryNode binaryNode) { + public boolean enterASSIGN_DIV(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1018,7 +1055,7 @@ } @Override - public Node enterASSIGN_MOD(final BinaryNode binaryNode) { + public boolean enterASSIGN_MOD(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1028,7 +1065,7 @@ } @Override - public Node enterASSIGN_MUL(final BinaryNode binaryNode) { + public boolean enterASSIGN_MUL(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1038,7 +1075,7 @@ } @Override - public Node enterASSIGN_SAR(final BinaryNode binaryNode) { + public boolean enterASSIGN_SAR(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1048,7 +1085,7 @@ } @Override - public Node enterASSIGN_SHL(final BinaryNode binaryNode) { + public boolean enterASSIGN_SHL(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1058,7 +1095,7 @@ } @Override - public Node enterASSIGN_SHR(final BinaryNode binaryNode) { + public boolean enterASSIGN_SHR(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1068,7 +1105,7 @@ } @Override - public Node enterASSIGN_SUB(final BinaryNode binaryNode) { + public boolean enterASSIGN_SUB(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1094,13 +1131,13 @@ @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 +1150,7 @@ final Node lhs = binaryNode.lhs(); final Node rhs = binaryNode.rhs(); - newTemporary(Type.BOOLEAN, binaryNode); + ensureSymbol(Type.BOOLEAN, binaryNode); ensureTypeNotUnknown(lhs); ensureTypeNotUnknown(rhs); @@ -1131,7 +1168,7 @@ //newType(binaryNode.lhs().getSymbol(), operandType); //newType(binaryNode.rhs().getSymbol(), operandType); - newTemporary(destType, binaryNode); + ensureSymbol(destType, binaryNode); return binaryNode; } @@ -1216,7 +1253,7 @@ @Override public Node leaveOR(final BinaryNode binaryNode) { - newTemporary(Type.OBJECT, binaryNode); + ensureSymbol(Type.OBJECT, binaryNode); end(binaryNode); return binaryNode; } @@ -1244,7 +1281,7 @@ @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 +1304,50 @@ 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); + 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 (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); - } + 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 +1356,19 @@ * * @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 +1394,7 @@ // 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 +1408,18 @@ /** * 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 +1427,7 @@ 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 +1467,7 @@ } 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 +1527,15 @@ } 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 +1589,7 @@ } 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 +1604,25 @@ 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 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 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 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 +1632,51 @@ 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 +1684,28 @@ * 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 +1719,11 @@ 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 +1732,13 @@ 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 +1754,7 @@ append("] "). append(printNode ? node.toString() : ""). append(" in '"). - append(getCurrentFunctionNode().getName()); + append(getLexicalContext().getCurrentFunction().getName()); if (node.getSymbol() == null) { sb.append(" <NO SYMBOL>"); @@ -1746,7 +1763,7 @@ } LOG.unindent(); - LOG.info(sb.toString()); + LOG.info(sb); } return node;
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java Fri Apr 19 16:11:16 2013 +0200 @@ -58,12 +58,14 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ */ 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 @@ 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 @@ * @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 @@ * @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 @@ * @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 @@ * @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 @@ * @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 @@ 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); + } + }
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Apr 19 16:11:16 2013 +0200 @@ -27,14 +27,18 @@ 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.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.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; @@ -85,6 +91,7 @@ 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,6 +114,7 @@ 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.Property; @@ -157,14 +165,29 @@ /** 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; @@ -188,7 +211,37 @@ * @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 +270,7 @@ 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. @@ -237,11 +290,11 @@ * @return true if fast scope */ private boolean isFastScope(final Symbol symbol) { - if (!symbol.isScope() || !symbol.getBlock().needsScope()) { + if (!symbol.isScope() || !getLexicalContext().getDefiningBlock(symbol).needsScope()) { return false; } // Allow fast scope access if no function contains with or eval - for(final Iterator<FunctionNode> it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) { + for (final Iterator<FunctionNode> it = getLexicalContext().getFunctions(); it.hasNext();) { final FunctionNode func = it.next(); if (func.hasWith() || func.hasEval()) { return false; @@ -251,7 +304,7 @@ } 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 +324,10 @@ 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 +338,9 @@ } 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 +381,46 @@ */ 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 +428,9 @@ } @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 +455,7 @@ 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 +489,20 @@ * @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 +510,10 @@ method.label(block.getBreakLabel()); symbolInfo(block); - if (block.needsScope()) { + if (block.needsScope() && !block.isTerminal()) { popBlockScope(block); } - lexicalContext.pop(block); + --nextFreeSlotsSize; return block; } @@ -477,34 +523,30 @@ 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(breakNode.getTargetLabel()); - - return null; + method.splitAwareGoto(getLexicalContext(), breakFrom.getBreakLabel()); + + return false; } private int loadArgs(final List<Node> args) { @@ -541,21 +583,17 @@ } @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(); - - function.accept(new NodeVisitor(getCurrentCompileUnit(), method) { + final Block currentBlock = getLexicalContext().getCurrentBlock(); + + 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 +629,7 @@ // 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 +656,7 @@ } @Override - public Node enterIdentNode(final IdentNode node) { + public boolean enterIdentNode(final IdentNode node) { final Symbol symbol = node.getSymbol(); if (symbol.isScope()) { @@ -637,16 +675,16 @@ } 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 +693,34 @@ 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 +734,11 @@ 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 +746,41 @@ 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(continueNode.getTargetLabel()); - - return null; + method.splitAwareGoto(getLexicalContext(), continueTo.getContinueLabel()); + + return false; } @Override - public Node enterDoWhileNode(final DoWhileNode doWhileNode) { - return enterWhileNode(doWhileNode); - } - - @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 +810,10 @@ 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 +853,19 @@ 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 +874,26 @@ * @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(); - - /* Fix the predefined slots so they have numbers >= 0, like varargs. */ - frame.realign(); - - if (isFunctionNode) { + final boolean isFunctionBody = getLexicalContext().isFunctionBody(); + + 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); + + final FunctionNode function = getLexicalContext().getCurrentFunction(); + if (isFunctionBody) { + /* Fix the predefined slots so they have numbers >= 0, like varargs. */ if (function.needsParentScope()) { initParentScope(); } @@ -876,14 +917,18 @@ 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 +952,6 @@ } } - /* Correct slot numbering again */ - frame.realign(); - // we may have locals that need to be initialized initSymbols(locals); @@ -931,7 +973,7 @@ @Override protected void loadScope(MethodEmitter m) { if(function.needsParentScope()) { - m.loadScope(); + m.loadCompilerConstant(SCOPE); } else { m.loadNull(); } @@ -940,118 +982,102 @@ 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()) { + // Must do it now; can't postpone it until leaveFunctionNode() + newFunctionObject(functionNode, functionNode); + return false; } - if (functionNode.isLazy()) { - return null; - } - - LOG.info("=== BEGIN " + functionNode.getName()); - lexicalContext.push(functionNode); - - setCurrentCompileUnit(functionNode.getCompileUnit()); - assert getCurrentCompileUnit() != null; - - setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode)); - functionNode.setMethodEmitter(method); + LOG.info("=== BEGIN ", functionNode.getName()); + + assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode); + push(functionNode.getCompileUnit()); + assert !compileUnits.isEmpty(); + + 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 +1108,21 @@ 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("line:" + lineNumberNode.getLineNumber() + " (" + getLexicalContext().getCurrentFunction().getName() + ")"); method.label(label); method.lineNumber(lineNumberNode.getLineNumber(), label); - return null; + return false; } /** @@ -1131,43 +1148,43 @@ final Type elementType = arrayType.getElementType(); if (units != null) { - final CompileUnit savedCompileUnit = getCurrentCompileUnit(); - final MethodEmitter savedMethod = getCurrentMethodEmitter(); - - try { - for (final ArrayUnit unit : units) { - setCurrentCompileUnit(unit.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); - - setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature)); - method.setFunctionNode(getCurrentFunctionNode()); - method.begin(); - - fixScopeSlot(); - - method.load(arrayType, SPLIT_ARRAY_ARG.slot()); - - for (int i = unit.getLo(); i < unit.getHi(); i++) { - storeElement(nodes, elementType, postsets[i]); - } - - method._return(); - method.end(); - - savedMethod.loadThis(); - savedMethod.swap(); - savedMethod.loadCallee(); - savedMethod.swap(); - savedMethod.loadScope(); - savedMethod.swap(); - savedMethod.invokestatic(className, name, signature); + final MethodEmitter savedMethod = method; + + for (final ArrayUnit arrayUnit : units) { + push(arrayUnit.getCompileUnit()); + + 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); + + final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature); + pushMethodEmitter(me); + + method.setFunctionNode(getLexicalContext().getCurrentFunction()); + method.begin(); + + fixScopeSlot(); + + method.load(arrayType, SPLIT_ARRAY_ARG.slot()); + + 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 +1234,12 @@ * @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 +1250,14 @@ * @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 +1320,14 @@ 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 +1335,7 @@ 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 +1345,14 @@ @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 +1417,14 @@ 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 +1449,25 @@ 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"; - - method.loadScope(); - method.checkcast(Scope.class); - method.load(0); - method.invoke(Scope.SET_SPLIT_STATE); - } + public boolean enterReturnNode(final ReturnNode returnNode) { + method.registerReturn(); + + 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()); - - return null; + method._return(returnType); + + return false; } private static boolean isNullLiteral(final Node node) { @@ -1542,19 +1545,20 @@ } 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 +1585,7 @@ } @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 +1624,7 @@ 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 +1636,11 @@ 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 +1658,146 @@ 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(); - - if (splitNode.hasReturn() || targetCount > 0) { - - caller.loadScope(); - caller.checkcast(Scope.class); - caller.invoke(Scope.GET_SPLIT_STATE); - - // 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 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); + + 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 + + //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(); + + 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 +1805,7 @@ } @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 +1823,7 @@ if (cases.isEmpty()) { method.label(breakLabel); - return null; + return false; } if (allInteger) { @@ -1916,15 +1929,11 @@ 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 +1952,11 @@ 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 +1979,68 @@ 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); - - 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; + //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(); + + new Store<IdentNode>(exception) { + @Override + protected void storeNonDiscard() { + return; } - - 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; - - if (exceptionCondition != null) { - next = new Label("next"); - load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next); + @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; + + if (exceptionCondition != null) { + next = new Label("next"); + load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next); + } else { + next = null; + } + + catchBody.accept(this); + + if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) { + method._goto(skip); + } + + 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 { - next = null; - } - - catchBody.accept(this); - - if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) { - method._goto(skip); + 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 +2048,15 @@ // 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 +2066,7 @@ final boolean needsScope = varSymbol.isScope(); if (needsScope) { - method.loadScope(); + method.loadCompilerConstant(SCOPE); } load(init); @@ -2087,22 +2086,18 @@ 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,21 +2109,17 @@ method.label(breakLabel); } - return null; + return false; } private void closeWith() { - method.loadScope(); + method.loadCompilerConstant(SCOPE); method.invoke(ScriptRuntime.CLOSE_WITH); - method.storeScope(); + 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(); @@ -2139,13 +2130,13 @@ method.label(tryLabel); - method.loadScope(); + method.loadCompilerConstant(SCOPE); load(expression); assert expression.getType().isObject() : "with expression needs to be object: " + expression; method.invoke(ScriptRuntime.OPEN_WITH); - method.storeScope(); + method.storeCompilerConstant(SCOPE); body.accept(this); @@ -2164,40 +2155,27 @@ method._try(tryLabel, endLabel, catchLabel); - return null; + 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 +2208,11 @@ 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 +2256,28 @@ } }.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 +2287,11 @@ 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 +2308,14 @@ 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 +2328,7 @@ } @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 +2342,10 @@ 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 +2364,16 @@ 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 +2392,7 @@ } }.store(); - return null; + return false; } /** @@ -2473,14 +2423,6 @@ this.opType = opType; } - @Override - public void store() { - if (assignNode.testResolved()) { - return; - } - super.store(); - } - protected abstract void op(); @Override @@ -2493,35 +2435,43 @@ } @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 +2479,11 @@ } }.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 +2491,11 @@ } }.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 +2503,11 @@ } }.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 +2515,11 @@ } }.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 +2527,11 @@ } }.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 +2539,11 @@ } }.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 +2551,11 @@ } }.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,11 +2563,11 @@ } }.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() { @@ -2626,11 +2576,11 @@ } }.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 +2588,7 @@ } }.store(); - return null; + return false; } /** @@ -2649,9 +2599,6 @@ protected abstract void op(); protected void evaluate(final BinaryNode node) { - if (node.testResolved()) { - return; - } load(node.lhs()); load(node.rhs()); op(); @@ -2660,7 +2607,7 @@ } @Override - public Node enterBIT_AND(final BinaryNode binaryNode) { + public boolean enterBIT_AND(final BinaryNode binaryNode) { new BinaryArith() { @Override protected void op() { @@ -2668,11 +2615,11 @@ } }.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 +2627,11 @@ } }.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 +2639,10 @@ } }.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 +2650,21 @@ 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 +2672,10 @@ } }.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 +2701,45 @@ 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 +2747,11 @@ } }.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 +2759,26 @@ } }.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 +2786,11 @@ } }.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,11 +2798,11 @@ } }.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() { @@ -2871,11 +2811,11 @@ } }.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 +2823,11 @@ } }.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 +2859,7 @@ method.label(exitLabel); method.store(symbol); - return null; + return false; } /** @@ -2955,7 +2888,7 @@ 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 +2907,7 @@ 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 +2970,6 @@ /** 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 +2985,6 @@ protected Store(final T assignNode, final Node target) { this.assignNode = assignNode; this.target = target; - this.alwaysDiscard = assignNode == target; } /** @@ -3077,21 +3006,21 @@ 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 +3038,13 @@ } @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 +3060,14 @@ 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 +3080,28 @@ * @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 +3126,22 @@ */ 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 +3153,20 @@ } 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 +3185,23 @@ 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 +3213,7 @@ loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap())); if (isLazy || functionNode.needsParentScope()) { - m.loadScope(); + m.loadCompilerConstant(SCOPE); } else { m.loadNull(); }
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Fri Apr 19 16:11:16 2013 +0200 @@ -15,6 +15,7 @@ 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.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 @@ */ 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 @@ }, /* - * 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 @@ }, /* - * 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 @@ }, /* - * 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 @@ }, /* - * 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 @@ /* * 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 @@ /* * 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 @@ 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 @@ 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 @@ 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 @@ 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))); } }
--- a/src/jdk/nashorn/internal/codegen/Compiler.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Fri Apr 19 16:11:16 2013 +0200 @@ -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.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 @@ private final ConstantData constantData; - private final FunctionNode functionNode; - private final CompilationSequence sequence; private final ScriptEnvironment env; @@ -90,6 +91,8 @@ private boolean strict; + private FunctionNode functionNode; + private CodeInstaller<ScriptEnvironment> installer; /** logger for compiler, trampolines, splits and related code generation events @@ -103,8 +106,12 @@ * 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 @@ 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 @@ 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 @@ * 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 @@ * 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 @@ 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 @@ append(" ms "); } - LOG.fine(sb.toString()); + LOG.fine(sb); } } @@ -311,14 +318,14 @@ 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 @@ @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 @@ 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 @@ } if (sb != null) { - LOG.info(sb.toString()); + LOG.info(sb); } return rootClass; @@ -495,7 +491,7 @@ 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; }
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java Fri Apr 19 16:11:16 2013 +0200 @@ -52,9 +52,6 @@ /** lazy prefix for classes of jitted methods */ LAZY("Lazy"), - /** leaf tag used for functions that require no scope */ - LEAF("__leaf__"), - /** constructor name */ INIT("<init>"), @@ -96,7 +93,7 @@ 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), @@ -167,30 +164,30 @@ /** 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 @@ * * @return the tag */ - public final String tag() { - return tag; + public final String symbolName() { + return symbolName; } /** @@ -277,7 +274,7 @@ * @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 @@ * @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 @@ * @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); } /**
--- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Fri Apr 19 16:11:16 2013 +0200 @@ -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 @@ * @param method the method emitter to use */ protected void loadScope(final MethodEmitter method) { - method.loadScope(); + method.loadCompilerConstant(SCOPE); } /** @@ -105,7 +106,7 @@ 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));
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,18 +25,22 @@ 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; 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 +89,11 @@ 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 +130,7 @@ @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 +250,7 @@ @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 +350,30 @@ } @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 +382,54 @@ final Node test = forNode.getTest(); final Node modify = forNode.getModify(); - if (forNode.isForIn()) { - forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 - return forNode; - } - - if (init != null) { - forNode.setInit(discard(init)); - } + final LexicalContext lc = getLexicalContext(); - if (test != null) { - forNode.setTest(convert(test, Type.BOOLEAN)); - } else { - assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode(); + 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(); - if (modify != null) { - forNode.setModify(discard(modify)); - } - - 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 +443,14 @@ } } - 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 +466,24 @@ @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(); + final List<CaseNode> newCases = new ArrayList<>(); - 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)); - } - } + 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 +493,7 @@ @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 +516,24 @@ 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 +547,28 @@ 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 +608,7 @@ //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 +635,8 @@ } 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 +652,28 @@ 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 +754,12 @@ 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 +783,8 @@ 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 +811,23 @@ 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 +852,7 @@ 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); } }
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java Fri Apr 19 16:11:16 2013 +0200 @@ -57,7 +57,7 @@ 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 @@ 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
--- a/src/jdk/nashorn/internal/codegen/Frame.java Fri Apr 19 18:23:00 2013 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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); - } - }
--- a/src/jdk/nashorn/internal/codegen/Lower.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Fri Apr 19 16:11:16 2013 +0200 @@ -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.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,356 +82,167 @@ 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 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; + } + }); + } + + @Override + 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 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(); - } - } - block.setStatements(statements); + 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 + + final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext(); + + Node last = lc.getLastStatement(); - } finally { - this.statements = savedStatements; - this.lastStatement = savedLastStatement; - lexicalContext.pop(block); + 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); } - return null; + if (last != null && last.isTerminal()) { + return block.setIsTerminal(lc, true); + } + + return block; } @Override - public Node enterBreakNode(final BreakNode breakNode) { - return enterBreakOrContinue(breakNode); + public boolean enterBreakNode(final BreakNode breakNode) { + addStatement(breakNode); + return false; } @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; - } - - @Override - public Node leaveCaseNode(final CaseNode caseNode) { - caseNode.copyTerminalFlags(caseNode.getBody()); - return caseNode; + public Node leaveCallNode(final CallNode callNode) { + return checkEval(callNode.setFunction(markerFunction(callNode.getFunction()))); } @Override public Node leaveCatchNode(final CatchNode catchNode) { - catchNode.copyTerminalFlags(catchNode.getBody()); - addStatement(catchNode); - return catchNode; - } - - @Override - public Node enterContinueNode(final ContinueNode continueNode) { - return enterBreakOrContinue(continueNode); + return addStatement(catchNode); } @Override - public Node enterDoWhileNode(final DoWhileNode doWhileNode) { - return enterWhileNode(doWhileNode); + public boolean enterContinueNode(final ContinueNode continueNode) { + addStatement(continueNode); + return false; } @Override - public Node leaveDoWhileNode(final DoWhileNode doWhileNode) { - return leaveWhileNode(doWhileNode); - } - - @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; - if (getCurrentFunctionNode().isProgram()) { + final FunctionNode currentFunction = getLexicalContext().getCurrentFunction(); + + 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(); + ForNode newForNode = forNode; - if (!forNode.isForIn() && test == null) { - setHasGoto(forNode); - } - - final boolean escapes = controlFlowEscapes(body); - if (escapes) { - setTerminal(body, false); + final Node test = forNode.getTest(); + if (!forNode.isForIn() && conservativeAlwaysTrue(test)) { + newForNode = forNode.setTest(getLexicalContext(), null); } - // pop the loop from the loop context - unnest(forNode); - - if (!forNode.isForIn() && conservativeAlwaysTrue(test)) { - forNode.setTest(null); - setHasGoto(forNode); - setTerminal(forNode, !escapes); - } - - 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; - - statements = new ArrayList<>(); - lastStatement = null; - - if (functionNode.needsSelfSymbol()) { - //function needs to start with var funcIdent = __callee_; - statements.add(functionNode.getSelfSymbolInit().accept(this)); - } - - 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); - - if (!functionNode.isTerminal()) { - guaranteeReturn(functionNode); - } - } finally { - statements = savedStatements; - lastStatement = savedLastStatement; - } - - LOG.info("END FunctionNode: " + functionNode.getName()); - unnest(functionNode); - lexicalContext.pop(functionNode); - - functionNode.setState(CompilationState.LOWERED); - - return null; - } - - /** - * 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); - } - } - - private void addInitialEvalResult(final FunctionNode functionNode) { - new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(), - getInitialEvalResult(functionNode)).accept(this); - } - - /** - * 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; - } - } - return lastFnName != null ? new IdentNode(lastFnName) : LiteralNode.newInstance(program, ScriptRuntime.UNDEFINED); + public boolean enterFunctionNode(final FunctionNode functionNode) { + return !functionNode.isLazy(); } @Override - public Node enterIfNode(final IfNode ifNode) { - return nest(ifNode); + public Node leaveFunctionNode(final FunctionNode functionNode) { + LOG.info("END FunctionNode: ", functionNode.getName()); + return functionNode.setState(getLexicalContext(), CompilationState.LOWERED); } @Override public Node leaveIfNode(final IfNode ifNode) { - final Node pass = ifNode.getPass(); - final Node fail = ifNode.getFail(); - - if (pass.isTerminal() && fail != null && fail.isTerminal()) { - setTerminal(ifNode, true); - } - - addStatement(ifNode); - unnest(ifNode); - - return ifNode; - } - - @Override - public Node enterLabelNode(LabelNode labelNode) { - final Block body = labelNode.getBody(); - body.accept(this); - copyTerminal(labelNode, body); - addStatement(labelNode); - return null; - } - - @Override - public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { - addStatement(lineNumberNode, false); // don't put it in lastStatement cache - return null; + return addStatement(ifNode); } @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); + public Node leaveLabelNode(final LabelNode labelNode) { + return addStatement(labelNode); + } - //splice in the finally code, inlining it here - if (copyFinally(tryNode, null)) { - return null; - } - - //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)); - } - - addStatement(returnNode); - - return null; + @Override + public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) { + addStatement(lineNumberNode); // don't put it in lastStatement cache + return false; } @Override @@ -448,31 +251,10 @@ return returnNode; } - @Override - public Node enterSwitchNode(final SwitchNode switchNode) { - nest(switchNode); - return switchNode; - } @Override public Node leaveSwitchNode(final SwitchNode switchNode) { - unnest(switchNode); - - final List<CaseNode> cases = switchNode.getCases(); - final CaseNode defaultCase = switchNode.getDefaultCase(); - - boolean allTerminal = !cases.isEmpty(); - for (final CaseNode caseNode : switchNode.getCases()) { - allTerminal &= caseNode.isTerminal(); - } - - if (allTerminal && defaultCase != null && defaultCase.isTerminal()) { - setTerminal(switchNode, true); - } - - addStatement(switchNode); - - return switchNode; + return addStatement(switchNode); } @Override @@ -481,208 +263,234 @@ return throwNode; } - @Override - public Node enterTryNode(final TryNode tryNode) { - final Block finallyBody = tryNode.getFinallyBody(); - final long token = tryNode.getToken(); - final int finish = tryNode.getFinish(); + private static Node ensureUniqueLabelsIn(final Node node) { + return node.accept(new NodeVisitor() { + @Override + public Node leaveDefault(final Node labelledNode) { + return labelledNode.ensureUniqueLabels(getLexicalContext()); + } + }); + } + + 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 newStatements; + } + + private Block catchAllBlock(final TryNode tryNode) { + final Source source = tryNode.getSource(); + final long token = tryNode.getToken(); + final int finish = tryNode.getFinish(); + + final IdentNode exception = new IdentNode(source, token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all")); + + final Block catchBody = new Block(source, token, finish, new ThrowNode(source, token, finish, new IdentNode(exception))). + setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal + + final CatchNode catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody); + final Block catchAllBlock = new Block(source, token, finish, catchAllNode); + + //catchallblock -> catchallnode (catchnode) -> exception -> throw + + return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower + } + + private IdentNode compilerConstant(final CompilerConstants cc) { + final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); + return new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName()); + } + + private static boolean isTerminal(final List<Node> statements) { + return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags(); + } + + /** + * 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(); + + assert tryNode.getFinallyBody() == null; + + final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() { + final List<Node> insideTry = new ArrayList<>(); - nest(tryNode); + @Override + public boolean enterDefault(final Node node) { + insideTry.add(node); + return true; + } + + @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 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 leaveBreakNode(final BreakNode breakNode) { + return copy(breakNode, Lower.this.getLexicalContext().getBreakable(breakNode.getLabel())); + } + + @Override + public Node leaveContinueNode(final ContinueNode continueNode) { + return copy(continueNode, Lower.this.getLexicalContext().getContinueTo(continueNode.getLabel())); + } - if (finallyBody == null) { - //do nothing if no finally exists - return tryNode; + @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; + } + + newStatements.addAll(copyFinally(finallyBody)); + if (!isTerminal(newStatements)) { + newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode)); + } + + return new ExecuteNode(new Block(source, returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements)); + } + + 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; + } + }); + + addStatement(newTryNode); + for (final Node statement : finallyBody.getStatements()) { + addStatement(statement); } - /* - * We have a finally clause. - * - * Transform to do finally tail duplication as follows: - * - * <pre> - * try { - * try_body - * } catch e1 { - * catchbody_1 - * } - * ... - * } catch en { - * catchbody_n - * } finally { - * finally_body - * } - * - * (where e1 ... en are optional) - * - * 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> - * - * 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 - - final Block catchBody = new Block(source, token, finish); - final Node catchAllFinally = finallyBody.copy(); - - 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(); - - catchBlock.addStatement(catchAllNode); - - // replace all catches of outer tryNode with the catch-all - tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock))); - - /* - * 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 - */ - - return tryNode; + return newTryNode; } @Override public Node leaveTryNode(final TryNode tryNode) { - final Block finallyBody = tryNode.getFinallyBody(); + final Block finallyBody = tryNode.getFinallyBody(); - boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal()); - - for (final Block catchBlock : tryNode.getCatchBlocks()) { - allTerminal &= catchBlock.isTerminal(); + if (finallyBody == null) { + return addStatement(tryNode); } - tryNode.setIsTerminal(allTerminal); - - addStatement(tryNode); - unnest(tryNode); + /* + * create a new trynode + * if we have catches: + * + * try try + * x try + * catch x + * y catch + * finally z y + * catchall + * rethrow + * + * otheriwse + * + * try try + * x x + * finally catchall + * y rethrow + * + * + * now splice in finally code wherever needed + * + */ + TryNode newTryNode; - // if finally body is present, place it after the tryNode - if (finallyBody != null) { - tryNode.setFinallyBody(null); - addStatement(finallyBody); + final Block catchAll = catchAllBlock(tryNode); + + 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; + + 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); } - return tryNode; + newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null); + + /* + * Now that the transform is done, we have to go into the try and splice + * the finally block in front of any statement that is outside the try + */ + return spliceFinally(newTryNode, rethrows, finallyBody); } @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(); - - if (test == null) { - setHasGoto(whileNode); - } + final Block body = whileNode.getBody(); - 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 @@ * * @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 @@ * @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 @@ @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,136 +606,24 @@ 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); + 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); } - - //create a return statement - final Node returnNode = new ReturnNode(functionNode.getSource(), functionNode.getLastToken(), functionNode.getFinish(), resultNode, null); - returnNode.accept(this); + return loopNode; } - 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); + private Node addStatement(final Node statement) { + ((BlockLexicalContext)getLexicalContext()).appendStatement(statement); + return statement; } /** - * 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; - } - } - return false; - } - - /** - * 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; - } - - - /** * An internal expression has a symbol that is tagged internal. Check if * this is such a node * @@ -939,40 +637,21 @@ /** * 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; } }
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Fri Apr 19 16:11:16 2013 +0200 @@ -53,9 +53,12 @@ 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,8 @@ 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 +84,14 @@ 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 +121,10 @@ 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 +208,7 @@ @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 +481,8 @@ 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 +659,7 @@ * @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; } @@ -835,13 +840,13 @@ 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 +875,13 @@ 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 +897,24 @@ 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()); - } - - /** - * Push the arguments array to the stack - * - * @return the method emitter - */ - MethodEmitter loadArguments() { - debug("load arguments ", functionNode.getArgumentsNode().getSymbol()); - assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0; - return load(functionNode.getArgumentsNode().getSymbol()); + private Symbol compilerConstant(final CompilerConstants cc) { + return functionNode.getBody().getExistingSymbol(cc.symbolName()); } - /** - * 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); + 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 scope from the stack and store it in its predefined slot - */ - void storeScope() { - debug("store scope"); - store(functionNode.getScopeNode().getSymbol()); - } - - /** - * 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 +962,13 @@ 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); } @@ -1345,6 +1277,11 @@ } } + MethodEmitter registerReturn() { + this.hasReturn = true; + return this; + } + /** * Perform a non void return, popping the type from the stack * @@ -1385,22 +1322,7 @@ * * @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); } @@ -2237,7 +2159,7 @@ } 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 +2176,12 @@ 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; } + }
--- a/src/jdk/nashorn/internal/codegen/Namespace.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/Namespace.java Fri Apr 19 16:11:16 2013 +0200 @@ -53,7 +53,7 @@ */ public Namespace(final Namespace parent) { this.parent = parent; - directory = new HashMap<>(); + this.directory = new HashMap<>(); } /** @@ -65,10 +65,6 @@ 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 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) {
--- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Fri Apr 19 16:11:16 2013 +0200 @@ -204,8 +204,8 @@ * @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,7 @@ * @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; } /** @@ -449,7 +449,7 @@ * @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();
--- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java Fri Apr 19 16:11:16 2013 +0200 @@ -36,7 +36,7 @@ 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 @@ */ 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;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java Fri Apr 19 16:11:16 2013 +0200 @@ -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; + } +}
--- a/src/jdk/nashorn/internal/codegen/Splitter.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/Splitter.java Fri Apr 19 16:11:16 2013 +0200 @@ -28,29 +28,18 @@ 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 @@ 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 @@ /** 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 @@ */ 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 @@ * * @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 @@ statements = new ArrayList<>(); statementsWeight = 0; } - } if (statement.isTerminal()) { @@ -201,9 +209,7 @@ 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 Source source = parent.getSource(); final long token = parent.getToken(); final int finish = parent.getFinish(); - final String name = function.uniqueName(SPLIT_PREFIX.tag()); - - final Block newBlock = new Block(source, token, finish); - newBlock.setFrame(new Frame(parent.getFrame())); - newBlock.setStatements(statements); + final String name = function.uniqueName(SPLIT_PREFIX.symbolName()); - final SplitNode splitNode = new SplitNode(name, functionNode, newBlock); + final Block newBlock = new Block(source, token, finish, statements); - 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 @@ return literal; } - functionNode.setIsSplit(); + getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT); if (literal instanceof ArrayLiteralNode) { final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal; @@ -312,123 +311,12 @@ } @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; + 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; } - - @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); - } - } - } + return false; } }
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java Fri Apr 19 16:11:16 2013 +0200 @@ -35,7 +35,6 @@ 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 @@ * @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 @@ } @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 @@ } @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 @@ } @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 @@ @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 @@ } } - return null; + return false; } - return literalNode; + return true; } @Override @@ -249,9 +242,9 @@ } @Override - public Node enterSplitNode(final SplitNode splitNode) { + public boolean enterSplitNode(final SplitNode splitNode) { weight += SPLIT_WEIGHT; - return null; + return false; } @Override
--- a/src/jdk/nashorn/internal/ir/AccessNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/AccessNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,23 +25,18 @@ package jdk.nashorn.internal.ir; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS; - -import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representation of a property access (period operator.) - * */ -public class AccessNode extends BaseNode implements TypeOverride<AccessNode> { +@Immutable +public final class AccessNode extends BaseNode { /** Property ident. */ - private IdentNode property; - - /** Does this node have a type override */ - private boolean hasCallSiteType; + private final IdentNode property; /** * Constructor @@ -53,49 +48,13 @@ * @param property property */ public AccessNode(final Source source, final long token, final int finish, final Node base, final IdentNode property) { - super(source, token, finish, base); - - this.start = base.getStart(); + super(source, token, finish, base, false, false); this.property = property.setIsPropertyName(); } - /** - * Copy constructor - * - * @param accessNode source node - */ - public AccessNode(final AccessNode accessNode) { - this(accessNode, new CopyState()); - } - - /** - * Internal copy constructor - * - * @param accessNode source node - * @param cs copy state - */ - protected AccessNode(final AccessNode accessNode, final CopyState cs) { - super(accessNode, cs); - this.property = (IdentNode)cs.existingOrCopy(accessNode.getProperty()); - } - - @Override - protected Node copy(final CopyState cs) { - return new AccessNode(this, cs); - } - - @Override - public boolean equals(final Object other) { - if (!super.equals(other)) { - return false; - } - final AccessNode accessNode = (AccessNode)other; - return property.equals(accessNode.getProperty()); - } - - @Override - public int hashCode() { - return super.hashCode() ^ property.hashCode(); + private AccessNode(final AccessNode accessNode, final Node base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) { + super(accessNode, base, isFunction, hasCallSiteType); + this.property = property; } /** @@ -104,12 +63,11 @@ */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enterAccessNode(this) != null) { - base = base.accept(visitor); - property = (IdentNode)property.accept(visitor); - return visitor.leaveAccessNode(this); + if (visitor.enterAccessNode(this)) { + return visitor.leaveAccessNode( + setBase(base.accept(visitor)). + setProperty((IdentNode)property.accept(visitor))); } - return this; } @@ -117,7 +75,7 @@ public void toString(final StringBuilder sb) { final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true); - if (hasCallSiteType) { + if (hasCallSiteType()) { sb.append('{'); final String desc = getType().getDescriptor(); sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor()); @@ -147,19 +105,34 @@ return property; } - @Override - public AccessNode setType(final Type type) { - if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { - ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType()); + private AccessNode setBase(final Node base) { + if (this.base == base) { + return this; } - property = property.setType(type); - getSymbol().setTypeOverride(type); //always a temp so this is fine. - hasCallSiteType = true; - return this; + return new AccessNode(this, base, property, isFunction(), hasCallSiteType()); + } + + + private AccessNode setProperty(final IdentNode property) { + if (this.property == property) { + return this; + } + return new AccessNode(this, base, property, isFunction(), hasCallSiteType()); } @Override - public boolean canHaveCallSiteType() { - return true; //carried by the symbol and always the same nodetype==symboltype + public AccessNode setType(final Type type) { + logTypeChange(type); + getSymbol().setTypeOverride(type); //always a temp so this is fine. + return new AccessNode(this, base, property.setType(type), isFunction(), hasCallSiteType()); } + + @Override + public BaseNode setIsFunction() { + if (isFunction()) { + return this; + } + return new AccessNode(this, base, property, true, hasCallSiteType()); + } + }
--- a/src/jdk/nashorn/internal/ir/BaseNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/BaseNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,6 +25,10 @@ package jdk.nashorn.internal.ir; +import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS; +import jdk.nashorn.internal.codegen.ObjectClassGenerator; +import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.runtime.Source; /** @@ -33,12 +37,15 @@ * @see AccessNode * @see IndexNode */ -public abstract class BaseNode extends Node implements FunctionCall { +@Immutable +public abstract class BaseNode extends Node implements FunctionCall, TypeOverride<BaseNode> { /** Base Node. */ - protected Node base; + protected final Node base; - private boolean function; + private final boolean isFunction; + + private final boolean hasCallSiteType; /** * Constructor @@ -47,37 +54,28 @@ * @param token token * @param finish finish * @param base base node + * @param isFunction is this a function + * @param hasCallSiteType does this access have a callsite type */ - public BaseNode(final Source source, final long token, final int finish, final Node base) { - super(source, token, finish); - this.base = base; - setStart(base.getStart()); + public BaseNode(final Source source, final long token, final int finish, final Node base, final boolean isFunction, final boolean hasCallSiteType) { + super(source, token, base.getStart(), finish); + this.base = base; + this.isFunction = isFunction; + this.hasCallSiteType = hasCallSiteType; } /** - * Copy constructor - * - * @param baseNode the base node - * @param cs a copy state + * Copy constructor for immutable nodes + * @param baseNode node to inherit from + * @param base base + * @param isFunction is this a function + * @param hasCallSiteType does this access have a callsite type */ - protected BaseNode(final BaseNode baseNode, final CopyState cs) { + protected BaseNode(final BaseNode baseNode, final Node base, final boolean isFunction, final boolean hasCallSiteType) { super(baseNode); - this.base = cs.existingOrCopy(baseNode.getBase()); - setStart(base.getStart()); - } - - @Override - public boolean equals(final Object other) { - if (!super.equals(other)) { - return false; - } - final BaseNode baseNode = (BaseNode)other; - return base.equals(baseNode.getBase()); - } - - @Override - public int hashCode() { - return base.hashCode(); + this.base = base; + this.isFunction = isFunction; + this.hasCallSiteType = hasCallSiteType; } /** @@ -88,25 +86,37 @@ return base; } - /** - * Reset the base node for this access - * @param base new base node - */ - public void setBase(final Node base) { - this.base = base; - } - @Override public boolean isFunction() { - return function; + return isFunction; } /** * Mark this node as being the callee operand of a {@link CallNode}. * @return a base node identical to this one in all aspects except with its function flag set. */ - public BaseNode setIsFunction() { - function = true; - return this; + public abstract BaseNode setIsFunction(); + + @Override + public boolean canHaveCallSiteType() { + return true; //carried by the symbol and always the same nodetype==symboltype + } + + /** + * Does the access have a call site type override? + * @return true if overridden + */ + protected boolean hasCallSiteType() { + return hasCallSiteType; + } + + /** + * Debug type change + * @param type new type + */ + protected final void logTypeChange(final Type type) { + if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { + ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType()); + } } }
--- a/src/jdk/nashorn/internal/ir/BinaryNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/BinaryNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.ir; import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Source; @@ -33,9 +34,12 @@ /** * BinaryNode nodes represent two operand operations. */ -public class BinaryNode extends UnaryNode { +@Immutable +public final class BinaryNode extends Node implements Assignment<Node> { /** Left hand side argument. */ - private Node lhs; + private final Node lhs; + + private final Node rhs; /** * Constructor @@ -46,28 +50,15 @@ * @param rhs right hand side */ public BinaryNode(final Source source, final long token, final Node lhs, final Node rhs) { - super(source, token, rhs); - - start = lhs.getStart(); - finish = rhs.getFinish(); - - this.lhs = lhs; + super(source, token, lhs.getStart(), rhs.getFinish()); + this.lhs = lhs; + this.rhs = rhs; } - /** - * Copy constructor - * - * @param binaryNode the binary node - * @param cs copy state - */ - protected BinaryNode(final BinaryNode binaryNode, final CopyState cs) { - super(binaryNode, cs); - lhs = cs.existingOrCopy(binaryNode.lhs); - } - - @Override - protected Node copy(final CopyState cs) { - return new BinaryNode(this, cs); + private BinaryNode(final BinaryNode binaryNode, final Node lhs, final Node rhs) { + super(binaryNode); + this.lhs = lhs; + this.rhs = rhs; } /** @@ -149,28 +140,14 @@ return rhs(); } - @Override - public boolean equals(final Object other) { - if (!super.equals(other)) { - return false; - } - return lhs.equals(((BinaryNode)other).lhs()); - } - - @Override - public int hashCode() { - return super.hashCode() ^ lhs().hashCode(); - } - /** * Assist in IR navigation. * @param visitor IR navigating visitor. */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enterBinaryNode(this) != null) { - // TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers - return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor))); + if (visitor.enterBinaryNode(this)) { + return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor))); } return this; @@ -231,14 +208,35 @@ } /** + * Get the right hand side expression for this node + * @return the left hand side expression + */ + public Node rhs() { + return rhs; + } + + /** * Set the left hand side expression for this node * @param lhs new left hand side expression * @return a node equivalent to this one except for the requested change. */ public BinaryNode setLHS(final Node lhs) { - if(this.lhs == lhs) return this; - final BinaryNode n = (BinaryNode)clone(); - n.lhs = lhs; - return n; + if (this.lhs == lhs) { + return this; + } + return new BinaryNode(this, lhs, rhs); } + + /** + * Set the right hand side expression for this node + * @param rhs new left hand side expression + * @return a node equivalent to this one except for the requested change. + */ + public BinaryNode setRHS(final Node rhs) { + if (this.rhs == rhs) { + return this; + } + return new BinaryNode(this, lhs, rhs); + } + }
--- a/src/jdk/nashorn/internal/ir/Block.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/Block.java Fri Apr 19 16:11:16 2013 +0200 @@ -27,14 +27,15 @@ import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; -import java.util.ListIterator; -import jdk.nashorn.internal.codegen.Frame; +import java.util.Map; import jdk.nashorn.internal.codegen.Label; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; @@ -42,97 +43,79 @@ * IR representation for a list of statements and functions. All provides the * basis for script body. */ -public class Block extends Node { +@Immutable +public class Block extends BreakableNode implements Flags<Block> { /** List of statements */ - protected List<Node> statements; + protected final List<Node> statements; - /** Symbol table. */ - protected final HashMap<String, Symbol> symbols; - - /** Variable frame. */ - protected Frame frame; + /** Symbol table - keys must be returned in the order they were put in. */ + protected final Map<String, Symbol> symbols; /** Entry label. */ protected final Label entryLabel; - /** Break label. */ - protected final Label breakLabel; + /** Does the block/function need a new scope? */ + protected final int flags; + + /** Flag indicating that this block needs scope */ + public static final int NEEDS_SCOPE = 1 << 0; - /** Does the block/function need a new scope? */ - protected boolean needsScope; + /** + * Flag indicating whether this block needs + * self symbol assignment at the start. This is used only for + * blocks that are the bodies of function nodes who refer to themselves + * by name. It causes codegen to insert a var [fn_name] = __callee__ + * at the start of the body + */ + public static final int NEEDS_SELF_SYMBOL = 1 << 1; + + /** + * Is this block tagged as terminal based on its contents + * (usually the last statement) + */ + public static final int IS_TERMINAL = 1 << 2; /** * Constructor * - * @param source source code - * @param token token - * @param finish finish + * @param source source code + * @param token token + * @param finish finish + * @param statements statements */ - public Block(final Source source, final long token, final int finish) { - super(source, token, finish); + public Block(final Source source, final long token, final int finish, final Node... statements) { + super(source, token, finish, new Label("block_break")); - this.statements = new ArrayList<>(); - this.symbols = new HashMap<>(); + this.statements = Arrays.asList(statements); + this.symbols = new LinkedHashMap<>(); this.entryLabel = new Label("block_entry"); - this.breakLabel = new Label("block_break"); + this.flags = 0; } /** - * Internal copy constructor + * Constructor * - * @param block the source block - * @param cs the copy state + * @param source source code + * @param token token + * @param finish finish + * @param statements statements */ - protected Block(final Block block, final CopyState cs) { - super(block); + public Block(final Source source, final long token, final int finish, final List<Node> statements) { + this(source, token, finish, statements.toArray(new Node[statements.size()])); + } - this.statements = new ArrayList<>(); - for (final Node statement : block.getStatements()) { - statements.add(cs.existingOrCopy(statement)); - } - this.symbols = new HashMap<>(); - this.frame = block.frame == null ? null : block.frame.copy(); + private Block(final Block block, final int finish, final List<Node> statements, final int flags) { + super(block); + this.statements = statements; + this.flags = flags; + this.symbols = block.symbols; //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now this.entryLabel = new Label(block.entryLabel); - this.breakLabel = new Label(block.breakLabel); - - assert block.symbols.isEmpty() : "must not clone with symbols"; + this.finish = finish; } @Override - protected Node copy(final CopyState cs) { - return new Block(this, cs); - } - - /** - * Add a new statement to the statement list. - * - * @param statement Statement node to add. - */ - public void addStatement(final Node statement) { - if (statement != null) { - statements.add(statement); - if (getFinish() < statement.getFinish()) { - setFinish(statement.getFinish()); - } - } - } - - /** - * Prepend statements to the statement list - * - * @param prepended statement to add - */ - public void prependStatements(final List<Node> prepended) { - statements.addAll(0, prepended); - } - - /** - * Add a list of statements to the statement list. - * - * @param statementList Statement nodes to add. - */ - public void addStatements(final List<Node> statementList) { - statements.addAll(statementList); + public Node ensureUniqueLabels(final LexicalContext lc) { + return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags)); } /** @@ -142,19 +125,9 @@ * @return new or same node */ @Override - public Node accept(final NodeVisitor visitor) { - final Block saveBlock = visitor.getCurrentBlock(); - visitor.setCurrentBlock(this); - - try { - // Ignore parent to avoid recursion. - - if (visitor.enterBlock(this) != null) { - visitStatements(visitor); - return visitor.leaveBlock(this); - } - } finally { - visitor.setCurrentBlock(saveBlock); + public Node accept(final LexicalContext lc, final NodeVisitor visitor) { + if (visitor.enterBlock(this)) { + return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Node.class, statements))); } return this; @@ -222,11 +195,18 @@ } /** - * Get the break label for this block - * @return the break label + * Tag block as terminal or non terminal + * @param lc lexical context + * @param isTerminal is block terminal + * @return same block, or new if flag changed */ - public Label getBreakLabel() { - return breakLabel; + public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) { + return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL); + } + + @Override + public boolean isTerminal() { + return getFlag(IS_TERMINAL); } /** @@ -238,23 +218,6 @@ } /** - * Get the frame for this block - * @return the frame - */ - public Frame getFrame() { - return frame; - } - - /** - * Reset the frame for this block - * - * @param frame the new frame - */ - public void setFrame(final Frame frame) { - this.frame = frame; - } - - /** * Get the list of statements in this block * * @return a list of statements @@ -264,21 +227,21 @@ } /** - * Applies the specified visitor to all statements in the block. - * @param visitor the visitor. - */ - public void visitStatements(NodeVisitor visitor) { - for (ListIterator<Node> stmts = statements.listIterator(); stmts.hasNext();) { - stmts.set(stmts.next().accept(visitor)); - } - } - /** * Reset the statement list for this block * - * @param statements new statement list + * @param lc lexical context + * @param statements new statement list + * @return new block if statements changed, identity of statements == block.statements */ - public void setStatements(final List<Node> statements) { - this.statements = statements; + public Block setStatements(final LexicalContext lc, final List<Node> statements) { + if (this.statements == statements) { + return this; + } + int lastFinish = 0; + if (!statements.isEmpty()) { + lastFinish = statements.get(statements.size() - 1).getFinish(); + } + return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags)); } /** @@ -297,39 +260,65 @@ * @return true if this function needs a scope */ public boolean needsScope() { - return needsScope; + return (flags & NEEDS_SCOPE) == NEEDS_SCOPE; + } + + @Override + public Block setFlags(final LexicalContext lc, int flags) { + if (this.flags == flags) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags)); + } + + @Override + public Block clearFlag(final LexicalContext lc, int flag) { + return setFlags(lc, flags & ~flag); + } + + @Override + public Block setFlag(final LexicalContext lc, int flag) { + return setFlags(lc, flags | flag); + } + + @Override + public boolean getFlag(final int flag) { + return (flags & flag) == flag; } /** * Set the needs scope flag. + * @param lc lexicalContext + * @return new block if state changed, otherwise this */ - public void setNeedsScope() { - needsScope = true; + public Block setNeedsScope(final LexicalContext lc) { + if (needsScope()) { + return this; + } + + return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE)); } /** - * Marks this block as using a specified scoped symbol. The block and its parent blocks up to but not - * including the block defining the symbol will be marked as needing parent scope. The block defining the symbol - * will be marked as one that needs to have its own scope. - * @param symbol the symbol being used. - * @param ancestors the iterator over block's containing lexical context + * Computationally determine the next slot for this block, + * indexed from 0. Use this as a relative base when computing + * frames + * @return next slot */ - public void setUsesScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) { - if(symbol.getBlock() == this) { - setNeedsScope(); - } else { - setUsesParentScopeSymbol(symbol, ancestors); + public int nextSlot() { + final Iterator<Symbol> iter = symbolIterator(); + int next = 0; + while (iter.hasNext()) { + final Symbol symbol = iter.next(); + if (symbol.hasSlot()) { + next += symbol.slotCount(); } + } + return next; } - /** - * Invoked when this block uses a scope symbol defined in one of its ancestors. - * @param symbol the scope symbol being used - * @param ancestors iterator over ancestor blocks - */ - void setUsesParentScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) { - if(ancestors.hasNext()) { - ancestors.next().setUsesScopeSymbol(symbol, ancestors); - } + @Override + protected boolean isBreakableWithoutLabel() { + return false; } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Fri Apr 19 16:11:16 2013 +0200 @@ -0,0 +1,120 @@ +/* + * 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.ir; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.ListIterator; + +/** + * This is a subclass of lexical context used for filling + * blocks (and function nodes) with statements. When popping + * a block from the lexical context, any statements that have + * been generated in it are commited to the block. This saves + * unnecessary object mutations and lexical context replacement + */ +public class BlockLexicalContext extends LexicalContext { + /** statement stack, each block on the lexical context maintains one of these, which is + * committed to the block on pop */ + private Deque<List<Node>> sstack = new ArrayDeque<>(); + + /** Last non debug statement emitted in this context */ + protected Node lastStatement; + + @Override + public <T extends LexicalContextNode> T push(final T node) { + T pushed = super.push(node); + if (node instanceof Block) { + sstack.push(new ArrayList<Node>()); + } + return pushed; + } + + /** + * Get the statement list from the stack, possibly filtered + * @return statement list + */ + protected List<Node> popStatements() { + return sstack.pop(); + } + + @Override + @SuppressWarnings("unchecked") + public <T extends LexicalContextNode> T pop(final T node) { + T expected = node; + if (node instanceof Block) { + final List<Node> newStatements = popStatements(); + expected = (T)((Block)node).setStatements(this, newStatements); + if (!sstack.isEmpty()) { + lastStatement = lastStatement(sstack.peek()); + } + } + return super.pop(expected); + } + + /** + * Append a statement to the block being generated + * @param statement statement to add + */ + public void appendStatement(final Node statement) { + assert statement != null; + sstack.peek().add(statement); + if (!statement.isDebug()) { + lastStatement = statement; + } + } + + /** + * Prepend a statement to the block being generated + * @param statement statement to prepend + * @return the prepended statement + */ + public Node prependStatement(final Node statement) { + assert statement != null; + sstack.peek().add(0, statement); + return statement; + } + + /** + * Get the last (non debug) statement that was emitted into a block + * @return the last statement emitted + */ + public Node getLastStatement() { + return lastStatement; + } + + private static Node lastStatement(final List<Node> statements) { + for (final ListIterator<Node> iter = statements.listIterator(statements.size()); iter.hasPrevious(); ) { + final Node node = iter.previous(); + if (!node.isDebug()) { + return node; + } + } + return null; + } +}
--- a/src/jdk/nashorn/internal/ir/BreakNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/BreakNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,37 +25,34 @@ package jdk.nashorn.internal.ir; -import jdk.nashorn.internal.codegen.Label; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representation for {@code break} statements. */ -public class BreakNode extends LabeledNode { +@Immutable +public final class BreakNode extends Node { - /** + private final IdentNode label; + + /** * Constructor * - * @param source source code - * @param token token - * @param finish finish - * @param labelNode break label - * @param targetNode node to break to - * @param tryChain surrounding try chain + * @param source source code + * @param token token + * @param finish finish + * @param label label for break or null if none */ - public BreakNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) { - super(source, token, finish, labelNode, targetNode, tryChain); - setHasGoto(); - } - - private BreakNode(final BreakNode breakNode, final CopyState cs) { - super(breakNode, cs); + public BreakNode(final Source source, final long token, final int finish, final IdentNode label) { + super(source, token, finish); + this.label = label; } @Override - protected Node copy(final CopyState cs) { - return new BreakNode(this, cs); + public boolean hasGoto() { + return true; } /** @@ -64,7 +61,7 @@ */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enterBreakNode(this) != null) { + if (visitor.enterBreakNode(this)) { return visitor.leaveBreakNode(this); } @@ -72,26 +69,20 @@ } /** - * Return the target label of this break node. - * @return the target label. + * Get the label for this break node + * @return label, or null if none */ - public Label getTargetLabel() { - if (targetNode instanceof BreakableNode) { - return ((BreakableNode)targetNode).getBreakLabel(); - } else if (targetNode instanceof Block) { - return ((Block)targetNode).getBreakLabel(); - } - - throw new AssertionError("Invalid break target " + targetNode.getClass()); + public IdentNode getLabel() { + return label; } @Override public void toString(final StringBuilder sb) { sb.append("break"); - if (labelNode != null) { + if (label != null) { sb.append(' '); - labelNode.getLabel().toString(sb); + label.toString(sb); } } }
--- a/src/jdk/nashorn/internal/ir/BreakableNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/BreakableNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,27 +25,34 @@ package jdk.nashorn.internal.ir; +import java.util.Arrays; +import java.util.List; + import jdk.nashorn.internal.codegen.Label; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.runtime.Source; /** * This class represents a node from which control flow can execute * a {@code break} statement */ -public abstract class BreakableNode extends Node { +@Immutable +public abstract class BreakableNode extends LexicalContextNode { /** break label. */ - protected Label breakLabel; + protected final Label breakLabel; /** * Constructor * - * @param source source code - * @param token token - * @param finish finish + * @param source source code + * @param token token + * @param finish finish + * @param breakLabel break label */ - public BreakableNode(final Source source, final long token, final int finish) { + protected BreakableNode(final Source source, final long token, final int finish, final Label breakLabel) { super(source, token, finish); + this.breakLabel = breakLabel; } /** @@ -55,6 +62,19 @@ */ protected BreakableNode(final BreakableNode breakableNode) { super(breakableNode); + this.breakLabel = new Label(breakableNode.getBreakLabel()); + } + + @Override + public abstract Node ensureUniqueLabels(final LexicalContext lc); + + /** + * Check whether this can be broken out from without using a label, + * e.g. everything but Blocks, basically + * @return true if breakable without label + */ + protected boolean isBreakableWithoutLabel() { + return true; } /** @@ -65,4 +85,14 @@ return breakLabel; } + /** + * Return the labels associated with this node. Breakable nodes that + * aren't LoopNodes only have a break label -> the location immediately + * afterwards the node in code + * @return list of labels representing locations around this node + */ + public List<Label> getLabels() { + return Arrays.asList(breakLabel); + } + }
--- a/src/jdk/nashorn/internal/ir/CallNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/CallNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,49 +25,51 @@ package jdk.nashorn.internal.ir; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representation for a function call. - * */ -public class CallNode extends Node implements TypeOverride<CallNode> { +@Immutable +public final class CallNode extends LexicalContextNode implements TypeOverride<CallNode> { - private Type type; + private final Type type; /** Function identifier or function body. */ - private Node function; + private final Node function; /** Call arguments. */ - private List<Node> args; + private final List<Node> args; + + /** Is this a "new" operation */ + public static final int IS_NEW = 0x1; - /** flag - is new expression */ - private boolean isNew; + /** Is this call tagged as inside a with block */ + public static final int IN_WITH_BLOCK = 0x2; - /** flag - is in with block */ - private boolean inWithBlock; + private final int flags; /** * Arguments to be passed to builtin {@code eval} function */ public static class EvalArgs { /** evaluated code */ - private Node code; + private final Node code; /** 'this' passed to evaluated code */ - private IdentNode evalThis; + private final IdentNode evalThis; /** location string for the eval call */ - final private String location; + private final String location; /** is this call from a strict context? */ - final private boolean strictMode; + private final boolean strictMode; /** * Constructor @@ -92,12 +94,11 @@ return code; } - /** - * Set the code that is to be eval.ed by this eval function - * @param code the code as an AST node - */ - public void setCode(final Node code) { - this.code = code; + private EvalArgs setCode(final Node code) { + if (this.code == code) { + return this; + } + return new EvalArgs(code, evalThis, location, strictMode); } /** @@ -108,12 +109,11 @@ return this.evalThis; } - /** - * Set the {@code this} symbol used to invoke this eval call - * @param evalThis the {@code this} symbol - */ - public void setThis(final IdentNode evalThis) { - this.evalThis = evalThis; + private EvalArgs setThis(final IdentNode evalThis) { + if (this.evalThis == evalThis) { + return this; + } + return new EvalArgs(code, evalThis, location, strictMode); } /** @@ -135,7 +135,7 @@ /** arguments for 'eval' call. Non-null only if this call node is 'eval' */ @Ignore - private EvalArgs evalArgs; + private final EvalArgs evalArgs; /** * Constructors @@ -145,32 +145,27 @@ * @param finish finish * @param function the function to call * @param args args to the call + * @param flags flags */ - public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args) { + public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args, final int flags) { super(source, token, finish); - setStart(function.getStart()); - - this.function = function; - this.args = args; + this.function = function; + this.args = args; + this.flags = flags; + this.type = null; + this.evalArgs = null; } - private CallNode(final CallNode callNode, final CopyState cs) { + private CallNode(final CallNode callNode, final Node function, final List<Node> args, final int flags, final Type type, final EvalArgs evalArgs) { super(callNode); - - final List<Node> newArgs = new ArrayList<>(); - - for (final Node arg : callNode.args) { - newArgs.add(cs.existingOrCopy(arg)); - } - - this.function = cs.existingOrCopy(callNode.function); //TODO existing or same? - this.args = newArgs; - this.isNew = callNode.isNew; - this.inWithBlock = callNode.inWithBlock; + this.function = function; + this.args = args; + this.flags = flags; + this.type = type; + this.evalArgs = evalArgs; } - @Override public Type getType() { if (hasCallSiteType()) { @@ -181,8 +176,10 @@ @Override public CallNode setType(final Type type) { - this.type = type; - return this; + if (this.type == type) { + return this; + } + return new CallNode(this, function, args, flags, type, evalArgs); } private boolean hasCallSiteType() { @@ -194,11 +191,6 @@ return true; } - @Override - protected Node copy(final CopyState cs) { - return new CallNode(this, cs); - } - /** * Assist in IR navigation. * @@ -207,15 +199,22 @@ * @return node or replacement */ @Override - public Node accept(final NodeVisitor visitor) { - if (visitor.enterCallNode(this) != null) { - function = function.accept(visitor); - - for (int i = 0, count = args.size(); i < count; i++) { - args.set(i, args.get(i).accept(visitor)); + public Node accept(final LexicalContext lc, final NodeVisitor visitor) { + if (visitor.enterCallNode(this)) { + final CallNode newCallNode = (CallNode)visitor.leaveCallNode( + setFunction(function.accept(visitor)). + setArgs(Node.accept(visitor, Node.class, args)). + setFlags(flags). + setType(type). + setEvalArgs(evalArgs == null ? + null : + evalArgs.setCode(evalArgs.getCode().accept(visitor)). + setThis((IdentNode)evalArgs.getThis().accept(visitor)))); + // Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice, + // setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now. + if(this != newCallNode) { + return Node.replaceInLexicalContext(lc, this, newCallNode); } - - return visitor.leaveCallNode(this); } return this; @@ -226,7 +225,7 @@ if (hasCallSiteType()) { sb.append('{'); final String desc = getType().getDescriptor(); - sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor()); + sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor()); sb.append('}'); } @@ -261,8 +260,11 @@ * Reset the arguments for the call * @param args new arguments list */ - public void setArgs(final List<Node> args) { - this.args = args; + private CallNode setArgs(final List<Node> args) { + if (this.args == args) { + return this; + } + return new CallNode(this, function, args, flags, type, evalArgs); } /** @@ -278,9 +280,13 @@ * {@code eval} * * @param evalArgs eval args + * @return same node or new one on state change */ - public void setEvalArgs(final EvalArgs evalArgs) { - this.evalArgs = evalArgs; + public CallNode setEvalArgs(final EvalArgs evalArgs) { + if (this.evalArgs == evalArgs) { + return this; + } + return new CallNode(this, function, args, flags, type, evalArgs); } /** @@ -301,10 +307,14 @@ /** * Reset the function expression that this call invokes - * @param node the function + * @param function the function + * @return same node or new one on state change */ - public void setFunction(final Node node) { - function = node; + public CallNode setFunction(final Node function) { + if (this.function == function) { + return this; + } + return new CallNode(this, function, args, flags, type, evalArgs); } /** @@ -312,14 +322,15 @@ * @return true if this a new operation */ public boolean isNew() { - return isNew; + return (flags & IS_NEW) == IS_NEW; } /** * Flag this call as a new operation + * @return same node or new one on state change */ - public void setIsNew() { - this.isNew = true; + public CallNode setIsNew() { + return setFlags(IS_NEW); } /** @@ -327,13 +338,13 @@ * @return true if the call is inside a {@code with} block */ public boolean inWithBlock() { - return inWithBlock; + return (flags & IN_WITH_BLOCK) == IN_WITH_BLOCK; } - /** - * Flag this call to be inside a {@code with} block - */ - public void setInWithBlock() { - this.inWithBlock = true; + private CallNode setFlags(final int flags) { + if (this.flags == flags) { + return this; + } + return new CallNode(this, function, args, flags, type, evalArgs); } }
--- a/src/jdk/nashorn/internal/ir/CaseNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/CaseNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -26,19 +26,21 @@ package jdk.nashorn.internal.ir; import jdk.nashorn.internal.codegen.Label; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representation of CASE clause. - * + * Case nodes are not BreakableNodes, but the SwitchNode is */ -public class CaseNode extends BreakableNode { +@Immutable +public final class CaseNode extends Node { /** Test expression. */ - private Node test; + private final Node test; /** Statements. */ - private Block body; + private final Block body; /** Case entry label. */ private final Label entry; @@ -60,17 +62,17 @@ this.entry = new Label("entry"); } - private CaseNode(final CaseNode caseNode, final CopyState cs) { + CaseNode(final CaseNode caseNode, final Node test, final Block body) { super(caseNode); - this.test = cs.existingOrCopy(caseNode.test); - this.body = (Block)cs.existingOrCopy(caseNode.body); + this.test = test; + this.body = body; this.entry = new Label(caseNode.entry); } @Override - protected Node copy(final CopyState cs) { - return new CaseNode(this, cs); + public boolean isTerminal() { + return body.isTerminal(); } /** @@ -79,15 +81,11 @@ */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enterCaseNode(this) != null) { - if (test != null) { - test = test.accept(visitor); - } - if (body != null) { - body = (Block)body.accept(visitor); - } + if (visitor.enterCaseNode(this)) { + final Node newTest = test == null ? null : test.accept(visitor); + final Block newBody = body == null ? null : (Block)body.accept(visitor); - return visitor.leaveCaseNode(this); + return visitor.leaveCaseNode(setTest(newTest).setBody(newBody)); } return this; @@ -131,8 +129,19 @@ /** * Reset the test expression for this case node * @param test new test expression + * @return new or same CaseNode */ - public void setTest(final Node test) { - this.test = test; + public CaseNode setTest(final Node test) { + if (this.test == test) { + return this; + } + return new CaseNode(this, test, body); + } + + private CaseNode setBody(final Block body) { + if (this.body == body) { + return this; + } + return new CaseNode(this, test, body); } }
--- a/src/jdk/nashorn/internal/ir/CatchNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/CatchNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,26 +25,23 @@ package jdk.nashorn.internal.ir; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representation of a catch clause. - * */ -public class CatchNode extends Node { +@Immutable +public final class CatchNode extends Node { /** Exception identifier. */ - private IdentNode exception; + private final IdentNode exception; /** Exception condition. */ - private Node exceptionCondition; + private final Node exceptionCondition; /** Catch body. */ - private Block body; - - /** Is rethrow - e.g. synthetic catch block for e.g. finallies, the parser case where - * there has to be at least on catch for syntactic validity */ - private boolean isSyntheticRethrow; + private final Block body; /** * Constructors @@ -64,18 +61,12 @@ this.body = body; } - private CatchNode(final CatchNode catchNode, final CopyState cs) { + private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body) { super(catchNode); - this.exception = (IdentNode)cs.existingOrCopy(catchNode.exception); - this.exceptionCondition = cs.existingOrCopy(catchNode.exceptionCondition); - this.body = (Block)cs.existingOrCopy(catchNode.body); - this.isSyntheticRethrow = catchNode.isSyntheticRethrow; - } - - @Override - protected Node copy(final CopyState cs) { - return new CatchNode(this, cs); + this.exception = exception; + this.exceptionCondition = exceptionCondition; + this.body = body; } /** @@ -84,21 +75,22 @@ */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enterCatchNode(this) != null) { - exception = (IdentNode)exception.accept(visitor); - - if (exceptionCondition != null) { - exceptionCondition = exceptionCondition.accept(visitor); - } - - body = (Block)body.accept(visitor); - return visitor.leaveCatchNode(this); + if (visitor.enterCatchNode(this)) { + return visitor.leaveCatchNode( + setException((IdentNode)exception.accept(visitor)). + setExceptionCondition(exceptionCondition == null ? null : exceptionCondition.accept(visitor)). + setBody((Block)body.accept(visitor))); } return this; } @Override + public boolean isTerminal() { + return body.isTerminal(); + } + + @Override public void toString(final StringBuilder sb) { sb.append(" catch ("); exception.toString(sb); @@ -111,23 +103,6 @@ } /** - * Check if this catch is a synthetic rethrow - * @return true if this is a synthetic rethrow - */ - public boolean isSyntheticRethrow() { - return isSyntheticRethrow; - } - - /** - * Flag this as deliberatly generated catch all that rethrows the - * caught exception. This is used for example for generating finally - * expressions - */ - public void setIsSyntheticRethrow() { - this.isSyntheticRethrow = true; - } - - /** * Get the identifier representing the exception thrown * @return the exception identifier */ @@ -146,9 +121,13 @@ /** * Reset the exception condition for this catch block * @param exceptionCondition the new exception condition + * @return new or same CatchNode */ - public void setExceptionCondition(final Node exceptionCondition) { - this.exceptionCondition = exceptionCondition; + public CatchNode setExceptionCondition(final Node exceptionCondition) { + if (this.exceptionCondition == exceptionCondition) { + return this; + } + return new CatchNode(this, exception, exceptionCondition, body); } /** @@ -158,4 +137,18 @@ public Block getBody() { return body; } + + private CatchNode setException(final IdentNode exception) { + if (this.exception == exception) { + return this; + } + return new CatchNode(this, exception, exceptionCondition, body); + } + + private CatchNode setBody(final Block body) { + if (this.body == body) { + return this; + } + return new CatchNode(this, exception, exceptionCondition, body); + } }
--- a/src/jdk/nashorn/internal/ir/ContinueNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/ContinueNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,43 +25,39 @@ package jdk.nashorn.internal.ir; -import jdk.nashorn.internal.codegen.Label; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representation for CONTINUE statements. - * */ -public class ContinueNode extends LabeledNode { +@Immutable +public class ContinueNode extends Node { + + private IdentNode label; /** * Constructor * - * @param source the source - * @param token token - * @param finish finish - * @param labelNode the continue label - * @param targetNode node to continue to - * @param tryChain surrounding try chain + * @param source source code + * @param token token + * @param finish finish + * @param label label for break or null if none */ - public ContinueNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) { - super(source, token, finish, labelNode, targetNode, tryChain); - setHasGoto(); - } - - private ContinueNode(final ContinueNode continueNode, final CopyState cs) { - super(continueNode, cs); + public ContinueNode(final Source source, final long token, final int finish, final IdentNode label) { + super(source, token, finish); + this.label = label; } @Override - protected Node copy(final CopyState cs) { - return new ContinueNode(this, cs); + public boolean hasGoto() { + return true; } @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enterContinueNode(this) != null) { + if (visitor.enterContinueNode(this)) { return visitor.leaveContinueNode(this); } @@ -69,21 +65,20 @@ } /** - * Return the target label of this continue node. - * @return the target label. + * Get the label for this break node + * @return label, or null if none */ - public Label getTargetLabel() { - assert targetNode instanceof WhileNode : "continue target must be a while node"; - return ((WhileNode)targetNode).getContinueLabel(); + public IdentNode getLabel() { + return label; } @Override public void toString(final StringBuilder sb) { sb.append("continue"); - if (labelNode != null) { + if (label != null) { sb.append(' '); - labelNode.getLabel().toString(sb); + label.toString(sb); } } }
--- a/src/jdk/nashorn/internal/ir/DoWhileNode.java Fri Apr 19 18:23:00 2013 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +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.ir; - -import jdk.nashorn.internal.ir.visitor.NodeVisitor; -import jdk.nashorn.internal.runtime.Source; - -/** - * Loop representing do while loops. This is mostly split from WhileNode - * because of the different order of the Phi Traversals - * - */ -public class DoWhileNode extends WhileNode { - - /** - * Constructor - * - * @param source the source - * @param token token - * @param finish finish - */ - public DoWhileNode(final Source source, final long token, final int finish) { - super(source, token, finish); - } - - /** - * Copy constructor - * - * @param doWhileNode source node - * @param cs copy state - */ - protected DoWhileNode(final DoWhileNode doWhileNode, final CopyState cs) { - super(doWhileNode, cs); - } - - @Override - protected Node copy(final CopyState cs) { - return new DoWhileNode(this, cs); - } - - @Override - public Node accept(final NodeVisitor visitor) { - if (visitor.enterDoWhileNode(this) != null) { - body = (Block)body.accept(visitor); - test = test.accept(visitor); - - return visitor.leaveDoWhileNode(this); - } - - return this; - } - - @Override - public void toString(final StringBuilder sb) { - sb.append("while ("); - test.toString(sb); - sb.append(')'); - } -}
--- a/src/jdk/nashorn/internal/ir/EmptyNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/EmptyNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,14 +25,15 @@ package jdk.nashorn.internal.ir; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representation for an empty statement. - * */ -public class EmptyNode extends Node { +@Immutable +public final class EmptyNode extends Node { /** * Constructor @@ -57,7 +58,7 @@ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enterEmptyNode(this) != null) { + if (visitor.enterEmptyNode(this)) { return visitor.leaveEmptyNode(this); } return this;
--- a/src/jdk/nashorn/internal/ir/ExecuteNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/ExecuteNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,6 +25,7 @@ package jdk.nashorn.internal.ir; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; @@ -33,9 +34,10 @@ * node means "this code will be executed" and evaluating it results in * statements being added to the IR */ -public class ExecuteNode extends Node { +@Immutable +public final class ExecuteNode extends Node { /** Expression to execute. */ - private Node expression; + private final Node expression; /** * Constructor @@ -50,6 +52,11 @@ this.expression = expression; } + private ExecuteNode(final ExecuteNode executeNode, final Node expression) { + super(executeNode); + this.expression = expression; + } + /** * Constructor * @@ -60,34 +67,15 @@ this.expression = expression; } - private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) { - super(executeNode); - this.expression = cs.existingOrCopy(executeNode.expression); - } - @Override - protected Node copy(final CopyState cs) { - return new ExecuteNode(this, cs); - } - - @Override - public boolean equals(final Object other) { - if (!super.equals(other)) { - return false; - } - return expression.equals(((ExecuteNode)other).getExpression()); - } - - @Override - public int hashCode() { - return super.hashCode() ^ expression.hashCode(); + public boolean isTerminal() { + return expression.isTerminal(); } @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enterExecuteNode(this) != null) { - setExpression(expression.accept(visitor)); - return visitor.leaveExecuteNode(this); + if (visitor.enterExecuteNode(this)) { + return visitor.leaveExecuteNode(setExpression(expression.accept(visitor))); } return this; @@ -109,8 +97,12 @@ /** * Reset the expression to be executed * @param expression the expression + * @return new or same execute node */ - public void setExpression(final Node expression) { - this.expression = expression; + public ExecuteNode setExpression(final Node expression) { + if (this.expression == expression) { + return this; + } + return new ExecuteNode(this, expression); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/ir/Flags.java Fri Apr 19 16:11:16 2013 +0200 @@ -0,0 +1,69 @@ +/* + * 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.ir; + +/** + * Interface implemented by all nodes that have flags in + * a lexical context. This is needed as we sometimes have to save + * the setting of flags in the lexical context until a block + * is completely finished and its final version (after multiple + * copy on writes) is placed in the lexical context + * + * @param <T> lexical context node that can have flags set during code generation + */ +public interface Flags<T extends LexicalContextNode> { + + /** + * Check if a flag is set in a lexical context node + * @param flag flag to check + * @return flags + */ + public boolean getFlag(int flag); + + /** + * Clear a flag of a LexicalContextNode + * @param lc lexical context + * @param flag flag to clear + * @return the new LexicalContext node if flags were changed, same otherwise + */ + public T clearFlag(final LexicalContext lc, int flag); + + /** + * Set a flag of a LexicalContextNode + * @param lc lexical context + * @param flag flag to set + * @return the new LexicalContext node if flags were changed, same otherwise + */ + public T setFlag(final LexicalContext lc, int flag); + + /** + * Set all flags of a LexicalContextNode, overwriting previous flags + * @param lc lexical context + * @param flags new flags value + * @return the new LexicalContext node if flags were changed, same otherwise + */ + public T setFlags(final LexicalContext lc, int flags); +}
--- a/src/jdk/nashorn/internal/ir/ForNode.java Fri Apr 19 18:23:00 2013 +0530 +++ b/src/jdk/nashorn/internal/ir/ForNode.java Fri Apr 19 16:11:16 2013 +0200 @@ -25,73 +25,75 @@ package jdk.nashorn.internal.ir; +import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representing a FOR statement. - * */ -public class ForNode extends WhileNode { +@Immutable +public final class ForNode extends LoopNode { /** Initialize expression. */ - private Node init; + private final Node init; /** Test expression. */ - private Node modify; + private final Node modify; /** Iterator symbol. */ private Symbol iterator; - /** is for in */ - private boolean isForIn; + /** Is this a normal for loop? */ + public static final int IS_FOR = 1 << 0; - /** is for each */ - private boolean isForEach; + /** Is this a normal for in loop? */ + public static final int IS_FOR_IN = 1 << 1; + + /** Is this a normal for each in loop? */ + public static final int IS_FOR_EACH = 1 << 2; + + private final int flags; /** * Constructor * - * @param source the source - * @param token token - * @param finish finish + * @param source the source + * @param token token + * @param finish finish + * @param init init + * @param test test + * @param body body + * @param modify modify + * @param flags flags */ - public ForNode(final Source source, final long token, final int finish) { - super(source, token, finish); + public ForNode(final Source source, final long token, final int finish, final Node init, final Node test, final Block body, final Node modify, final int flags) { + super(source, token, finish, test, body, false); + this.init = init; + this.modify = modify; + this.flags = flags; } - private ForNode(final ForNode forNode, final CopyState cs) { - super(forNode, cs); - - this.init = cs.existingOrCopy(forNode.init); - this.modify = cs.existingOrCopy(forNode.modify); - this.iterator = forNode.iterator; - this.isForIn = forNode.isForIn; - this.isForEach = forNode.isForEach; + private ForNode(final ForNode forNode, final Node init, final Node test, final Block body, final Node modify, final int flags, final boolean controlFlowEscapes) { + super(forNode, test, body, controlFlowEscapes); + this.init = init; + this.modify = modify; + this.flags = flags; + this.iterator = forNode.iterator; //TODO is this acceptable? symbols are never cloned, just copied as references } @Override - protected Node copy(final CopyState cs) { - return new ForNode(this, cs); + public Node ensureUniqueLabels(LexicalContext lc) { + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes)); } @Override - public Node accept(final NodeVisitor visitor) { - if (visitor.enterForNode(this) != null) { - if (init != null) { - init = init.accept(visitor); - } - - if (test != null) { - test = test.accept(visitor); - } - - if (modify != null) { - modify = modify.accept(visitor); - } - - body = (Block)body.accept(visitor); - - return visitor.leaveForNode(this); + protected Node accept(final LexicalContext lc, final NodeVisitor visitor) { + if (visitor.enterForNode(this)) { + return visitor.leaveForNode( + setInit(lc, init == null ? null : init.accept(visitor)). + setTest(lc, test == null ? null : test.accept(visitor)). + setModify(lc, modify == null ? null : modify.accept(visitor)). + setBody(lc, (Block)body.accept(visitor))); } return this; @@ -122,6 +124,19 @@ sb.append(')'); } + @Override + public boolean hasGoto() { + return !isForIn() && test == null; + } + + @Override + public boolean mustEnter() { + if (isForIn()) { + return false; //may be an empty set to iterate over, then we skip the loop + } + return test == null; + } + /** * Get the initialization expression for this for loop * @return the initialization expression @@ -132,10 +147,15 @@ /** * Reset the initialization expression for this for loop + * @param lc lexical context * @param init new initialization expression + * @return new for node if changed or existing if not */ - public void setInit(final Node init) { - this.init = init; + public ForNode setInit(final LexicalContext lc, final Node init) { + if (this.init == init) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes)); } /** @@ -143,14 +163,16 @@ * @return true if this is a for in constructor */ public boolean isForIn() { - return isForIn; + return (flags & IS_FOR_IN) != 0; } /** * Flag this to be a for in construct + * @param lc lexical context + * @return new for node if changed or existing if not */ - public void setIsForIn() { - this.isForIn = true; + public ForNode setIsForIn(final LexicalContext lc) { + return setFlags(lc, flags | IS_FOR_IN); } /** @@ -159,14 +181,16 @@ * @return true if this is a for each construct */ public boolean isForEach() {