diff options
Diffstat (limited to 'src/jdk/nashorn/internal/codegen/Attr.java')
-rw-r--r-- | src/jdk/nashorn/internal/codegen/Attr.java | 587 |
1 files changed, 366 insertions, 221 deletions
diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java index 211140e4..9ecf7c89 100644 --- a/src/jdk/nashorn/internal/codegen/Attr.java +++ b/src/jdk/nashorn/internal/codegen/Attr.java @@ -37,13 +37,16 @@ import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; import static jdk.nashorn.internal.ir.Symbol.IS_LET; import static jdk.nashorn.internal.ir.Symbol.IS_PARAM; +import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE; import static jdk.nashorn.internal.ir.Symbol.IS_THIS; import static jdk.nashorn.internal.ir.Symbol.IS_VAR; +import static jdk.nashorn.internal.ir.Symbol.KINDMASK; import java.util.ArrayList; import java.util.HashSet; -import java.util.LinkedList; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Set; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; @@ -55,14 +58,15 @@ import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; -import jdk.nashorn.internal.ir.ReferenceNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; @@ -115,6 +119,8 @@ final class Attr extends NodeOperatorVisitor { */ private Set<String> localUses; + private final LexicalContext lexicalContext = new LexicalContext(); + private static final DebugLogger LOG = new DebugLogger("attr"); private static final boolean DEBUG = LOG.isEnabled(); @@ -135,14 +141,15 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final AccessNode accessNode) { + public Node leaveAccessNode(final AccessNode accessNode) { newTemporary(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this end(accessNode); return accessNode; } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { + lexicalContext.push(block); start(block); final Set<String> savedLocalDefs = localDefs; @@ -158,9 +165,7 @@ final class Attr extends NodeOperatorVisitor { localDefs = new HashSet<>(savedLocalDefs); localUses = new HashSet<>(savedLocalUses); - for (final Node statement : block.getStatements()) { - statement.accept(this); - } + block.visitStatements(this); } finally { localDefs = savedLocalDefs; localUses = savedLocalUses; @@ -170,11 +175,12 @@ final class Attr extends NodeOperatorVisitor { end(block); + lexicalContext.pop(block); return null; } @Override - public Node enter(final CallNode callNode) { + public Node enterCallNode(final CallNode callNode) { start(callNode); callNode.getFunction().accept(this); @@ -195,8 +201,7 @@ final class Attr extends NodeOperatorVisitor { evalArgs.setThis(thisNode); } - newTemporary(Type.OBJECT, callNode); // object type here, access specialization in FinalizeTypes may narrow it later - newType(callNode.getFunction().getSymbol(), Type.OBJECT); + newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later end(callNode); @@ -204,29 +209,106 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final CatchNode catchNode) { + public Node enterCatchNode(final CatchNode catchNode) { final IdentNode exception = catchNode.getException(); final Block block = getCurrentBlock(); start(catchNode); // define block-local exception variable - final Symbol def = block.defineSymbol(exception.getName(), IS_VAR | IS_LET, exception); + final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception); newType(def, Type.OBJECT); addLocalDef(exception.getName()); return catchNode; } + /** + * Declare the definition of a new symbol. + * + * @param name Name of symbol. + * @param symbolFlags Symbol flags. + * @param node Defining Node. + * + * @return Symbol for given name or null for redefinition. + */ + private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) { + int flags = symbolFlags; + Symbol symbol = findSymbol(block, name); // Locate symbol. + + if ((flags & KINDMASK) == IS_GLOBAL) { + flags |= IS_SCOPE; + } + + final FunctionNode function = lexicalContext.getFunction(block); + if (symbol != null) { + // Symbol was already defined. Check if it needs to be redefined. + if ((flags & KINDMASK) == IS_PARAM) { + if (!isLocal(function, symbol)) { + // Not defined in this function. Create a new definition. + symbol = null; + } else if (symbol.isParam()) { + // Duplicate parameter. Null return will force an error. + assert false : "duplicate parameter"; + return null; + } + } else if ((flags & KINDMASK) == IS_VAR) { + if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) { + assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block"; + // Always create a new definition. + symbol = null; + } else { + // Not defined in this function. Create a new definition. + if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { + symbol = null; + } + } + } + } + + if (symbol == null) { + // If not found, then create a new one. + Block symbolBlock; + + // Determine where to create it. + if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { + symbolBlock = block; + } else { + symbolBlock = function; + } + + // Create and add to appropriate block. + symbol = new Symbol(name, flags, node, symbolBlock); + symbolBlock.putSymbol(name, symbol); + + if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { + symbolBlock.getFrame().addSymbol(symbol); + symbol.setNeedsSlot(true); + } + } else if (symbol.less(flags)) { + symbol.setFlags(flags); + } + + if (node != null) { + node.setSymbol(symbol); + } + + return symbol; + } + @Override - public Node enter(final FunctionNode functionNode) { + public Node enterFunctionNode(final FunctionNode functionNode) { start(functionNode, false); if (functionNode.isLazy()) { - LOG.info("LAZY: " + functionNode.getName()); + LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT"); + newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode); + functionNode.setReturnType(Type.OBJECT); end(functionNode); return null; } + lexicalContext.push(functionNode); + clearLocalDefs(); clearLocalUses(); @@ -242,24 +324,36 @@ final class Attr extends NodeOperatorVisitor { initScope(functionNode); initReturn(functionNode); - // Add all nested functions as symbols in this function - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { + // Add all nested declared functions as symbols in this function + for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) { final IdentNode ident = nestedFunction.getIdent(); - if (ident != null && nestedFunction.isStatement()) { - final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction); + if (ident != null) { + assert nestedFunction.isDeclared(); + final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction); newType(functionSymbol, Type.typeFor(ScriptFunction.class)); } } - if (functionNode.isScript()) { + if (functionNode.isProgram()) { initFromPropertyMap(functionNode); } // Add function name as local symbol - if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) { - final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode); - newType(selfSymbol, Type.OBJECT); - selfSymbol.setNode(functionNode); + if (!functionNode.isDeclared() && !functionNode.isProgram()) { + if(functionNode.getSymbol() != null) { + // a temporary left over from an earlier pass when the function was lazy + assert functionNode.getSymbol().isTemp(); + // remove it + functionNode.setSymbol(null); + } + final Symbol selfSymbol; + if(functionNode.isAnonymous()) { + selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode); + } else { + selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode); + newType(selfSymbol, Type.OBJECT); + selfSymbol.setNode(functionNode); + } } /* @@ -280,32 +374,26 @@ final class Attr extends NodeOperatorVisitor { */ final List<Symbol> declaredSymbols = new ArrayList<>(); - for (final VarNode decl : functionNode.getDeclarations()) { - final IdentNode ident = decl.getName(); - // any declared symbols that aren't visited need to be typed as well, hence the list - declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident))); - } - - // Every nested function needs a definition in the outer function with its name. Add these. - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { - final VarNode varNode = nestedFunction.getFunctionVarNode(); - if (varNode != null) { - varNode.accept(this); - assert varNode.isFunctionVarNode() : varNode + " should be function var node"; + // This visitor will assign symbol to all declared variables, except function declarations (which are taken care + // in a separate step above) and "var" declarations in for loop initializers. + functionNode.accept(new NodeOperatorVisitor() { + @Override + public Node enterFunctionNode(FunctionNode nestedFn) { + // Don't descend into nested functions + return nestedFn == functionNode ? nestedFn : null; } - } - - for (final Node statement : functionNode.getStatements()) { - if (statement instanceof VarNode && ((VarNode)statement).isFunctionVarNode()) { - continue; //var nodes have already been processed, skip or they will generate additional defs/uses and false "can be undefined" + @Override + public Node enterVarNode(VarNode varNode) { + if(varNode.isStatement() && !varNode.isFunctionDeclaration()) { + final IdentNode ident = varNode.getName(); + // any declared symbols that aren't visited need to be typed as well, hence the list + declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident))); + } + return null; } - statement.accept(this); - } + }); - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { - LOG.info("Going into nested function " + functionNode.getName() + " -> " + nestedFunction.getName()); - nestedFunction.accept(this); - } + visitFunctionStatements(functionNode); //unknown parameters are promoted to object type. finalizeParameters(functionNode); @@ -332,13 +420,28 @@ final class Attr extends NodeOperatorVisitor { functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this)); } + if (functionNode.hasLazyChildren()) { + objectifySymbols(functionNode); + } + functionNode.popFrame(); + functionNode.setState(CompilationState.ATTR); + end(functionNode, false); + lexicalContext.pop(functionNode); return null; } + private void visitFunctionStatements(final FunctionNode functionNode) { + final List<Node> newStatements = new ArrayList<>(functionNode.getStatements()); + for(ListIterator<Node> stmts = newStatements.listIterator(); stmts.hasNext();) { + stmts.set(stmts.next().accept(this)); + } + functionNode.setStatements(newStatements); + } + @Override public Node leaveCONVERT(final UnaryNode unaryNode) { assert false : "There should be no convert operators in IR during Attribution"; @@ -347,7 +450,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final IdentNode identNode) { + public Node enterIdentNode(final IdentNode identNode) { final String name = identNode.getName(); start(identNode); @@ -364,7 +467,7 @@ final class Attr extends NodeOperatorVisitor { final Block block = getCurrentBlock(); final Symbol oldSymbol = identNode.getSymbol(); - Symbol symbol = block.findSymbol(name); + Symbol symbol = findSymbol(block, name); //If an existing symbol with the name is found, use that otherwise, declare a new one if (symbol != null) { @@ -388,22 +491,13 @@ final class Attr extends NodeOperatorVisitor { } identNode.setSymbol(symbol); - if (!getCurrentFunctionNode().isLocal(symbol)) { - // non-local: we need to put symbol in scope (if it isn't already) - if (!symbol.isScope()) { - final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction()); - for (final Block lookupBlock : lookupBlocks) { - final Symbol refSymbol = lookupBlock.findSymbol(name); - if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f() - LOG.finest("Found a ref symbol that must be scope " + refSymbol); - refSymbol.setIsScope(); - } - } - } + // non-local: we need to put symbol in scope (if it isn't already) + if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) { + symbol.setIsScope(); } } else { LOG.info("No symbol exists. Declare undefined: " + symbol); - symbol = block.useSymbol(name, identNode); + symbol = useSymbol(block, name, identNode); // we have never seen this before, it can be undefined newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway? symbol.setCanBeUndefined(); @@ -412,9 +506,10 @@ final class Attr extends NodeOperatorVisitor { assert symbol != null; if(symbol.isGlobal()) { - getCurrentFunctionNode().setUsesGlobalSymbol(); + setUsesGlobalSymbol(); } else if(symbol.isScope()) { - getCurrentFunctionNode().setUsesScopeSymbol(symbol); + final Iterator<Block> blocks = lexicalContext.getBlocks(); + blocks.next().setUsesScopeSymbol(symbol, blocks); } if (symbol != oldSymbol && !identNode.isInitializedHere()) { @@ -427,15 +522,68 @@ final class Attr extends NodeOperatorVisitor { return null; } + /** + * Marks the current function as one using any global symbol. The function and all its parent functions will all be + * marked as needing parent scope. + * @see #needsParentScope() + */ + private void setUsesGlobalSymbol() { + for(final Iterator<FunctionNode> fns = lexicalContext.getFunctions(); fns.hasNext();) { + fns.next().setUsesAncestorScope(); + } + } + + /** + * Declare the use of a symbol in a block. + * + * @param block block in which the symbol is used + * @param name Name of symbol. + * @param node Using node + * + * @return Symbol for given name. + */ + private Symbol useSymbol(final Block block, final String name, final Node node) { + Symbol symbol = findSymbol(block, name); + + if (symbol == null) { + // If not found, declare as a free var. + symbol = defineSymbol(block, name, IS_GLOBAL, node); + } else { + node.setSymbol(symbol); + } + + return symbol; + } + + + /** + * Search for symbol in the lexical context starting from the given block. + * @param name Symbol name. + * @return Found symbol or null if not found. + */ + private Symbol findSymbol(final Block block, final String name) { + // Search up block chain to locate symbol. + + for(final Iterator<Block> blocks = lexicalContext.getBlocks(block); blocks.hasNext();) { + // Find name. + final Symbol symbol = blocks.next().getExistingSymbol(name); + // If found then we are good. + if(symbol != null) { + return symbol; + } + } + return null; + } + @Override - public Node leave(final IndexNode indexNode) { - newTemporary(Type.OBJECT, indexNode); //TORO + public Node leaveIndexNode(final IndexNode indexNode) { + newTemporary(Type.OBJECT, indexNode); //TODO return indexNode; } @SuppressWarnings("rawtypes") @Override - public Node enter(final LiteralNode literalNode) { + public Node 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 @@ -464,14 +612,14 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final ObjectNode objectNode) { + public Node leaveObjectNode(final ObjectNode objectNode) { newTemporary(Type.OBJECT, objectNode); end(objectNode); return objectNode; } @Override - public Node enter(final PropertyNode propertyNode) { + public Node enterPropertyNode(final PropertyNode propertyNode) { // assign a pseudo symbol to property name, see NASHORN-710 propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); end(propertyNode); @@ -479,31 +627,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final ReferenceNode referenceNode) { - final FunctionNode functionNode = referenceNode.getReference(); - if (functionNode != null) { - functionNode.addReferencingParentBlock(getCurrentBlock()); - } - return referenceNode; - } - - @Override - public Node leave(final ReferenceNode referenceNode) { - newTemporary(Type.OBJECT, referenceNode); //reference node type is always an object, i.e. the scriptFunction. the function return type varies though - - final FunctionNode functionNode = referenceNode.getReference(); - //assert !functionNode.getType().isUnknown() || functionNode.isLazy() : functionNode.getType(); - if (functionNode.isLazy()) { - LOG.info("Lazy function node call reference: " + functionNode.getName() + " => Promoting to OBJECT"); - functionNode.setReturnType(Type.OBJECT); - } - end(referenceNode); - - return referenceNode; - } - - @Override - public Node leave(final ReturnNode returnNode) { + public Node leaveReturnNode(final ReturnNode returnNode) { final Node expr = returnNode.getExpression(); if (expr != null) { @@ -522,7 +646,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final SwitchNode switchNode) { + public Node leaveSwitchNode(final SwitchNode switchNode) { Type type = Type.UNKNOWN; for (final CaseNode caseNode : switchNode.getCases()) { @@ -559,7 +683,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final TryNode tryNode) { + public Node leaveTryNode(final TryNode tryNode) { tryNode.setException(exceptionSymbol()); if (tryNode.getFinallyBody() != null) { @@ -572,13 +696,13 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final VarNode varNode) { + public Node enterVarNode(final VarNode varNode) { start(varNode); final IdentNode ident = varNode.getName(); final String name = ident.getName(); - final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident); + final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident); assert symbol != null; LOG.info("VarNode " + varNode + " set symbol " + symbol); @@ -590,23 +714,15 @@ final class Attr extends NodeOperatorVisitor { symbol.setCanBeUndefined(); } - if (varNode.getInit() != null) { - varNode.getInit().accept(this); - } - return varNode; } @Override - public Node leave(final VarNode varNode) { + public Node leaveVarNode(final VarNode varNode) { final Node init = varNode.getInit(); final IdentNode ident = varNode.getName(); final String name = ident.getName(); - if (init != null) { - addLocalDef(name); - } - if (init == null) { // var x; with no init will be treated like a use of x by // visit(IdentNode) unless we remove the name @@ -615,8 +731,10 @@ final class Attr extends NodeOperatorVisitor { return varNode; } + addLocalDef(name); + final Symbol symbol = varNode.getSymbol(); - final boolean isScript = symbol.getBlock().getFunction().isScript(); //see NASHORN-56 + final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).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()); @@ -710,11 +828,9 @@ final class Attr extends NodeOperatorVisitor { runtimeNode = new RuntimeNode(unaryNode, request, args); assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this - runtimeNode.accept(this); - return runtimeNode; + return leaveRuntimeNode(runtimeNode); } - @Override public Node leaveNEW(final UnaryNode unaryNode) { newTemporary(Type.OBJECT, unaryNode); @@ -747,7 +863,7 @@ final class Attr extends NodeOperatorVisitor { runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); assert runtimeNode.getSymbol() == unaryNode.getSymbol(); - runtimeNode.accept(this); + runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode); end(unaryNode); @@ -755,7 +871,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final RuntimeNode runtimeNode) { + public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode); return runtimeNode; } @@ -815,12 +931,12 @@ final class Attr extends NodeOperatorVisitor { final IdentNode ident = (IdentNode)lhs; final String name = ident.getName(); - Symbol symbol = getCurrentBlock().findSymbol(name); + Symbol symbol = findSymbol(getCurrentBlock(), name); if (symbol == null) { - symbol = block.defineSymbol(name, IS_GLOBAL, ident); + symbol = defineSymbol(block, name, IS_GLOBAL, ident); binaryNode.setSymbol(symbol); - } else if (!getCurrentFunctionNode().isLocal(symbol)) { + } else if (!isLocal(getCurrentFunctionNode(), symbol)) { symbol.setIsScope(); } @@ -830,6 +946,12 @@ final class Attr extends NodeOperatorVisitor { return binaryNode; } + private boolean isLocal(FunctionNode function, Symbol symbol) { + final Block block = symbol.getBlock(); + // some temp symbols have no block, so can be assumed local + return block == null || lexicalContext.getFunction(block) == function; + } + @Override public Node enterASSIGN(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); @@ -957,20 +1079,17 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveBIT_AND(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override public Node leaveBIT_OR(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override public Node leaveBIT_XOR(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override @@ -990,7 +1109,7 @@ final class Attr extends NodeOperatorVisitor { return leaveBinaryArithmetic(binaryNode); } - private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { + private Node leaveCmp(final BinaryNode binaryNode) { final Node lhs = binaryNode.lhs(); final Node rhs = binaryNode.rhs(); @@ -1002,49 +1121,64 @@ final class Attr extends NodeOperatorVisitor { return binaryNode; } + private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) { + // TODO we currently don't support changing inferred type based on uses, only on + // definitions. we would need some additional logic. We probably want to do that + // in the future, if e.g. a specialized method gets parameter that is only used + // 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); + newTemporary(destType, binaryNode); + return binaryNode; + } + + private Node coerce(final BinaryNode binaryNode, final Type type) { + return coerce(binaryNode, type, type); + } + //leave a binary node and inherit the widest type of lhs , rhs private Node leaveBinaryArithmetic(final BinaryNode binaryNode) { - if (!Compiler.shouldUseIntegerArithmetic()) { - newTemporary(Type.NUMBER, binaryNode); - return binaryNode; - } - newTemporary(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType(), Type.NUMBER), binaryNode); - return binaryNode; + assert !Compiler.shouldUseIntegerArithmetic(); + return end(coerce(binaryNode, Type.NUMBER)); } @Override public Node leaveEQ(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.EQ); + return leaveCmp(binaryNode); } @Override public Node leaveEQ_STRICT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.EQ_STRICT); + return leaveCmp(binaryNode); } @Override public Node leaveGE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.GE); + return leaveCmp(binaryNode); } @Override public Node leaveGT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.GT); + return leaveCmp(binaryNode); } @Override public Node leaveIN(final BinaryNode binaryNode) { - try { - return new RuntimeNode(binaryNode, Request.IN).accept(this); - } finally { - end(binaryNode); - } + return leaveBinaryRuntimeOperator(binaryNode, Request.IN); } @Override public Node leaveINSTANCEOF(final BinaryNode binaryNode) { + return leaveBinaryRuntimeOperator(binaryNode, Request.INSTANCEOF); + } + + private Node leaveBinaryRuntimeOperator(final BinaryNode binaryNode, final Request request) { try { - return new RuntimeNode(binaryNode, Request.INSTANCEOF).accept(this); + // Don't do a full RuntimeNode.accept, as we don't want to double-visit the binary node operands + return leaveRuntimeNode(new RuntimeNode(binaryNode, request)); } finally { end(binaryNode); } @@ -1052,12 +1186,12 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveLE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.LE); + return leaveCmp(binaryNode); } @Override public Node leaveLT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.LT); + return leaveCmp(binaryNode); } @Override @@ -1072,12 +1206,12 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveNE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.NE); + return leaveCmp(binaryNode); } @Override public Node leaveNE_STRICT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.NE_STRICT); + return leaveCmp(binaryNode); } @Override @@ -1089,23 +1223,17 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveSAR(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - end(binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override public Node leaveSHL(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - end(binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override public Node leaveSHR(final BinaryNode binaryNode) { - newTemporary(Type.LONG, binaryNode); - end(binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.LONG)); } @Override @@ -1114,9 +1242,9 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final ForNode forNode) { + public Node leaveForNode(final ForNode forNode) { if (forNode.isForIn()) { - forNode.setIterator(newInternal(getCurrentFunctionNode(), getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73 + forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73 /* * Iterators return objects, so we need to widen the scope of the * init variable if it, for example, has been assigned double type @@ -1131,7 +1259,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final TernaryNode ternaryNode) { + public Node leaveTernaryNode(final TernaryNode ternaryNode) { final Node lhs = ternaryNode.rhs(); final Node rhs = ternaryNode.third(); @@ -1146,24 +1274,24 @@ final class Attr extends NodeOperatorVisitor { return ternaryNode; } - private static void initThis(final FunctionNode functionNode) { - final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null); + private void initThis(final FunctionNode functionNode) { + final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null); newType(thisSymbol, Type.OBJECT); thisSymbol.setNeedsSlot(true); functionNode.getThisNode().setSymbol(thisSymbol); LOG.info("Initialized scope symbol: " + thisSymbol); } - private static void initScope(final FunctionNode functionNode) { - final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null); + private void initScope(final FunctionNode functionNode) { + final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null); newType(scopeSymbol, Type.typeFor(ScriptObject.class)); scopeSymbol.setNeedsSlot(true); functionNode.getScopeNode().setSymbol(scopeSymbol); LOG.info("Initialized scope symbol: " + scopeSymbol); } - private static void initReturn(final FunctionNode functionNode) { - final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null); + private void initReturn(final FunctionNode functionNode) { + final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null); newType(returnSymbol, Type.OBJECT); returnSymbol.setNeedsSlot(true); functionNode.getResultNode().setSymbol(returnSymbol); @@ -1173,7 +1301,7 @@ final class Attr extends NodeOperatorVisitor { private void initVarArg(final FunctionNode functionNode) { if (functionNode.isVarArg()) { - final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null); + final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null); varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY); varArgsSymbol.setNeedsSlot(true); functionNode.getVarArgsNode().setSymbol(varArgsSymbol); @@ -1181,7 +1309,7 @@ final class Attr extends NodeOperatorVisitor { if (functionNode.needsArguments()) { final String argumentsName = functionNode.getArgumentsNode().getName(); - final Symbol argumentsSymbol = functionNode.defineSymbol(argumentsName, IS_VAR | IS_INTERNAL, null); + final Symbol argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null); newType(argumentsSymbol, Type.typeFor(ScriptObject.class)); argumentsSymbol.setNeedsSlot(true); functionNode.getArgumentsNode().setSymbol(argumentsSymbol); @@ -1191,9 +1319,9 @@ final class Attr extends NodeOperatorVisitor { } } - private static void initCallee(final FunctionNode functionNode) { + private void initCallee(final FunctionNode functionNode) { assert functionNode.getCalleeNode() != null : functionNode + " has no callee"; - final Symbol calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null); + final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null); newType(calleeSymbol, Type.typeFor(ScriptFunction.class)); calleeSymbol.setNeedsSlot(true); functionNode.getCalleeNode().setSymbol(calleeSymbol); @@ -1211,11 +1339,17 @@ final class Attr extends NodeOperatorVisitor { // type or its parameters with the widest (OBJECT) type for safety. functionNode.setReturnType(Type.UNKNOWN); - for (final IdentNode ident : functionNode.getParameters()) { - addLocalDef(ident.getName()); - final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident); + for (final IdentNode param : functionNode.getParameters()) { + addLocalDef(param.getName()); + final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param); if (paramSymbol != null) { - newType(paramSymbol, Type.UNKNOWN); + final Type callSiteParamType = functionNode.getSpecializedType(param); + if (callSiteParamType != null) { + LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that."); + + System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that."); + } + newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType); } LOG.info("Initialized param " + paramSymbol); @@ -1229,36 +1363,29 @@ final class Attr extends NodeOperatorVisitor { * @param functionNode functionNode */ private static void finalizeParameters(final FunctionNode functionNode) { - boolean nonObjectParams = false; - List<Type> paramSpecializations = new ArrayList<>(); + final boolean isVarArg = functionNode.isVarArg(); for (final IdentNode ident : functionNode.getParameters()) { final Symbol paramSymbol = ident.getSymbol(); - if (paramSymbol != null) { - Type type = paramSymbol.getSymbolType(); - if (type.isUnknown()) { - type = Type.OBJECT; - } - paramSpecializations.add(type); - if (!type.isObject()) { - nonObjectParams = true; - } - newType(paramSymbol, Type.OBJECT); + + assert paramSymbol != null; + Type type = functionNode.getSpecializedType(ident); + if (type == null) { + type = Type.OBJECT; } - } - if (!nonObjectParams) { - paramSpecializations = null; - // Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters - // here. If the callee has parameter specializations, we can regenerate it with those particular types for speed. - } else { - LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations); - } + // 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()); + } - // parameters should not be slots for a function that uses variable arity signature - if (functionNode.isVarArg()) { - for (final IdentNode param : functionNode.getParameters()) { - param.getSymbol().setNeedsSlot(false); + newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType())); + + // parameters should not be slots for a function that uses variable arity signature + if (isVarArg) { + paramSymbol.setNeedsSlot(false); } } } @@ -1267,15 +1394,15 @@ final class Attr extends NodeOperatorVisitor { * Move any properties from a global map into the scope of this method * @param functionNode the function node for which to init scope vars */ - private static void initFromPropertyMap(final FunctionNode functionNode) { + private void initFromPropertyMap(final FunctionNode functionNode) { // For a script, add scope symbols as defined in the property map - assert functionNode.isScript(); + assert functionNode.isProgram(); final PropertyMap map = Context.getGlobalMap(); for (final Property property : map.getProperties()) { final String key = property.getKey(); - final Symbol symbol = functionNode.defineSymbol(key, IS_GLOBAL, null); + final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null); newType(symbol, Type.OBJECT); LOG.info("Added global symbol from property map " + symbol); } @@ -1342,9 +1469,14 @@ final class Attr extends NodeOperatorVisitor { private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) { assignmentDest.accept(new NodeVisitor() { @Override - public Node leave(final IndexNode indexNode) { + public Node leaveIndexNode(final IndexNode indexNode) { + assert indexNode.getSymbol().isTemp(); final Node index = indexNode.getIndex(); - index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant()); + //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); + } return indexNode; } }); @@ -1387,7 +1519,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final FunctionNode node) { + public Node enterFunctionNode(final FunctionNode node) { return node.isLazy() ? null : node; } @@ -1407,7 +1539,7 @@ final class Attr extends NodeOperatorVisitor { */ @SuppressWarnings("fallthrough") @Override - public Node leave(final BinaryNode binaryNode) { + public Node leaveBinaryNode(final BinaryNode binaryNode) { final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); switch (binaryNode.tokenType()) { default: @@ -1465,22 +1597,6 @@ final class Attr extends NodeOperatorVisitor { return binaryNode; } - private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) { - if (currentFunction.findParentFunction() == topFunction) { - final List<Block> blocks = new LinkedList<>(); - - blocks.add(currentFunction.getParent()); - blocks.addAll(currentFunction.getReferencingParentBlocks()); - return blocks; - } - /* - * assumption: all parent blocks of an inner function will always be in the same outer function; - * therefore we can simply skip through intermediate functions. - * @see FunctionNode#addReferencingParentBlock(Block) - */ - return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction); - } - private static boolean isFunctionExpressionSelfReference(final Symbol symbol) { if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) { return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName()); @@ -1497,16 +1613,12 @@ final class Attr extends NodeOperatorVisitor { return newTemporary(getCurrentFunctionNode(), type, node); } - private Symbol newInternal(final FunctionNode functionNode, final String name, final Type type) { - final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); + private Symbol newInternal(final String name, final Type type) { + final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null); iter.setType(type); // NASHORN-73 return iter; } - private Symbol newInternal(final String name, final Type type) { - return newInternal(getCurrentFunctionNode(), name, type); - } - private static void newType(final Symbol symbol, final Type type) { final Type oldType = symbol.getSymbolType(); symbol.setType(type); @@ -1548,6 +1660,39 @@ final class Attr extends NodeOperatorVisitor { localUses.add(name); } + /** + * Pessimistically promote all symbols in current function node to Object types + * This is done when the function contains unevaluated black boxes such as + * lazy sub-function nodes that have not been compiled. + * + * @param functionNode function node in whose scope symbols should conservatively be made objects + */ + private static void objectifySymbols(final FunctionNode functionNode) { + functionNode.accept(new NodeVisitor() { + private void toObject(final Block block) { + for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) { + final Symbol symbol = iter.next(); + newType(symbol, Type.OBJECT); + } + } + + @Override + public Node enterBlock(final Block block) { + toObject(block); + return block; + } + + @Override + public Node enterFunctionNode(final FunctionNode node) { + toObject(node); + if (node.isLazy()) { + return null; + } + return node; + } + }); + } + private static String name(final Node node) { final String cn = node.getClass().getName(); int lastDot = cn.lastIndexOf('.'); |