diff options
Diffstat (limited to 'src/jdk/nashorn/internal/codegen/Attr.java')
-rw-r--r-- | src/jdk/nashorn/internal/codegen/Attr.java | 667 |
1 files changed, 329 insertions, 338 deletions
diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java index 9b17c47f..4e47892f 100644 --- a/src/jdk/nashorn/internal/codegen/Attr.java +++ b/src/jdk/nashorn/internal/codegen/Attr.java @@ -29,11 +29,13 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX; +import static jdk.nashorn.internal.codegen.CompilerConstants.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.PropertyNode; 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.ECMAException; 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { @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); - - 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); - } - } + initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL, Type.typeFor(ScriptObject.class)); + initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL, 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 @@ final class Attr extends NodeOperatorVisitor { } @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 @@ final class Attr extends NodeOperatorVisitor { } @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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { // 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 @@ final class Attr extends NodeOperatorVisitor { symbol.setFlags(flags); } - if (node != null) { - node.setSymbol(symbol); - } - return symbol; } @@ -357,30 +374,22 @@ final class Attr extends NodeOperatorVisitor { 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<Block> blocks = getLexicalContext().getBlocks(); if (blocks.hasNext()) { - defineSymbol( - blocks.next(), - functionNode.getIdent().getName(), - IS_VAR, - functionNode); - } else { - // Q: What's an outermost function in a lexical context that is not a program? - // A: It's a function being compiled lazily! - assert getLexicalContext().getOutermostFunction() == functionNode && !functionNode.isProgram(); + 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 class Attr extends NodeOperatorVisitor { final LexicalContext lc = getLexicalContext(); + final Block body = newFunctionNode.getBody(); + + //look for this function in the parent block + if (functionNode.isDeclared()) { + final Iterator<Block> 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 class Attr extends NodeOperatorVisitor { } } - 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName()); - final List<Node> newStatements = new ArrayList<>(); - newStatements.add(selfInit); + final List<Statement> newStatements = new ArrayList<>(); assert callee.getSymbol() != null && callee.getSymbol().hasSlot(); final IdentNode name = selfInit.getName(); @@ -428,9 +455,10 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { } } - 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { * @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 @@ final class Attr extends NodeOperatorVisitor { private Symbol findSymbol(final Block block, final String name) { // Search up block chain to locate symbol. - for(final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) { + for (final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) { // Find name. final Symbol symbol = blocks.next().getExistingSymbol(name); // If found then we are good. - if(symbol != null) { + if (symbol != null) { return symbol; } } @@ -625,39 +648,19 @@ final class Attr extends NodeOperatorVisitor { @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 @@ final class Attr extends NodeOperatorVisitor { @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 class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { @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 @@ final class Attr extends NodeOperatorVisitor { newType(symbol, Type.OBJECT); } - assert varNode.hasType() : varNode; - - end(varNode); + assert newVarNode.hasType() : newVarNode + " has no type"; - 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 @@ final class Attr extends NodeOperatorVisitor { @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 @@ final class Attr extends NodeOperatorVisitor { @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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { @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 @@ final class Attr extends NodeOperatorVisitor { } 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 @@ final class Attr extends NodeOperatorVisitor { // 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 @@ final class Attr extends NodeOperatorVisitor { @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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { * @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 @@ final class Attr extends NodeOperatorVisitor { * * @param functionNode functionNode */ - private static void finalizeParameters(final FunctionNode functionNode) { + private FunctionNode finalizeParameters(final FunctionNode functionNode) { + final List<IdentNode> 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { * 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 @@ final class Attr extends NodeOperatorVisitor { * * 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 @@ final class Attr extends NodeOperatorVisitor { * * @param functionNode */ - private static void finalizeTypes(final FunctionNode functionNode) { + private FunctionNode finalizeTypes(final FunctionNode functionNode) { final Set<Node> 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 @@ final class Attr extends NodeOperatorVisitor { @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 class Attr extends NodeOperatorVisitor { 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; - } - - 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); + return end(ensureSymbol(destType, ensureAssignmentSlots(binaryNode))); } - 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 @@ final class Attr extends NodeOperatorVisitor { 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 @@ final class Attr extends NodeOperatorVisitor { private static void objectifySymbols(final Block body) { body.accept(new NodeVisitor() { private void toObject(final Block block) { - for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) { - final Symbol symbol = iter.next(); + for (final Symbol symbol : block.getSymbols()) { if (!symbol.isTemp()) { newType(symbol, Type.OBJECT); } @@ -1788,6 +1775,10 @@ final class Attr extends NodeOperatorVisitor { } 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(); |