# HG changeset patch # User jlaskey # Date 1368533103 10800 # Node ID 7bd8457aa8d9b89c1481947067c642f2b1e9e9f0 # Parent 4dec41b3c5e3bb616f0c6f15830d940905aa5d16# Parent 0f5bedbf0e0672c9453d9f2c0bfa5f512fc0a26d Merge diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/.hgignore --- a/nashorn/.hgignore Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/.hgignore Tue May 14 09:05:03 2013 -0300 @@ -8,6 +8,7 @@ private.properties webrev/* webrev.zip +.classpath *.class *.clazz *.log diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/bin/jjs --- a/nashorn/bin/jjs Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/bin/jjs Tue May 14 09:05:03 2013 -0300 @@ -26,4 +26,4 @@ [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1; -$JAVA_HOME/bin/java -server -XX:-TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $* +$JAVA_HOME/bin/java -server -XX:+TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $* diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/make/project.properties --- a/nashorn/make/project.properties Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/make/project.properties Tue May 14 09:05:03 2013 -0300 @@ -194,6 +194,8 @@ test262-test-sys-prop.test.js.exclude.dir=\ ${test262.suite.dir}/intl402/ +test262-test-sys-prop.test.failed.list.file=${build.dir}/test/failedTests + # test262 test frameworks test262-test-sys-prop.test.js.framework=\ -timezone=PST \ @@ -214,7 +216,7 @@ # -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods # add '-Dtest.js.outofprocess' to run each test in a new sub-process -run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8 +run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:+TieredCompilation -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8 #-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main} diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java --- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Tue May 14 09:05:03 2013 -0300 @@ -78,7 +78,6 @@ this(factory, DEFAULT_OPTIONS, appLoader); } - @SuppressWarnings("LeakingThisInConstructor") NashornScriptEngine(final NashornScriptEngineFactory factory, final String[] args, final ClassLoader appLoader) { this.factory = factory; final Options options = new Options("nashorn"); @@ -102,7 +101,7 @@ }); // create new global object - this.global = createNashornGlobal(); + this.global = createNashornGlobal(); // set the default engine scope for the default context context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE); diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java --- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java Tue May 14 09:05:03 2013 -0300 @@ -31,7 +31,6 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import jdk.nashorn.internal.runtime.Version; -import sun.reflect.Reflection; /** * JSR-223 compliant script engine factory for Nashorn. The engine answers for: diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/Attr.java --- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Tue May 14 09:05:03 2013 -0300 @@ -29,11 +29,13 @@ 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.LITERAL_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 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_CONSTANT; 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; @@ -73,8 +75,10 @@ import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; +import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.ir.TemporarySymbols; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.TryNode; import jdk.nashorn.internal.ir.UnaryNode; @@ -90,7 +94,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; /** @@ -129,13 +132,16 @@ private static final DebugLogger LOG = new DebugLogger("attr"); private static final boolean DEBUG = LOG.isEnabled(); + private final TemporarySymbols temporarySymbols; + /** * Constructor. */ - Attr() { - localDefs = new ArrayDeque<>(); - localUses = new ArrayDeque<>(); - returnTypes = new ArrayDeque<>(); + Attr(final TemporarySymbols temporarySymbols) { + this.temporarySymbols = temporarySymbols; + this.localDefs = new ArrayDeque<>(); + this.localUses = new ArrayDeque<>(); + this.returnTypes = new ArrayDeque<>(); } @Override @@ -150,67 +156,50 @@ @Override public Node leaveAccessNode(final AccessNode accessNode) { - ensureSymbol(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this - end(accessNode); - return accessNode; + //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this, that + //is why we can't set the access node base to be an object here, that will ruin access specialization + //for example for a.x | 17. + return end(ensureSymbol(Type.OBJECT, accessNode)); } - private void enterFunctionBody() { + private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) { + initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL, FunctionNode.FUNCTION_TYPE); + initCompileConstant(THIS, body, IS_PARAM | IS_THIS, Type.OBJECT); - final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); - final Block body = getLexicalContext().getCurrentBlock(); - initCallee(body); - initThis(body); if (functionNode.isVarArg()) { - initVarArg(body, functionNode.needsArguments()); + initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL, Type.OBJECT_ARRAY); + if (functionNode.needsArguments()) { + initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL, Type.typeFor(ScriptObject.class)); + addLocalDef(ARGUMENTS.symbolName()); + } } initParameters(functionNode, body); - initScope(body); - initReturn(body); + initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL, Type.typeFor(ScriptObject.class)); + initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL, Type.OBJECT); + } - if (functionNode.isProgram()) { - initFromPropertyMap(body); - } else if(!functionNode.isDeclared()) { - // It's neither declared nor program - it's a function expression then; assign it a self-symbol. - if (functionNode.getSymbol() != null) { - // a temporary left over from an earlier pass when the function was lazy - assert functionNode.getSymbol().isTemp(); - // remove it - functionNode.setSymbol(null); - } - final boolean anonymous = functionNode.isAnonymous(); - final String name = anonymous ? null : functionNode.getIdent().getName(); - if (anonymous || body.getExistingSymbol(name) != null) { - // The function is either anonymous, or another local identifier already trumps its name on entry: - // either it has the same name as one of its parameters, or is named "arguments" and also references the - // "arguments" identifier in its body. - ensureSymbol(functionNode, Type.typeFor(ScriptFunction.class), functionNode); - } else { - final Symbol selfSymbol = defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF, functionNode); - assert selfSymbol.isFunctionSelf(); - newType(selfSymbol, Type.OBJECT); - } - } - - /* - * This pushes all declarations (except for non-statements, i.e. for - * node temporaries) to the top of the function scope. This way we can - * get around problems like - * - * while (true) { - * break; - * if (true) { - * var s; - * } - * } - * - * to an arbitrary nesting depth. - * - * @see NASHORN-73 - */ - + /** + * This pushes all declarations (except for non-statements, i.e. for + * node temporaries) to the top of the function scope. This way we can + * get around problems like + * + * while (true) { + * break; + * if (true) { + * var s; + * } + * } + * + * to an arbitrary nesting depth. + * + * see NASHORN-73 + * + * @param functionNode the FunctionNode we are entering + * @param body the body of the FunctionNode we are entering + */ + private void acceptDeclarations(final FunctionNode functionNode, final Block body) { // This visitor will assign symbol to all declared variables, except function declarations (which are taken care // in a separate step above) and "var" declarations in for loop initializers. body.accept(new NodeOperatorVisitor() { @@ -220,27 +209,52 @@ } @Override - public boolean enterVarNode(final VarNode varNode) { - + public Node leaveVarNode(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(); - final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident)); + final IdentNode ident = varNode.getName(); + final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR); functionNode.addDeclaredSymbol(symbol); if (varNode.isFunctionDeclaration()) { newType(symbol, FunctionNode.FUNCTION_TYPE); } + return varNode.setName((IdentNode)ident.setSymbol(getLexicalContext(), symbol)); } - return false; + return varNode; } }); } + private void enterFunctionBody() { + + final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); + final Block body = getLexicalContext().getCurrentBlock(); + + initFunctionWideVariables(functionNode, body); + + if (functionNode.isProgram()) { + initFromPropertyMap(body); + } else if (!functionNode.isDeclared()) { + // It's neither declared nor program - it's a function expression then; assign it a self-symbol. + assert functionNode.getSymbol() == null; + + final boolean anonymous = functionNode.isAnonymous(); + final String name = anonymous ? null : functionNode.getIdent().getName(); + if (!(anonymous || body.getExistingSymbol(name) != null)) { + assert !anonymous && name != null; + newType(defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF), Type.OBJECT); + } + } + + acceptDeclarations(functionNode, body); + } + @Override public boolean enterBlock(final Block block) { start(block); + //ensure that we don't use information from a previous compile. This is very ugly TODO + //the symbols in the block should really be stateless + block.clearSymbols(); if (getLexicalContext().isFunctionBody()) { enterFunctionBody(); @@ -257,14 +271,13 @@ } @Override - public Node leaveCallNode(final CallNode callNode) { - ensureSymbol(callNode.getType(), callNode); - return end(callNode); + public boolean enterCallNode(final CallNode callNode) { + return start(callNode); } @Override - public boolean enterCallNode(final CallNode callNode) { - return start(callNode); + public Node leaveCallNode(final CallNode callNode) { + return end(ensureSymbol(callNode.getType(), callNode)); } @Override @@ -275,23 +288,31 @@ start(catchNode); // define block-local exception variable - final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception); + final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET); newType(def, Type.OBJECT); addLocalDef(exception.getName()); return true; } + @Override + public Node leaveCatchNode(final CatchNode catchNode) { + final IdentNode exception = catchNode.getException(); + final Block block = getLexicalContext().getCurrentBlock(); + final Symbol symbol = findSymbol(block, exception.getName()); + assert symbol != null; + return end(catchNode.setException((IdentNode)exception.setSymbol(getLexicalContext(), symbol))); + } + /** * 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) { + private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) { int flags = symbolFlags; Symbol symbol = findSymbol(block, name); // Locate symbol. @@ -337,7 +358,7 @@ // Create and add to appropriate block. symbol = new Symbol(name, flags); - symbolBlock.putSymbol(name, symbol); + symbolBlock.putSymbol(getLexicalContext(), symbol); if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { symbol.setNeedsSlot(true); @@ -346,10 +367,6 @@ symbol.setFlags(flags); } - if (node != null) { - node.setSymbol(symbol); - } - return symbol; } @@ -357,30 +374,22 @@ public boolean enterFunctionNode(final FunctionNode functionNode) { start(functionNode, false); + if (functionNode.isLazy()) { + return false; + } + + //an outermost function in our lexical context that is not a program (runScript) + //is possible - it is a function being compiled lazily if (functionNode.isDeclared()) { final Iterator 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(); + defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR); } } - 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; } @@ -390,9 +399,30 @@ final LexicalContext lc = getLexicalContext(); + final Block body = newFunctionNode.getBody(); + + //look for this function in the parent block + if (functionNode.isDeclared()) { + final Iterator blocks = getLexicalContext().getBlocks(); + if (blocks.hasNext()) { + newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, findSymbol(blocks.next(), functionNode.getIdent().getName())); + } + } else if (!functionNode.isProgram()) { + final boolean anonymous = functionNode.isAnonymous(); + final String name = anonymous ? null : functionNode.getIdent().getName(); + if (anonymous || body.getExistingSymbol(name) != null) { + newFunctionNode = (FunctionNode)ensureSymbol(lc, FunctionNode.FUNCTION_TYPE, newFunctionNode); + } else { + assert name != null; + final Symbol self = body.getExistingSymbol(name); + assert self != null && self.isFunctionSelf(); + newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, body.getExistingSymbol(name)); + } + } + //unknown parameters are promoted to object type. - finalizeParameters(newFunctionNode); - finalizeTypes(newFunctionNode); + newFunctionNode = finalizeParameters(newFunctionNode); + newFunctionNode = finalizeTypes(newFunctionNode); for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) { if (symbol.getSymbolType().isUnknown()) { symbol.setType(Type.OBJECT); @@ -400,8 +430,6 @@ } } - final Block body = newFunctionNode.getBody(); - if (newFunctionNode.hasLazyChildren()) { //the final body has already been assigned as we have left the function node block body by now objectifySymbols(body); @@ -409,9 +437,9 @@ if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) { final IdentNode callee = compilerConstant(CALLEE); - final VarNode selfInit = + VarNode selfInit = new VarNode( - newFunctionNode.getSource(), + newFunctionNode.getLineNumber(), newFunctionNode.getToken(), newFunctionNode.getFinish(), newFunctionNode.getIdent(), @@ -419,8 +447,7 @@ LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName()); - final List newStatements = new ArrayList<>(); - newStatements.add(selfInit); + final List newStatements = new ArrayList<>(); assert callee.getSymbol() != null && callee.getSymbol().hasSlot(); final IdentNode name = selfInit.getName(); @@ -428,9 +455,10 @@ assert nameSymbol != null; - name.setSymbol(nameSymbol); - selfInit.setSymbol(nameSymbol); + selfInit = selfInit.setName((IdentNode)name.setSymbol(lc, nameSymbol)); + selfInit = (VarNode)selfInit.setSymbol(lc, nameSymbol); + newStatements.add(selfInit); newStatements.addAll(body.getStatements()); newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements)); } @@ -447,34 +475,32 @@ end(newFunctionNode, false); - return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode)); + return newFunctionNode; } @Override public Node leaveCONVERT(final UnaryNode unaryNode) { assert false : "There should be no convert operators in IR during Attribution"; - end(unaryNode); - return unaryNode; + return end(unaryNode); } @Override - public boolean enterIdentNode(final IdentNode identNode) { + public Node leaveIdentNode(final IdentNode identNode) { final String name = identNode.getName(); start(identNode); + final LexicalContext lc = getLexicalContext(); + 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.unindent(); - identNode.setSymbol(pseudoSymbol); - return false; + return end(identNode.setSymbol(lc, pseudoSymbol)); } - final LexicalContext lc = getLexicalContext(); - final Block block = lc.getCurrentBlock(); - final Symbol oldSymbol = identNode.getSymbol(); + final Block block = lc.getCurrentBlock(); Symbol symbol = findSymbol(block, name); @@ -495,12 +521,11 @@ } } - identNode.setSymbol(symbol); // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already) maybeForceScope(symbol); } else { LOG.info("No symbol exists. Declare undefined: ", symbol); - symbol = defineSymbol(block, name, IS_GLOBAL, identNode); + symbol = defineSymbol(block, name, IS_GLOBAL); // we have never seen this before, it can be undefined newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway? symbol.setCanBeUndefined(); @@ -509,14 +534,12 @@ setBlockScope(name, symbol); - if (symbol != oldSymbol && !identNode.isInitializedHere()) { + if (!identNode.isInitializedHere()) { symbol.increaseUseCount(); } addLocalUse(identNode.getName()); - end(identNode); - - return false; + return end(identNode.setSymbol(lc, symbol)); } /** @@ -525,7 +548,7 @@ * @param symbol the symbol that might be scoped */ private void maybeForceScope(final Symbol symbol) { - if(!symbol.isScope() && symbolNeedsToBeScope(symbol)) { + if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) { Symbol.setSymbolIsScope(getLexicalContext(), symbol); } } @@ -612,11 +635,11 @@ private Symbol findSymbol(final Block block, final String name) { // Search up block chain to locate symbol. - for(final Iterator blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) { + for (final Iterator blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) { // Find name. final Symbol symbol = blocks.next().getExistingSymbol(name); // If found then we are good. - if(symbol != null) { + if (symbol != null) { return symbol; } } @@ -625,39 +648,19 @@ @Override public Node leaveIndexNode(final IndexNode indexNode) { - ensureSymbol(Type.OBJECT, indexNode); //TODO - return indexNode; + return end(ensureSymbol(Type.OBJECT, indexNode)); } @SuppressWarnings("rawtypes") @Override - 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 - - if (literalNode instanceof ArrayLiteralNode) { - final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; - final Node[] array = arrayLiteralNode.getValue(); - - for (int i = 0; i < array.length; i++) { - final Node element = array[i]; - if (element != null) { - array[i] = element.accept(this); - } - } - arrayLiteralNode.analyze(); - //array literal node now has an element type and all elements are attributed - } else { - assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported"; - } - - getLexicalContext().getCurrentFunction().newLiteral(literalNode); - } finally { - end(literalNode); + public Node leaveLiteralNode(final LiteralNode literalNode) { + assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens + assert literalNode instanceof ArrayLiteralNode || !(literalNode.getValue() instanceof Node) : "literals with Node values not supported"; + final Symbol symbol = new Symbol(getLexicalContext().getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType()); + if (literalNode instanceof ArrayLiteralNode) { + ((ArrayLiteralNode)literalNode).analyze(); } - - return false; + return end(literalNode.setSymbol(getLexicalContext(), symbol)); } @Override @@ -667,18 +670,13 @@ @Override public Node leaveObjectNode(final ObjectNode objectNode) { - ensureSymbol(Type.OBJECT, objectNode); - return end(objectNode); + return end(ensureSymbol(Type.OBJECT, objectNode)); } - //TODO is this correct why not leave? @Override - public boolean enterPropertyNode(final PropertyNode propertyNode) { + public Node leavePropertyNode(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 true; + return propertyNode.setSymbol(getLexicalContext(), new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); } @Override @@ -763,12 +761,9 @@ final IdentNode ident = varNode.getName(); final String name = ident.getName(); - final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR, ident); + final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR); assert symbol != null; - LOG.info("VarNode ", varNode, " set symbol ", symbol); - varNode.setSymbol(symbol); - // NASHORN-467 - use before definition of vars - conservative if (isLocalUse(ident.getName())) { newType(symbol, Type.OBJECT); @@ -780,22 +775,32 @@ @Override public Node leaveVarNode(final VarNode varNode) { - final Node init = varNode.getInit(); - final IdentNode ident = varNode.getName(); + VarNode newVarNode = varNode; + + final Node init = newVarNode.getInit(); + final IdentNode ident = newVarNode.getName(); final String name = ident.getName(); + final LexicalContext lc = getLexicalContext(); + final Symbol symbol = findSymbol(lc.getCurrentBlock(), ident.getName()); + if (init == null) { // var x; with no init will be treated like a use of x by - // visit(IdentNode) unless we remove the name - // from the localdef list. + // leaveIdentNode unless we remove the name from the localdef list. removeLocalDef(name); - return varNode; + return end(newVarNode.setSymbol(lc, symbol)); } addLocalDef(name); - final Symbol symbol = varNode.getSymbol(); - final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56 + assert symbol != null; + + final IdentNode newIdent = (IdentNode)ident.setSymbol(lc, symbol); + + newVarNode = newVarNode.setName(newIdent); + newVarNode = (VarNode)newVarNode.setSymbol(lc, symbol); + + final boolean isScript = lc.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()); @@ -803,36 +808,28 @@ newType(symbol, Type.OBJECT); } - assert varNode.hasType() : varNode; + assert newVarNode.hasType() : newVarNode + " has no type"; - end(varNode); - - return varNode; + return end(newVarNode); } @Override public Node leaveADD(final UnaryNode unaryNode) { - ensureSymbol(arithType(), unaryNode); - end(unaryNode); - return unaryNode; + return end(ensureSymbol(arithType(), unaryNode)); } @Override public Node leaveBIT_NOT(final UnaryNode unaryNode) { - ensureSymbol(Type.INT, unaryNode); - end(unaryNode); - return unaryNode; + return end(ensureSymbol(Type.INT, unaryNode)); } @Override public Node leaveDECINC(final UnaryNode unaryNode) { // @see assignOffset - ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs()); + final UnaryNode newUnaryNode = unaryNode.setRHS(ensureAssignmentSlots(unaryNode.rhs())); final Type type = arithType(); - newType(unaryNode.rhs().getSymbol(), type); - ensureSymbol(type, unaryNode); - end(unaryNode); - return unaryNode; + newType(newUnaryNode.rhs().getSymbol(), type); + return end(ensureSymbol(type, newUnaryNode)); } @Override @@ -908,23 +905,24 @@ @Override public Node leaveNEW(final UnaryNode unaryNode) { - ensureSymbol(Type.OBJECT, unaryNode); - end(unaryNode); - return unaryNode; + return end(ensureSymbol(Type.OBJECT, unaryNode)); } @Override public Node leaveNOT(final UnaryNode unaryNode) { - ensureSymbol(Type.BOOLEAN, unaryNode); - end(unaryNode); - return unaryNode; + return end(ensureSymbol(Type.BOOLEAN, 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; + return (IdentNode) + new IdentNode( + functionNode.getToken(), + functionNode.getFinish(), + cc.symbolName()). + setSymbol( + getLexicalContext(), + functionNode.compilerConstant(cc)); } @Override @@ -952,15 +950,12 @@ @Override public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { - ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode); - return runtimeNode; + return end(ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode)); } @Override public Node leaveSUB(final UnaryNode unaryNode) { - ensureSymbol(arithType(), unaryNode); - end(unaryNode); - return unaryNode; + return end(ensureSymbol(arithType(), unaryNode)); } @Override @@ -982,18 +977,16 @@ ensureTypeNotUnknown(lhs); ensureTypeNotUnknown(rhs); - ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode); - - end(binaryNode); - - return binaryNode; + //even if we are adding two known types, this can overflow. i.e. + //int and number -> number. + //int and int are also number though. + //something and object is object + return end(ensureSymbol(Type.widest(arithType(), Type.widest(lhs.getType(), rhs.getType())), binaryNode)); } @Override public Node leaveAND(final BinaryNode binaryNode) { - ensureSymbol(Type.OBJECT, binaryNode); - end(binaryNode); - return binaryNode; + return end(ensureSymbol(Type.OBJECT, binaryNode)); } /** @@ -1013,8 +1006,7 @@ Symbol symbol = findSymbol(block, name); if (symbol == null) { - symbol = defineSymbol(block, name, IS_GLOBAL, ident); - binaryNode.setSymbol(symbol); + symbol = defineSymbol(block, name, IS_GLOBAL); } else { maybeForceScope(symbol); } @@ -1025,6 +1017,31 @@ return true; } + + /** + * This assign helper is called after an assignment, when all children of + * the assign has been processed. It fixes the types and recursively makes + * sure that everyhing has slots that should have them in the chain. + * + * @param binaryNode assignment node + */ + private Node leaveAssignmentNode(final BinaryNode binaryNode) { + BinaryNode newBinaryNode = binaryNode; + + final Node lhs = binaryNode.lhs(); + final Node rhs = binaryNode.rhs(); + final Type type; + + if (rhs.getType().isNumeric()) { + type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); + } else { + type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too. + } + + newType(lhs.getSymbol(), type); + return end(ensureSymbol(type, newBinaryNode)); + } + private boolean isLocal(FunctionNode function, Symbol symbol) { 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 @@ -1173,14 +1190,12 @@ @Override public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { - ensureSymbol(binaryNode.rhs().getType(), binaryNode); - return binaryNode; + return end(ensureSymbol(binaryNode.rhs().getType(), binaryNode)); } @Override public Node leaveCOMMALEFT(final BinaryNode binaryNode) { - ensureSymbol(binaryNode.lhs().getType(), binaryNode); - return binaryNode; + return end(ensureSymbol(binaryNode.lhs().getType(), binaryNode)); } @Override @@ -1189,15 +1204,10 @@ } private Node leaveCmp(final BinaryNode binaryNode) { - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); + ensureTypeNotUnknown(binaryNode.lhs()); + ensureTypeNotUnknown(binaryNode.rhs()); - ensureSymbol(Type.BOOLEAN, binaryNode); - ensureTypeNotUnknown(lhs); - ensureTypeNotUnknown(rhs); - - end(binaryNode); - return binaryNode; + return end(ensureSymbol(Type.BOOLEAN, binaryNode)); } private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) { @@ -1207,11 +1217,9 @@ // as, say, an int : function(x) { return x & 4711 }, and x is not defined in // the function. to make this work, uncomment the following two type inferences // and debug. - //newType(binaryNode.lhs().getSymbol(), operandType); //newType(binaryNode.rhs().getSymbol(), operandType); - ensureSymbol(destType, binaryNode); - return binaryNode; + return ensureSymbol(destType, binaryNode); } private Node coerce(final BinaryNode binaryNode, final Type type) { @@ -1295,9 +1303,7 @@ @Override public Node leaveOR(final BinaryNode binaryNode) { - ensureSymbol(Type.OBJECT, binaryNode); - end(binaryNode); - return binaryNode; + return end(ensureSymbol(Type.OBJECT, binaryNode)); } @Override @@ -1346,50 +1352,13 @@ ensureTypeNotUnknown(rhs); final Type type = Type.widest(lhs.getType(), rhs.getType()); - ensureSymbol(type, ternaryNode); - - end(ternaryNode); - assert ternaryNode.getSymbol() != null; - - return ternaryNode; - } - - 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); - } - - 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); + return end(ensureSymbol(type, ternaryNode)); } - 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); - //return symbol is always object as it's the __return__ thing. What returnType is is another matter though - } - - private void initVarArg(final Block block, final boolean needsArguments) { - final Symbol varArgsSymbol = defineSymbol(block, VARARGS.symbolName(), IS_PARAM | IS_INTERNAL, null); - varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY); - varArgsSymbol.setNeedsSlot(true); - - if (needsArguments) { - final Symbol argumentsSymbol = defineSymbol(block, ARGUMENTS.symbolName(), IS_VAR | IS_INTERNAL, null); - newType(argumentsSymbol, Type.typeFor(ScriptObject.class)); - argumentsSymbol.setNeedsSlot(true); - addLocalDef(ARGUMENTS.symbolName()); - } - } - - private void initCallee(final Block block) { - final Symbol calleeSymbol = defineSymbol(block, CALLEE.symbolName(), IS_PARAM | IS_INTERNAL, null); - newType(calleeSymbol, FunctionNode.FUNCTION_TYPE); - calleeSymbol.setNeedsSlot(true); + private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) { + final Symbol symbol = defineSymbol(block, cc.symbolName(), flags); + newType(symbol, type); + symbol.setNeedsSlot(true); } /** @@ -1399,19 +1368,26 @@ * @param functionNode the function node */ private void initParameters(final FunctionNode functionNode, final Block body) { + int pos = 0; for (final IdentNode param : functionNode.getParameters()) { addLocalDef(param.getName()); - 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."); - } - newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType); + + final Type callSiteParamType = functionNode.getHints().getParameterType(pos); + int flags = IS_PARAM; + if (callSiteParamType != null) { + LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that."); + flags |= Symbol.IS_SPECIALIZED_PARAM; } - LOG.info("Initialized param ", paramSymbol); + final Symbol paramSymbol = defineSymbol(body, param.getName(), flags); + assert paramSymbol != null; + + newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType); + + LOG.info("Initialized param ", pos, "=", paramSymbol); + pos++; } + } /** @@ -1420,23 +1396,34 @@ * * @param functionNode functionNode */ - private static void finalizeParameters(final FunctionNode functionNode) { + private FunctionNode finalizeParameters(final FunctionNode functionNode) { + final List newParams = new ArrayList<>(); final boolean isVarArg = functionNode.isVarArg(); + final int nparams = functionNode.getParameters().size(); - for (final IdentNode ident : functionNode.getParameters()) { - final Symbol paramSymbol = ident.getSymbol(); + int specialize = 0; + int pos = 0; + for (final IdentNode param : functionNode.getParameters()) { + final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName()); + assert paramSymbol != null; + assert paramSymbol.isParam(); + newParams.add((IdentNode)param.setSymbol(getLexicalContext(), paramSymbol)); assert paramSymbol != null; - Type type = functionNode.getSpecializedType(ident); + Type type = functionNode.getHints().getParameterType(pos); if (type == null) { type = Type.OBJECT; } // if we know that a parameter is only used as a certain type throughout // 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()); + // call site is, use this information: + // we also need more than half of the parameters to be specializable + // for the heuristic to be worth it, and we need more than one use of + // the parameter to consider it, i.e. function(x) { call(x); } doens't count + if (paramSymbol.getUseCount() > 1 && !paramSymbol.getSymbolType().isObject()) { + LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType()); + specialize++; } newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType())); @@ -1445,7 +1432,17 @@ if (isVarArg) { paramSymbol.setNeedsSlot(false); } + + pos++; } + + FunctionNode newFunctionNode = functionNode; + + if (nparams == 0 || (specialize * 2) < nparams) { + newFunctionNode = newFunctionNode.clearSnapshot(getLexicalContext()); + } + + return newFunctionNode.setParameters(getLexicalContext(), newParams); } /** @@ -1459,7 +1456,7 @@ for (final Property property : map.getProperties()) { final String key = property.getKey(); - final Symbol symbol = defineSymbol(block, key, IS_GLOBAL, null); + final Symbol symbol = defineSymbol(block, key, IS_GLOBAL); newType(symbol, Type.OBJECT); LOG.info("Added global symbol from property map ", symbol); } @@ -1498,7 +1495,7 @@ * objects as parameters, for example +, but not *, which is known * to coerce types into doubles */ - if (node.getType().isUnknown() || symbol.isParam()) { + if (node.getType().isUnknown() || (symbol.isParam() && !symbol.isSpecializedParam())) { newType(symbol, Type.OBJECT); symbol.setCanBeUndefined(); } @@ -1520,19 +1517,25 @@ * * see NASHORN-258 * - * @param functionNode the current function node (has to be passed as it changes in the visitor below) * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes */ - private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) { - assignmentDest.accept(new NodeVisitor() { + private Node ensureAssignmentSlots(final Node assignmentDest) { + final LexicalContext attrLexicalContext = getLexicalContext(); + return assignmentDest.accept(new NodeVisitor() { @Override public Node leaveIndexNode(final IndexNode indexNode) { assert indexNode.getSymbol().isTemp(); final Node index = indexNode.getIndex(); //only temps can be set as needing slots. the others will self resolve //it is illegal to take a scope var and force it to be a slot, that breaks - if (index.getSymbol().isTemp() && !index.getSymbol().isConstant()) { - index.getSymbol().setNeedsSlot(true); + Symbol indexSymbol = index.getSymbol(); + if (indexSymbol.isTemp() && !indexSymbol.isConstant() && !indexSymbol.hasSlot()) { + if(indexSymbol.isShared()) { + indexSymbol = temporarySymbols.createUnshared(indexSymbol); + } + indexSymbol.setNeedsSlot(true); + attrLexicalContext.getCurrentBlock().putSymbol(attrLexicalContext, indexSymbol); + return indexNode.setIndex(index.setSymbol(attrLexicalContext, indexSymbol)); } return indexNode; } @@ -1557,22 +1560,30 @@ * * @param functionNode */ - private static void finalizeTypes(final FunctionNode functionNode) { + private FunctionNode finalizeTypes(final FunctionNode functionNode) { final Set changed = new HashSet<>(); + FunctionNode currentFunctionNode = functionNode; do { changed.clear(); - functionNode.accept(new NodeVisitor() { + final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor() { - private void widen(final Node node, final Type to) { + private Node widen(final Node node, final Type to) { if (node instanceof LiteralNode) { - return; + return node; } 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); - newType(node.getSymbol(), to); - changed.add(node); + LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to); + Symbol symbol = node.getSymbol(); + if(symbol.isShared() && symbol.wouldChangeType(to)) { + symbol = temporarySymbols.getTypedTemporarySymbol(to); + } + newType(symbol, to); + final Node newNode = node.setSymbol(getLexicalContext(), symbol); + changed.add(newNode); + return newNode; } + return node; } @Override @@ -1598,43 +1609,23 @@ @Override public Node leaveBinaryNode(final BinaryNode binaryNode) { final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); + BinaryNode newBinaryNode = binaryNode; switch (binaryNode.tokenType()) { default: if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) { break; } - widen(binaryNode.lhs(), widest); + newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest)); case ADD: - widen(binaryNode, widest); - break; + newBinaryNode = (BinaryNode)widen(newBinaryNode, widest); } - return binaryNode; + return newBinaryNode; } }); + getLexicalContext().replace(currentFunctionNode, newFunctionNode); + currentFunctionNode = newFunctionNode; } while (!changed.isEmpty()); - } - - /** - * This assign helper is called after an assignment, when all children of - * the assign has been processed. It fixes the types and recursively makes - * sure that everyhing has slots that should have them in the chain. - * - * @param binaryNode assignment node - */ - private Node leaveAssignmentNode(final BinaryNode binaryNode) { - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); - - final Type type; - if (rhs.getType().isNumeric()) { - type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); - } else { - type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too. - } - ensureSymbol(type, binaryNode); - newType(lhs.getSymbol(), type); - end(binaryNode); - return binaryNode; + return currentFunctionNode; } private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) { @@ -1646,25 +1637,18 @@ final Node lhs = binaryNode.lhs(); newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType - ensureSymbol(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(getLexicalContext().getCurrentFunction(), binaryNode); - - end(binaryNode); - return binaryNode; + return end(ensureSymbol(destType, ensureAssignmentSlots(binaryNode))); } - private Symbol ensureSymbol(final FunctionNode functionNode, final Type type, final Node node) { - LOG.info("New TEMPORARY added to ", functionNode.getName(), " type=", type); - return functionNode.ensureSymbol(getLexicalContext().getCurrentBlock(), type, node); - } - - private Symbol ensureSymbol(final Type type, final Node node) { - return ensureSymbol(getLexicalContext().getCurrentFunction(), type, node); + private Node ensureSymbol(final Type type, final Node node) { + LOG.info("New TEMPORARY added to ", getLexicalContext().getCurrentFunction().getName(), " type=", type); + return ensureSymbol(getLexicalContext(), type, node); } private Symbol newInternal(final String name, final Type type) { - final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL, null); + final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL); iter.setType(type); // NASHORN-73 return iter; } @@ -1721,6 +1705,10 @@ localUses.peek().add(name); } + private Node ensureSymbol(final LexicalContext lc, final Type type, final Node node) { + return temporarySymbols.ensureSymbol(lc, type, node); + } + /** * Pessimistically promote all symbols in current function node to Object types * This is done when the function contains unevaluated black boxes such as @@ -1731,8 +1719,7 @@ private static void objectifySymbols(final Block body) { body.accept(new NodeVisitor() { private void toObject(final Block block) { - for (final Iterator iter = block.symbolIterator(); iter.hasNext();) { - final Symbol symbol = iter.next(); + for (final Symbol symbol : block.getSymbols()) { if (!symbol.isTemp()) { newType(symbol, Type.OBJECT); } @@ -1788,6 +1775,10 @@ } private Node end(final Node node, final boolean printNode) { + if(node instanceof Statement) { + // If we're done with a statement, all temporaries can be reused. + temporarySymbols.reuse(); + } if (DEBUG) { final StringBuilder sb = new StringBuilder(); diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue May 14 09:05:03 2013 -0300 @@ -63,6 +63,7 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; + import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.CompilerConstants.Call; import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode; @@ -88,7 +89,6 @@ import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContextNode; -import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; @@ -100,6 +100,7 @@ import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.TernaryNode; @@ -191,6 +192,8 @@ /** Current compile unit */ private CompileUnit unit; + private int lastLineNumber = -1; + /** When should we stop caching regexp expressions in fields to limit bytecode size? */ private static final int MAX_REGEX_FIELDS = 2 * 1024; @@ -261,14 +264,15 @@ return method.load(symbol); } - final String name = symbol.getName(); + final String name = symbol.getName(); + final Source source = getLexicalContext().getCurrentFunction().getSource(); if (CompilerConstants.__FILE__.name().equals(name)) { - return method.load(identNode.getSource().getName()); + return method.load(source.getName()); } else if (CompilerConstants.__DIR__.name().equals(name)) { - return method.load(identNode.getSource().getBase()); + return method.load(source.getBase()); } else if (CompilerConstants.__LINE__.name().equals(name)) { - return method.load(identNode.getSource().getLine(identNode.position())).convert(Type.OBJECT); + return method.load(source.getLine(identNode.position())).convert(Type.OBJECT); } else { assert identNode.getSymbol().isScope() : identNode + " is not in scope!"; @@ -568,8 +572,7 @@ * @param block block containing symbols. */ private void symbolInfo(final Block block) { - for (final Iterator iter = block.symbolIterator(); iter.hasNext(); ) { - final Symbol symbol = iter.next(); + for (final Symbol symbol : block.getSymbols()) { if (symbol.hasSlot()) { method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel()); } @@ -619,6 +622,8 @@ @Override public boolean enterBreakNode(final BreakNode breakNode) { + lineNumber(breakNode); + final BreakableNode breakFrom = getLexicalContext().getBreakable(breakNode.getLabel()); for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(breakFrom); i++) { closeWith(); @@ -663,6 +668,8 @@ @Override public boolean enterCallNode(final CallNode callNode) { + lineNumber(callNode); + final List args = callNode.getArgs(); final Node function = callNode.getFunction(); final Block currentBlock = getLexicalContext().getCurrentBlock(); @@ -836,6 +843,8 @@ @Override public boolean enterContinueNode(final ContinueNode continueNode) { + lineNumber(continueNode); + final LoopNode continueTo = getLexicalContext().getContinueTo(continueNode.getLabel()); for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(continueTo); i++) { closeWith(); @@ -847,11 +856,15 @@ @Override public boolean enterEmptyNode(final EmptyNode emptyNode) { + lineNumber(emptyNode); + return false; } @Override public boolean enterExecuteNode(final ExecuteNode executeNode) { + lineNumber(executeNode); + final Node expression = executeNode.getExpression(); expression.accept(this); @@ -860,6 +873,8 @@ @Override public boolean enterForNode(final ForNode forNode) { + lineNumber(forNode); + final Node test = forNode.getTest(); final Block body = forNode.getBody(); final Node modify = forNode.getModify(); @@ -937,11 +952,10 @@ private static int assignSlots(final Block block, final int firstSlot) { int nextSlot = firstSlot; - for (final Iterator iter = block.symbolIterator(); iter.hasNext(); ) { - final Symbol next = iter.next(); - if (next.hasSlot()) { - next.setSlot(nextSlot); - nextSlot += next.slotCount(); + for (final Symbol symbol : block.getSymbols()) { + if (symbol.hasSlot()) { + symbol.setSlot(nextSlot); + nextSlot += symbol.slotCount(); } } return nextSlot; @@ -1002,10 +1016,7 @@ final boolean hasArguments = function.needsArguments(); - final Iterator symbols = block.symbolIterator(); - - while (symbols.hasNext()) { - final Symbol symbol = symbols.next(); + for (final Symbol symbol : block.getSymbols()) { if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) { continue; @@ -1076,12 +1087,7 @@ } } - final Iterator iter = block.symbolIterator(); - final List symbols = new ArrayList<>(); - while (iter.hasNext()) { - symbols.add(iter.next()); - } - initSymbols(symbols); + initSymbols(block.getSymbols()); } // Debugging: print symbols? @see --print-symbols flag @@ -1157,6 +1163,8 @@ @Override public boolean enterIfNode(final IfNode ifNode) { + lineNumber(ifNode); + final Node test = ifNode.getTest(); final Block pass = ifNode.getPass(); final Block fail = ifNode.getFail(); @@ -1196,12 +1204,12 @@ return false; } - @Override - public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) { - final Label label = new Label((String)null); - method.label(label); - method.lineNumber(lineNumberNode.getLineNumber(), label); - return false; + private void lineNumber(final Statement statement) { + final int lineNumber = statement.getLineNumber(); + if (lineNumber != lastLineNumber) { + method.lineNumber(statement.getLineNumber()); + } + lastLineNumber = lineNumber; } /** @@ -1533,6 +1541,8 @@ @Override public boolean enterReturnNode(final ReturnNode returnNode) { + lineNumber(returnNode); + method.registerReturn(); final Type returnType = getLexicalContext().getCurrentFunction().getReturnType(); @@ -1742,6 +1752,8 @@ @Override public boolean enterSplitNode(final SplitNode splitNode) { + lineNumber(splitNode); + final CompileUnit splitCompileUnit = splitNode.getCompileUnit(); final FunctionNode fn = getLexicalContext().getCurrentFunction(); @@ -1885,6 +1897,8 @@ @Override public boolean enterSwitchNode(final SwitchNode switchNode) { + lineNumber(switchNode); + final Node expression = switchNode.getExpression(); final Symbol tag = switchNode.getTag(); final boolean allInteger = tag.getSymbolType().isInteger(); @@ -1967,7 +1981,6 @@ method.tableswitch(lo, hi, defaultLabel, table); } else { final int[] ints = new int[size]; - for (int i = 0; i < size; i++) { ints[i] = values[i]; } @@ -2013,10 +2026,13 @@ @Override public boolean enterThrowNode(final ThrowNode throwNode) { + lineNumber(throwNode); + method._new(ECMAException.class).dup(); + final Source source = getLexicalContext().getCurrentFunction().getSource(); + final Node expression = throwNode.getExpression(); - final Source source = throwNode.getSource(); final int position = throwNode.position(); final int line = source.getLine(position); final int column = source.getColumn(position); @@ -2036,6 +2052,8 @@ @Override public boolean enterTryNode(final TryNode tryNode) { + lineNumber(tryNode); + final Block body = tryNode.getBody(); final List catchBlocks = tryNode.getCatchBlocks(); final Symbol symbol = tryNode.getException(); @@ -2132,12 +2150,15 @@ @Override public boolean enterVarNode(final VarNode varNode) { + final Node init = varNode.getInit(); if (init == null) { return false; } + lineNumber(varNode); + final Symbol varSymbol = varNode.getSymbol(); assert varSymbol != null : "variable node " + varNode + " requires a symbol"; @@ -2170,6 +2191,8 @@ @Override public boolean enterWhileNode(final WhileNode whileNode) { + lineNumber(whileNode); + final Node test = whileNode.getTest(); final Block body = whileNode.getBody(); final Label breakLabel = whileNode.getBreakLabel(); @@ -2192,7 +2215,7 @@ } private void closeWith() { - if(method.hasScope()) { + if (method.hasScope()) { method.loadCompilerConstant(SCOPE); method.invoke(ScriptRuntime.CLOSE_WITH); method.storeCompilerConstant(SCOPE); @@ -2235,7 +2258,7 @@ // Always process body body.accept(this); - if(hasScope) { + if (hasScope) { // Ensure we always close the WithObject final Label endLabel = new Label("with_end"); final Label catchLabel = new Label("with_catch"); @@ -2364,7 +2387,6 @@ public boolean enterDISCARD(final UnaryNode unaryNode) { final Node rhs = unaryNode.rhs(); - // System.err.println("**** Enter discard " + unaryNode); discard.push(rhs); load(rhs); @@ -2373,7 +2395,7 @@ method.pop(); discard.pop(); } - // System.err.println("**** Leave discard " + unaryNode); + return false; } @@ -3019,12 +3041,12 @@ * @param block the block we are in * @param ident identifier for block or function where applicable */ + @SuppressWarnings("resource") private void printSymbols(final Block block, final String ident) { if (!compiler.getEnv()._print_symbols) { return; } - @SuppressWarnings("resource") final PrintWriter out = compiler.getEnv().getErr(); out.println("[BLOCK in '" + ident + "']"); if (!block.printSymbols(out)) { @@ -3200,9 +3222,6 @@ 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); @@ -3298,7 +3317,7 @@ // 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 || + if (lc.getOutermostFunction() == functionNode || (!functionNode.needsCallee()) && lc.isFunctionDefinedInCurrentCall(originalFunctionNode)) { return; } diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java --- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Tue May 14 09:05:03 2013 -0300 @@ -21,6 +21,7 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.TemporarySymbols; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; @@ -42,7 +43,7 @@ */ LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) { @Override - FunctionNode transform(final Compiler compiler, final FunctionNode fn0) { + FunctionNode transform(final Compiler compiler, final FunctionNode fn) { /* * For lazy compilation, we might be given a node previously marked @@ -58,8 +59,7 @@ * function from a trampoline */ - final FunctionNode outermostFunctionNode = compiler.getFunctionNode(); - assert outermostFunctionNode == fn0; + final FunctionNode outermostFunctionNode = fn; final Set neverLazy = new HashSet<>(); final Set lazy = new HashSet<>(); @@ -172,20 +172,31 @@ ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) { @Override FunctionNode transform(final Compiler compiler, final FunctionNode fn) { - return (FunctionNode)initReturnTypes(fn).accept(new Attr()); + final TemporarySymbols ts = compiler.getTemporarySymbols(); + final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(ts)); + if(compiler.getEnv()._print_mem_usage) { + Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount()); + } + return newFunctionNode; } /** * Pessimistically set all lazy functions' return types to Object + * and the function symbols to object * @param functionNode node where to start iterating */ - private FunctionNode initReturnTypes(final FunctionNode functionNode) { + private FunctionNode enterAttr(final FunctionNode functionNode, final TemporarySymbols ts) { 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); + final LexicalContext lc = getLexicalContext(); + if (node.isLazy()) { + FunctionNode newNode = node.setReturnType(getLexicalContext(), Type.OBJECT); + return ts.ensureSymbol(lc, Type.OBJECT, newNode); + } + //node may have a reference here that needs to be nulled if it was referred to by + //its outer context, if it is lazy and not attributed + return node.setReturnType(lc, Type.UNKNOWN).setSymbol(lc, null); } }); } @@ -207,6 +218,7 @@ FunctionNode transform(final Compiler compiler, final FunctionNode fn) { final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName()); +// assert fn.isProgram() ; final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn); assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit; @@ -216,15 +228,6 @@ 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; } @@ -252,7 +255,7 @@ FunctionNode transform(final Compiler compiler, final FunctionNode fn) { final ScriptEnvironment env = compiler.getEnv(); - final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes()); + final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes(compiler.getTemporarySymbols())); if (env._print_lower_ast) { env.getErr().println(new ASTWriter(newFunctionNode)); diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/Compiler.java --- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Tue May 14 09:05:03 2013 -0300 @@ -36,26 +36,32 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; +import jdk.nashorn.internal.ir.TemporarySymbols; + import java.io.File; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; - import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.debug.ClassHistogramElement; +import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator; import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.ScriptEnvironment; @@ -77,6 +83,8 @@ /** Name of the objects package */ public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects"; + private Source source; + private final Map bytecode; private final Set compileUnits; @@ -87,13 +95,13 @@ private final ScriptEnvironment env; - private final String scriptName; + private String scriptName; private boolean strict; - private FunctionNode functionNode; + private CodeInstaller installer; - private CodeInstaller installer; + private final TemporarySymbols temporarySymbols = new TemporarySymbols(); /** logger for compiler, trampolines, splits and related code generation events * that affect classes */ @@ -168,6 +176,41 @@ } /** + * Environment information known to the compile, e.g. params + */ + public static class Hints { + private final Type[] paramTypes; + + /** singleton empty hints */ + public static final Hints EMPTY = new Hints(); + + private Hints() { + this.paramTypes = null; + } + + /** + * Constructor + * @param paramTypes known parameter types for this callsite + */ + public Hints(final Type[] paramTypes) { + this.paramTypes = paramTypes; + } + + /** + * Get the parameter type for this parameter position, or + * null if now known + * @param pos position + * @return parameter type for this callsite if known + */ + public Type getParameterType(final int pos) { + if (paramTypes != null && pos < paramTypes.length) { + return paramTypes[pos]; + } + return null; + } + } + + /** * Standard (non-lazy) compilation, that basically will take an entire script * and JIT it at once. This can lead to long startup time and fewer type * specializations @@ -207,21 +250,22 @@ * @param strict should this compilation use strict mode semantics */ //TODO support an array of FunctionNodes for batch lazy compilation - Compiler(final ScriptEnvironment env, final CodeInstaller installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) { + Compiler(final ScriptEnvironment env, final CodeInstaller installer, final CompilationSequence sequence, final boolean strict) { this.env = env; - this.functionNode = functionNode; this.sequence = sequence; this.installer = installer; - this.strict = strict || functionNode.isStrict(); this.constantData = new ConstantData(); this.compileUnits = new HashSet<>(); this.bytecode = new HashMap<>(); + } + private void initCompiler(final FunctionNode functionNode) { + this.strict = strict || functionNode.isStrict(); final StringBuilder sb = new StringBuilder(); sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))). append('$'). append(safeSourceName(functionNode.getSource())); - + this.source = functionNode.getSource(); this.scriptName = sb.toString(); } @@ -229,52 +273,79 @@ * Constructor * * @param installer code installer - * @param functionNode function node (in any available {@link CompilationState}) to compile * @param strict should this compilation use strict mode semantics */ - public Compiler(final CodeInstaller installer, final FunctionNode functionNode, final boolean strict) { - this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), strict); + public Compiler(final CodeInstaller installer, final boolean strict) { + this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), strict); } /** * Constructor - compilation will use the same strict semantics as in script environment * * @param installer code installer - * @param functionNode function node (in any available {@link CompilationState}) to compile */ - public Compiler(final CodeInstaller installer, final FunctionNode functionNode) { - this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict); + public Compiler(final CodeInstaller installer) { + this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict); } /** * Constructor - compilation needs no installer, but uses a script environment * Used in "compile only" scenarios * @param env a script environment - * @param functionNode functionNode to compile */ - public Compiler(final ScriptEnvironment env, final FunctionNode functionNode) { - this(env, null, functionNode, sequence(env._lazy_compilation), env._strict); + public Compiler(final ScriptEnvironment env) { + this(env, null, sequence(env._lazy_compilation), env._strict); + } + + private static void printMemoryUsage(final String phaseName, final FunctionNode functionNode) { + LOG.info(phaseName + " finished. Doing IR size calculation..."); + + final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification()); + osc.calculateObjectSize(functionNode); + + final List list = osc.getClassHistogram(); + + final StringBuilder sb = new StringBuilder(); + final long totalSize = osc.calculateObjectSize(functionNode); + sb.append(phaseName).append(" Total size = ").append(totalSize / 1024 / 1024).append("MB"); + LOG.info(sb); + + Collections.sort(list, new Comparator() { + @Override + public int compare(ClassHistogramElement o1, ClassHistogramElement o2) { + final long diff = o1.getBytes() - o2.getBytes(); + if (diff < 0) { + return 1; + } else if (diff > 0) { + return -1; + } else { + return 0; + } + } + }); + for (final ClassHistogramElement e : list) { + final String line = String.format(" %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances()); + LOG.info(line); + if (e.getBytes() < totalSize / 200) { + LOG.info(" ..."); + break; // never mind, so little memory anyway + } + } } /** * Execute the compilation this Compiler was created with - * @params param types if known, for specialization + * @param functionNode function node to compile from its current state * @throws CompilationException if something goes wrong * @return function node that results from code transforms */ - public FunctionNode compile() throws CompilationException { - return compile(null); - } + public FunctionNode compile(final FunctionNode functionNode) throws CompilationException { + FunctionNode newFunctionNode = functionNode; - /** - * Execute the compilation this Compiler was created with - * @param paramTypes param types if known, for specialization - * @throws CompilationException if something goes wrong - * @return function node that results from code transforms - */ - public FunctionNode compile(final Class paramTypes) throws CompilationException { + initCompiler(newFunctionNode); //TODO move this state into functionnode? + for (final String reservedName : RESERVED_NAMES) { - functionNode.uniqueName(reservedName); + newFunctionNode.uniqueName(reservedName); } final boolean fine = !LOG.levelAbove(Level.FINE); @@ -283,7 +354,11 @@ long time = 0L; for (final CompilationPhase phase : sequence) { - this.functionNode = phase.apply(this, functionNode); + newFunctionNode = phase.apply(this, newFunctionNode); + + if (env._print_mem_usage) { + printMemoryUsage(phase.toString(), newFunctionNode); + } final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L; time += duration; @@ -293,7 +368,7 @@ sb.append(phase.toString()). append(" done for function '"). - append(functionNode.getName()). + append(newFunctionNode.getName()). append('\''); if (duration > 0L) { @@ -309,7 +384,7 @@ if (info) { final StringBuilder sb = new StringBuilder(); sb.append("Compile job for '"). - append(functionNode.getName()). + append(newFunctionNode.getName()). append("' finished"); if (time > 0L) { @@ -321,7 +396,7 @@ LOG.info(sb); } - return functionNode; + return newFunctionNode; } private Class install(final String className, final byte[] code) { @@ -330,7 +405,6 @@ final Class clazz = installer.install(Compiler.binaryName(className), code); try { - final Source source = getSource(); final Object[] constants = getConstantData().toArray(); // Need doPrivileged because these fields are private AccessController.doPrivileged(new PrivilegedExceptionAction() { @@ -355,9 +429,10 @@ /** * Install compiled classes into a given loader + * @param functionNode function node to install - must be in {@link CompilationState#EMITTED} state * @return root script class - if there are several compile units they will also be installed */ - public Class install() { + public Class install(final FunctionNode functionNode) { final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L; assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed"; @@ -430,10 +505,6 @@ this.strict = strict; } - FunctionNode getFunctionNode() { - return functionNode; - } - ConstantData getConstantData() { return constantData; } @@ -442,8 +513,8 @@ return installer; } - Source getSource() { - return functionNode.getSource(); + TemporarySymbols getTemporarySymbols() { + return temporarySymbols; } void addClass(final String name, final byte[] code) { @@ -496,7 +567,7 @@ } private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) { - final ClassEmitter classEmitter = new ClassEmitter(env, functionNode.getSource().getName(), unitClassName, strict); + final ClassEmitter classEmitter = new ClassEmitter(env, source.getName(), unitClassName, strict); final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); classEmitter.begin(); @@ -550,6 +621,4 @@ USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic"); assert !USE_INT_ARITH : "Integer arithmetic is not enabled"; } - - } diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java --- a/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java Tue May 14 09:05:03 2013 -0300 @@ -105,25 +105,25 @@ ARGUMENTS("arguments", Object.class, 2), /** prefix for iterators for for (x in ...) */ - ITERATOR_PREFIX(":iter"), + ITERATOR_PREFIX(":i"), /** prefix for tag variable used for switch evaluation */ - SWITCH_TAG_PREFIX(":tag"), + SWITCH_TAG_PREFIX(":s"), /** prefix for all exceptions */ - EXCEPTION_PREFIX(":exception"), + EXCEPTION_PREFIX(":e"), /** prefix for quick slots generated in Store */ - QUICK_PREFIX(":quick"), + QUICK_PREFIX(":q"), /** prefix for temporary variables */ - TEMP_PREFIX(":temp"), + TEMP_PREFIX(":t"), /** prefix for literals */ - LITERAL_PREFIX(":lit"), + LITERAL_PREFIX(":l"), /** prefix for regexps */ - REGEX_PREFIX(":regex"), + REGEX_PREFIX(":r"), /** "this" used in non-static Java methods; always in slot 0 */ JAVA_THIS(null, 0), diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java --- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Tue May 14 09:05:03 2013 -0300 @@ -30,7 +30,6 @@ 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; @@ -56,6 +55,7 @@ import jdk.nashorn.internal.ir.RuntimeNode.Request; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.ir.TemporarySymbols; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.ThrowNode; import jdk.nashorn.internal.ir.TypeOverride; @@ -88,7 +88,10 @@ private static final DebugLogger LOG = new DebugLogger("finalize"); - FinalizeTypes() { + private final TemporarySymbols temporarySymbols; + + FinalizeTypes(final TemporarySymbols temporarySymbols) { + this.temporarySymbols = temporarySymbols; } @Override @@ -228,21 +231,27 @@ return leaveASSIGN(binaryNode); } + private boolean symbolIsInteger(Node node) { + final Symbol symbol = node.getSymbol(); + assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + getLexicalContext().getCurrentFunction().getSource(); + return true; + } + @Override - public Node leaveBIT_AND(BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol(); + public Node leaveBIT_AND(final BinaryNode binaryNode) { + assert symbolIsInteger(binaryNode); return leaveBinary(binaryNode, Type.INT, Type.INT); } @Override - public Node leaveBIT_OR(BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol(); + public Node leaveBIT_OR(final BinaryNode binaryNode) { + assert symbolIsInteger(binaryNode); return leaveBinary(binaryNode, Type.INT, Type.INT); } @Override - public Node leaveBIT_XOR(BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol(); + public Node leaveBIT_XOR(final BinaryNode binaryNode) { + assert symbolIsInteger(binaryNode); return leaveBinary(binaryNode, Type.INT, Type.INT); } @@ -252,8 +261,7 @@ 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()); - return newBinaryNode; + return propagateType(newBinaryNode, newBinaryNode.lhs().getType()); } @Override @@ -262,8 +270,7 @@ final BinaryNode newBinaryNode = binaryNode.setLHS(discard(binaryNode.lhs())); // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed // in that case, update the node type as well - propagateType(newBinaryNode, newBinaryNode.rhs().getType()); - return newBinaryNode; + return propagateType(newBinaryNode, newBinaryNode.rhs().getType()); } @Override @@ -354,13 +361,6 @@ return true; } - /* - @Override - 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(); @@ -372,6 +372,7 @@ @Override public Node leaveExecuteNode(final ExecuteNode executeNode) { + temporarySymbols.reuse(); return executeNode.setExpression(discard(executeNode.getExpression())); } @@ -497,8 +498,8 @@ @Override public Node leaveVarNode(final VarNode varNode) { - final Node rhs = varNode.getInit(); - if (rhs != null) { + final Node init = varNode.getInit(); + if (init != null) { final SpecializedNode specialized = specialize(varNode); final VarNode specVarNode = (VarNode)specialized.node; Type destType = specialized.type; @@ -506,8 +507,11 @@ destType = specVarNode.getType(); } assert specVarNode.hasType() : specVarNode + " doesn't have a type"; - return specVarNode.setInit(convert(rhs, destType)); + final Node convertedInit = convert(init, destType); + temporarySymbols.reuse(); + return specVarNode.setInit(convertedInit); } + temporarySymbols.reuse(); return varNode; } @@ -551,8 +555,7 @@ final boolean allVarsInScope = functionNode.allVarsInScope(); final boolean isVarArg = functionNode.isVarArg(); - for (final Iterator iter = block.symbolIterator(); iter.hasNext(); ) { - final Symbol symbol = iter.next(); + for (final Symbol symbol : block.getSymbols()) { if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) { continue; } @@ -687,7 +690,7 @@ } } - private static SpecializedNode specialize(final Assignment assignment) { + SpecializedNode specialize(final Assignment assignment) { final Node node = ((Node)assignment); final T lhs = assignment.getAssignmentDest(); final Node rhs = assignment.getAssignmentSource(); @@ -709,9 +712,9 @@ } final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to)); - propagateType(newNode, to); + final Node typePropagatedNode = propagateType(newNode, to); - return new SpecializedNode(newNode, to); + return new SpecializedNode(typePropagatedNode, to); } @@ -750,7 +753,7 @@ * @param to new type */ @SuppressWarnings("unchecked") - private static T setTypeOverride(final T node, final Type to) { + 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); @@ -759,7 +762,7 @@ } } LOG.info("Type override for lhs in '", node, "' => ", to); - return ((TypeOverride)node).setType(to); + return ((TypeOverride)node).setType(temporarySymbols, getLexicalContext(), to); } /** @@ -782,7 +785,7 @@ 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 + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction() + " " + node.getSource(); + assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction(); assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction(); final Type from = node.getType(); @@ -807,24 +810,22 @@ assert node instanceof TypeOverride; return setTypeOverride(node, to); } - resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node); + resultNode = new UnaryNode(Token.recast(node.getToken(), TokenType.CONVERT), node); } LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'"); + assert !node.isTerminal(); + 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. - lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode); - - assert !node.isTerminal(); - - return resultNode; + return temporarySymbols.ensureSymbol(lc, to, resultNode); } private static Node discard(final Node node) { if (node.getSymbol() != null) { - final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node); + final Node discard = new UnaryNode(Token.recast(node.getToken(), TokenType.DISCARD), node); //discard never has a symbol in the discard node - then it would be a nop assert !node.isTerminal(); return discard; @@ -847,12 +848,13 @@ * @param node * @param to */ - private static void propagateType(final Node node, final Type to) { - final Symbol symbol = node.getSymbol(); - if (symbol.isTemp()) { - symbol.setTypeOverride(to); + private Node propagateType(final Node node, final Type to) { + Symbol symbol = node.getSymbol(); + if (symbol.isTemp() && symbol.getSymbolType() != to) { + symbol = symbol.setTypeOverrideShared(to, temporarySymbols); LOG.info("Type override for temporary in '", node, "' => ", to); } + return node.setSymbol(getLexicalContext(), symbol); } /** @@ -877,7 +879,7 @@ * Whenever an explicit conversion is needed and the convertee is a literal, we can * just change the literal */ - static class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator> { + class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator> { private final Type type; LiteralNodeConstantEvaluator(final LiteralNode parent, final Type type) { @@ -892,20 +894,20 @@ LiteralNode literalNode = null; if (type.isString()) { - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toString(value)); + literalNode = LiteralNode.newInstance(token, finish, JSType.toString(value)); } else if (type.isBoolean()) { - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toBoolean(value)); + literalNode = LiteralNode.newInstance(token, finish, JSType.toBoolean(value)); } else if (type.isInteger()) { - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toInt32(value)); + literalNode = LiteralNode.newInstance(token, finish, JSType.toInt32(value)); } else if (type.isLong()) { - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toLong(value)); + literalNode = LiteralNode.newInstance(token, finish, JSType.toLong(value)); } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) { - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toNumber(value)); + literalNode = LiteralNode.newInstance(token, finish, JSType.toNumber(value)); } if (literalNode != null) { //inherit literal symbol for attr. - literalNode.setSymbol(parent.getSymbol()); + literalNode = (LiteralNode)literalNode.setSymbol(getLexicalContext(), parent.getSymbol()); } return literalNode; diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java --- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Tue May 14 09:05:03 2013 -0300 @@ -41,7 +41,6 @@ import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptRuntime; -import jdk.nashorn.internal.runtime.Source; /** * Simple constant folding pass, executed before IR is starting to be lowered. @@ -89,7 +88,7 @@ if (test instanceof LiteralNode) { final Block shortCut = ((LiteralNode)test).isTrue() ? ifNode.getPass() : ifNode.getFail(); if (shortCut != null) { - return new ExecuteNode(shortCut); + return new ExecuteNode(shortCut.getLineNumber(), shortCut.getToken(), shortCut.getFinish(), shortCut); } return new EmptyNode(ifNode); } @@ -112,13 +111,11 @@ */ abstract static class ConstantEvaluator { protected T parent; - protected final Source source; protected final long token; protected final int finish; protected ConstantEvaluator(final T parent) { this.parent = parent; - this.source = parent.getSource(); this.token = parent.getToken(); this.finish = parent.getFinish(); } @@ -152,23 +149,23 @@ switch (parent.tokenType()) { case ADD: if (rhsInteger) { - literalNode = LiteralNode.newInstance(source, token, finish, rhs.getInt32()); + literalNode = LiteralNode.newInstance(token, finish, rhs.getInt32()); } else { - literalNode = LiteralNode.newInstance(source, token, finish, rhs.getNumber()); + literalNode = LiteralNode.newInstance(token, finish, rhs.getNumber()); } break; case SUB: if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js - literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getInt32()); + literalNode = LiteralNode.newInstance(token, finish, -rhs.getInt32()); } else { - literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getNumber()); + literalNode = LiteralNode.newInstance(token, finish, -rhs.getNumber()); } break; case NOT: - literalNode = LiteralNode.newInstance(source, token, finish, !rhs.getBoolean()); + literalNode = LiteralNode.newInstance(token, finish, !rhs.getBoolean()); break; case BIT_NOT: - literalNode = LiteralNode.newInstance(source, token, finish, ~rhs.getInt32()); + literalNode = LiteralNode.newInstance(token, finish, ~rhs.getInt32()); break; default: return null; @@ -234,7 +231,7 @@ break; } assert res instanceof CharSequence : res + " was not a CharSequence, it was a " + res.getClass(); - return LiteralNode.newInstance(source, token, finish, res.toString()); + return LiteralNode.newInstance(token, finish, res.toString()); } return null; case MUL: @@ -247,33 +244,33 @@ value = lhs.getNumber() - rhs.getNumber(); break; case SHR: - return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & JSType.MAX_UINT); + return LiteralNode.newInstance(token, finish, (lhs.getInt32() >>> rhs.getInt32()) & JSType.MAX_UINT); case SAR: - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32()); + return LiteralNode.newInstance(token, finish, lhs.getInt32() >> rhs.getInt32()); case SHL: - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() << rhs.getInt32()); + return LiteralNode.newInstance(token, finish, lhs.getInt32() << rhs.getInt32()); case BIT_XOR: - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() ^ rhs.getInt32()); + return LiteralNode.newInstance(token, finish, lhs.getInt32() ^ rhs.getInt32()); case BIT_AND: - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() & rhs.getInt32()); + return LiteralNode.newInstance(token, finish, lhs.getInt32() & rhs.getInt32()); case BIT_OR: - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() | rhs.getInt32()); + return LiteralNode.newInstance(token, finish, lhs.getInt32() | rhs.getInt32()); case GE: - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject())); + return LiteralNode.newInstance(token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject())); case LE: - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject())); + return LiteralNode.newInstance(token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject())); case GT: - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject())); + return LiteralNode.newInstance(token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject())); case LT: - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject())); + return LiteralNode.newInstance(token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject())); case NE: - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject())); + return LiteralNode.newInstance(token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject())); case NE_STRICT: - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject())); + return LiteralNode.newInstance(token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject())); case EQ: - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject())); + return LiteralNode.newInstance(token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject())); case EQ_STRICT: - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject())); + return LiteralNode.newInstance(token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject())); default: return null; } @@ -282,12 +279,12 @@ isLong &= value != 0.0 && JSType.isRepresentableAsLong(value); if (isInteger) { - return LiteralNode.newInstance(source, token, finish, JSType.toInt32(value)); + return LiteralNode.newInstance(token, finish, JSType.toInt32(value)); } else if (isLong) { - return LiteralNode.newInstance(source, token, finish, JSType.toLong(value)); + return LiteralNode.newInstance(token, finish, JSType.toLong(value)); } - return LiteralNode.newInstance(source, token, finish, value); + return LiteralNode.newInstance(token, finish, value); } } } diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/Label.java --- a/nashorn/src/jdk/nashorn/internal/codegen/Label.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/Label.java Tue May 14 09:05:03 2013 -0300 @@ -24,9 +24,8 @@ */ package jdk.nashorn.internal.codegen; -import java.util.ArrayDeque; - import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.runtime.Debug; /** * Abstraction for labels, separating a label from the underlying @@ -35,12 +34,87 @@ * * see -Dnashorn.codegen.debug, --log=codegen */ -public class Label extends jdk.internal.org.objectweb.asm.Label { +public final class Label { + //byte code generation evaluation type stack for consistency check + //and correct opcode selection. one per label as a label may be a + //join point + static final class Stack { + Type[] data = new Type[8]; + int sp = 0; + + Stack() { + } + + private Stack(final Type[] type, final int sp) { + this(); + this.data = new Type[type.length]; + this.sp = sp; + for (int i = 0; i < sp; i++) { + data[i] = type[i]; + } + } + + boolean isEmpty() { + return sp == 0; + } + + int size() { + return sp; + } + + boolean isEquivalentTo(final Stack other) { + if (sp != other.sp) { + return false; + } + for (int i = 0; i < sp; i++) { + if (!data[i].isEquivalentTo(other.data[i])) { + return false; + } + } + return true; + } + + void clear() { + sp = 0; + } + + void push(final Type type) { + if (data.length == sp) { + final Type[] newData = new Type[sp * 2]; + for (int i = 0; i < sp; i++) { + newData[i] = data[i]; + } + data = newData; + } + data[sp++] = type; + } + + Type peek() { + return peek(0); + } + + Type peek(final int n) { + int pos = sp - 1 - n; + return pos < 0 ? null : data[pos]; + } + + Type pop() { + return data[--sp]; + } + + Stack copy() { + return new Stack(data, sp); + } + } + /** Name of this label */ private final String name; /** Type stack at this label */ - private ArrayDeque stack; + private Label.Stack stack; + + /** ASM representation of this label */ + private jdk.internal.org.objectweb.asm.Label label; /** * Constructor @@ -62,22 +136,24 @@ this.name = label.name; } - ArrayDeque getStack() { + + jdk.internal.org.objectweb.asm.Label getLabel() { + if (this.label == null) { + this.label = new jdk.internal.org.objectweb.asm.Label(); + } + return label; + } + + Label.Stack getStack() { return stack; } - void setStack(final ArrayDeque stack) { + void setStack(final Label.Stack stack) { this.stack = stack; } @Override public String toString() { - final StringBuilder sb = new StringBuilder(); - String s = super.toString(); - s = s.substring(1, s.length()); - sb.append(name).append('_').append(Long.toHexString(Long.parseLong(s))); - - return sb.toString(); + return name + '_' + Debug.id(this); } } - diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/Lower.java --- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Tue May 14 09:05:03 2013 -0300 @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; + import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; @@ -49,16 +50,15 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.LabelNode; 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.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.ThrowNode; import jdk.nashorn.internal.ir.TryNode; -import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; @@ -93,21 +93,25 @@ super(new BlockLexicalContext() { @Override - public List popStatements() { - List newStatements = new ArrayList<>(); + public List popStatements() { + final List newStatements = new ArrayList<>(); boolean terminated = false; - final List statements = super.popStatements(); - for (final Node statement : statements) { + final List statements = super.popStatements(); + for (final Statement statement : statements) { if (!terminated) { newStatements.add(statement); - if (statement.isTerminal()) { + if (statement.isTerminal() || statement instanceof BreakNode || statement instanceof ContinueNode) { //TODO hasGoto? But some Loops are hasGoto too - why? terminated = true; } } else { - if (statement instanceof VarNode) { - newStatements.add(((VarNode)statement).setInit(null)); - } + statement.accept(new NodeVisitor() { + @Override + public boolean enterVarNode(final VarNode varNode) { + newStatements.add(varNode.setInit(null)); + return false; + } + }); } } return newStatements; @@ -118,8 +122,9 @@ @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); + final FunctionNode function = lc.getCurrentFunction(); + if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) { + new ExecuteNode(block.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this); } return true; } @@ -131,20 +136,20 @@ final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext(); - Node last = lc.getLastStatement(); + Statement last = lc.getLastStatement(); if (lc.isFunctionBody()) { final FunctionNode currentFunction = getLexicalContext().getCurrentFunction(); final boolean isProgram = currentFunction.isProgram(); final ReturnNode returnNode = new ReturnNode( - currentFunction.getSource(), + last == null ? block.getLineNumber() : last.getLineNumber(), //TODO? currentFunction.getToken(), currentFunction.getFinish(), isProgram ? compilerConstant(RETURN) : LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)); - last = returnNode.accept(this); + last = (Statement)returnNode.accept(this); } if (last != null && last.isTerminal()) { @@ -193,7 +198,6 @@ if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) { node = executeNode.setExpression( new BinaryNode( - executeNode.getSource(), Token.recast( executeNode.getToken(), TokenType.ASSIGN), @@ -240,12 +244,6 @@ } @Override - public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) { - addStatement(lineNumberNode); // don't put it in lastStatement cache - return false; - } - - @Override public Node leaveReturnNode(final ReturnNode returnNode) { addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor return returnNode; @@ -272,10 +270,10 @@ }); } - private static List copyFinally(final Block finallyBody) { - final List newStatements = new ArrayList<>(); - for (final Node statement : finallyBody.getStatements()) { - newStatements.add(ensureUniqueLabelsIn(statement)); + private static List copyFinally(final Block finallyBody) { + final List newStatements = new ArrayList<>(); + for (final Statement statement : finallyBody.getStatements()) { + newStatements.add((Statement)ensureUniqueLabelsIn(statement)); if (statement.hasTerminalFlags()) { return newStatements; } @@ -284,17 +282,17 @@ } private Block catchAllBlock(final TryNode tryNode) { - final Source source = tryNode.getSource(); - final long token = tryNode.getToken(); - final int finish = tryNode.getFinish(); + final int lineNumber = tryNode.getLineNumber(); + final long token = tryNode.getToken(); + final int finish = tryNode.getFinish(); - final IdentNode exception = new IdentNode(source, token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all")); + final IdentNode exception = new IdentNode(token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all")); - final Block catchBody = new Block(source, token, finish, new ThrowNode(source, token, finish, new IdentNode(exception))). + final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, 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); + final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody); + final Block catchAllBlock = new Block(lineNumber, token, finish, catchAllNode); //catchallblock -> catchallnode (catchnode) -> exception -> throw @@ -303,10 +301,10 @@ private IdentNode compilerConstant(final CompilerConstants cc) { final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); - return new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName()); + return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName()); } - private static boolean isTerminal(final List statements) { + private static boolean isTerminal(final List statements) { return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags(); } @@ -318,8 +316,7 @@ * @return new try node after splicing finally code (same if nop) */ private Node spliceFinally(final TryNode tryNode, final List rethrows, final Block finallyBody) { - final Source source = tryNode.getSource(); - final int finish = tryNode.getFinish(); + final int finish = tryNode.getFinish(); assert tryNode.getFinallyBody() == null; @@ -341,11 +338,11 @@ @Override public Node leaveThrowNode(final ThrowNode throwNode) { if (rethrows.contains(throwNode)) { - final List newStatements = copyFinally(finallyBody); + final List newStatements = copyFinally(finallyBody); if (!isTerminal(newStatements)) { newStatements.add(throwNode); } - return new Block(source, throwNode.getToken(), throwNode.getFinish(), newStatements); + return new Block(throwNode.getLineNumber(), throwNode.getToken(), throwNode.getFinish(), newStatements); } return throwNode; } @@ -363,14 +360,14 @@ @Override public Node leaveReturnNode(final ReturnNode returnNode) { final Node expr = returnNode.getExpression(); - final List newStatements = new ArrayList<>(); + final List 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))); + newStatements.add(new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); } else { resultNode = null; } @@ -380,16 +377,16 @@ newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode)); } - return new ExecuteNode(new Block(source, returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements)); + return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements)); } - private Node copy(final Node endpoint, final Node targetNode) { + private Node copy(final Statement endpoint, final Node targetNode) { if (!insideTry.contains(targetNode)) { - final List newStatements = copyFinally(finallyBody); + final List newStatements = copyFinally(finallyBody); if (!isTerminal(newStatements)) { newStatements.add(endpoint); } - return new ExecuteNode(new Block(source, endpoint.getToken(), finish, newStatements)); + return new ExecuteNode(endpoint.getLineNumber(), endpoint.getToken(), endpoint.getFinish(), new Block(endpoint.getLineNumber(), endpoint.getToken(), finish, newStatements)); } return endpoint; } @@ -397,7 +394,7 @@ addStatement(newTryNode); for (final Node statement : finallyBody.getStatements()) { - addStatement(statement); + addStatement((Statement)statement); } return newTryNode; @@ -451,7 +448,7 @@ if (tryNode.getCatchBlocks().isEmpty()) { newTryNode = tryNode.setFinallyBody(null); } else { - Block outerBody = new Block(tryNode.getSource(), tryNode.getToken(), tryNode.getFinish(), new ArrayList(Arrays.asList(tryNode.setFinallyBody(null)))); + Block outerBody = new Block(tryNode.getLineNumber(), tryNode.getToken(), tryNode.getFinish(), new ArrayList(Arrays.asList(tryNode.setFinallyBody(null)))); newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null); } @@ -468,19 +465,19 @@ 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); + new ExecuteNode(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this); } return varNode; } @Override public Node leaveWhileNode(final WhileNode whileNode) { - final Node test = whileNode.getTest(); + final Node test = whileNode.getTest(); final Block body = whileNode.getBody(); 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); + final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this); getLexicalContext().replace(whileNode, forNode); return forNode; } @@ -493,16 +490,6 @@ return addStatement(withNode); } - @Override - public Node leaveDELETE(final UnaryNode unaryNode) { - final Node rhs = unaryNode.rhs(); - if (rhs instanceof IdentNode || rhs instanceof BaseNode) { - return unaryNode; - } - addStatement(new ExecuteNode(rhs)); - return LiteralNode.newInstance(unaryNode, true); - } - /** * Given a function node that is a callee in a CallNode, replace it with * the appropriate marker function. This is used by {@link CodeGenerator} @@ -525,11 +512,12 @@ * @param node a node * @return eval location */ - private static String evalLocation(final IdentNode node) { + private String evalLocation(final IdentNode node) { + final Source source = getLexicalContext().getCurrentFunction().getSource(); return new StringBuilder(). - append(node.getSource().getName()). + append(source.getName()). append('#'). - append(node.getSource().getLine(node.position())). + append(source.getLine(node.position())). append(""). toString(); } @@ -618,7 +606,7 @@ } - private Node addStatement(final Node statement) { + private Node addStatement(final Statement statement) { ((BlockLexicalContext)getLexicalContext()).appendStatement(statement); return statement; } diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java --- a/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java Tue May 14 09:05:03 2013 -0300 @@ -65,10 +65,12 @@ * Constructs a property map based on a set of fields. * * @param hasArguments does the created object have an "arguments" property + * @param fieldCount Number of fields in use. + * @param fieldMaximum Number of fields available. * * @return New map populated with accessor properties. */ - PropertyMap makeMap(final boolean hasArguments) { + PropertyMap makeMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) { final List properties = new ArrayList<>(); assert keys != null; @@ -82,7 +84,7 @@ } } - return PropertyMap.newMap(structure, properties); + return PropertyMap.newMap(structure, properties, fieldCount, fieldMaximum); } /** diff -r 4dec41b3c5e3 -r 7bd8457aa8d9 nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java --- a/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Jul 05 18:54:28 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java Tue May 14 09:05:03 2013 -0300 @@ -67,10 +67,9 @@ import java.io.PrintStream; import java.lang.reflect.Array; -import java.util.ArrayDeque; import java.util.EnumSet; -import java.util.Iterator; import java.util.List; + import jdk.internal.dynalink.support.NameCodec; import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.MethodVisitor; @@ -114,7 +113,7 @@ private final MethodVisitor method; /** Current type stack for current evaluation */ - private ArrayDeque stack; + private Label.Stack stack; /** Parent classEmitter representing the class of this method */ private final ClassEmitter classEmitter; @@ -189,7 +188,7 @@ @Override public void begin() { classEmitter.beginMethod(this); - stack = new ArrayDeque<>(); + newStack(); method.visitCode(); } @@ -205,6 +204,10 @@ classEmitter.endMethod(this); } + private void newStack() { + stack = new Label.Stack(); + } + @Override public String toString() { return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this); @@ -288,11 +291,7 @@ * @return the type at position "pos" on the stack */ final Type peekType(final int pos) { - final Iterator iter = stack.iterator(); - for (int i = 0; i < pos; i++) { - iter.next(); - } - return iter.next(); + return stack.peek(pos); } /** @@ -484,7 +483,7 @@ name = THIS_DEBUGGER.symbolName(); } - method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot()); + method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start.getLabel(), end.getLabel(), symbol.getSlot()); } /** @@ -509,17 +508,6 @@ } /** - * Associate a variable with a given range - * - * @param name name of the variable - * @param start start - * @param end end - */ - void markerVariable(final String name, final Label start, final Label end) { - method.visitLocalVariable(name, Type.OBJECT.getDescriptor(), null, start, end, 0); - } - - /** * Pops two integer types from the stack, performs a bitwise and and pushes * the result * @@ -626,7 +614,7 @@ * @param typeDescriptor type descriptor for exception */ void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor) { - method.visitTryCatchBlock(entry, exit, recovery, typeDescriptor); + method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor); } /** @@ -638,7 +626,7 @@ * @param clazz exception class */ void _try(final Label entry, final Label exit, final Label recovery, final Class clazz) { - method.visitTryCatchBlock(entry, exit, recovery, CompilerConstants.className(clazz)); + method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), CompilerConstants.className(clazz)); } /** @@ -871,7 +859,7 @@ } private boolean isThisSlot(final int slot) { - if(functionNode == null) { + if (functionNode == null) { return slot == CompilerConstants.JAVA_THIS.slot(); } final int thisSlot = compilerConstant(THIS).getSlot(); @@ -915,7 +903,6 @@ dup(); return this; } - debug("load compiler constant ", symbol); return load(symbol); } @@ -1228,6 +1215,14 @@ return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true); } + static jdk.internal.org.objectweb.asm.Label[] getLabels(final Label... table) { + final jdk.internal.org.objectweb.asm.Label[] internalLabels = new jdk.internal.org.objectweb.asm.Label[table.length]; + for (int i = 0; i < table.length; i++) { + internalLabels[i] = table[i].getLabel(); + } + return internalLabels; + } + /** * Generate a lookup switch, popping the switch value from the stack * @@ -1235,10 +1230,10 @@ * @param values case values for the table * @param table default label */ - void lookupswitch(final Label defaultLabel, final int[] values, final Label[] table) { + void lookupswitch(final Label defaultLabel, final int[] values, final Label... table) {//Collection