# HG changeset patch # User lana # Date 1419910821 28800 # Node ID 59e4cf23697e1c1994c4e5180b41ebbe55dffdfd # Parent 634ef69dfeb2b84bf069e4a6ecc4a9d57ab0a494# Parent acb0b8f6540e9dc0cb583a75ae11a0770cdb7b60 Merge diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java --- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Mon Dec 29 19:40:21 2014 -0800 @@ -40,21 +40,22 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; -import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.BreakNode; import jdk.nashorn.internal.ir.BreakableNode; +import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; @@ -65,9 +66,11 @@ import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContextNode; import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LocalVariableConversion; import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; @@ -82,6 +85,7 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; +import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.TokenType; @@ -131,8 +135,44 @@ OBJECT(Type.OBJECT); private final Type type; + private final TypeHolderExpression typeExpression; + private LvarType(final Type type) { this.type = type; + this.typeExpression = new TypeHolderExpression(type); + } + } + + /** + * A bogus Expression subclass that only reports its type. Used to interrogate BinaryNode and UnaryNode about their + * types by creating temporary copies of them and replacing their operands with instances of these. An alternative + * solution would be to add BinaryNode.getType(Type lhsType, Type rhsType) and UnaryNode.getType(Type exprType) + * methods. For the time being though, this is easier to implement and is in fact fairly clean. It does result in + * generation of higher number of temporary short lived nodes, though. + */ + private static class TypeHolderExpression extends Expression { + private static final long serialVersionUID = 1L; + + private final Type type; + + TypeHolderExpression(final Type type) { + super(0L, 0, 0); + this.type = type; + } + + @Override + public Node accept(final NodeVisitor visitor) { + throw new AssertionError(); + } + + @Override + public Type getType() { + return type; + } + + @Override + public void toString(final StringBuilder sb, final boolean printType) { + throw new AssertionError(); } } @@ -359,6 +399,8 @@ // allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current // value. private Map localVariableTypes = new IdentityHashMap<>(); + // Stack for evaluated expression types. + private final Deque typeStack = new ArrayDeque<>(); // Whether the current point in the AST is reachable code private boolean reachable = true; @@ -375,8 +417,6 @@ private final Map identifierLvarTypes = new IdentityHashMap<>(); private final Map symbolConversions = new IdentityHashMap<>(); - private SymbolToType symbolToType = new SymbolToType(); - // Stack of open labels for starts of catch blocks, one for every currently traversed try block; for inserting // control flow edges to them. Note that we currently don't insert actual control flow edges, but instead edges that // help us with type calculations. This means that some operations that can result in an exception being thrown @@ -400,62 +440,56 @@ private void doesNotContinueSequentially() { reachable = false; localVariableTypes = Collections.emptyMap(); + assertTypeStackIsEmpty(); } + private boolean pushExpressionType(final Expression expr) { + typeStack.push(toLvarType(expr.getType())); + return false; + } + + @Override + public boolean enterAccessNode(final AccessNode accessNode) { + visitExpression(accessNode.getBase()); + return pushExpressionType(accessNode); + } @Override public boolean enterBinaryNode(final BinaryNode binaryNode) { // NOTE: regardless of operator's lexical associativity, lhs is always evaluated first. final Expression lhs = binaryNode.lhs(); - final boolean isAssignment = binaryNode.isAssignment(); - LvarType lhsTypeOnLoad = null; - if(isAssignment) { - if(lhs instanceof BaseNode) { - ((BaseNode)lhs).getBase().accept(this); - if(lhs instanceof IndexNode) { - ((IndexNode)lhs).getIndex().accept(this); - } else { - assert lhs instanceof AccessNode; - } - } else { - assert lhs instanceof IdentNode; - if(binaryNode.isSelfModifying()) { - final IdentNode ident = ((IdentNode)lhs); - ident.accept(this); - // Self-assignment can cause a change in the type of the variable. For purposes of evaluating - // the type of the operation, we must use its type as it was when it was loaded. If we didn't - // do this, some awkward expressions would end up being calculated incorrectly, e.g. - // "var x; x += x = 0;". In this case we have undefined+int so the result type is double (NaN). - // However, if we used the type of "x" on LHS after we evaluated RHS, we'd see int+int, so the - // result type would be either optimistic int or pessimistic long, which would be wrong. - lhsTypeOnLoad = getLocalVariableTypeIfBytecode(ident.getSymbol()); - } - } + final LvarType lhsType; + if (!(lhs instanceof IdentNode && binaryNode.tokenType() == TokenType.ASSIGN)) { + lhsType = visitExpression(lhs); } else { - lhs.accept(this); + // Can't visit IdentNode on LHS of a simple assignment, as visits imply use, and this is def. + // The type is irrelevant, as only RHS is used to determine the type anyway. + lhsType = LvarType.UNDEFINED; } final boolean isLogical = binaryNode.isLogical(); - assert !(isAssignment && isLogical); // there are no logical assignment operators in JS final Label joinLabel = isLogical ? new Label("") : null; if(isLogical) { jumpToLabel((JoinPredecessor)lhs, joinLabel); } final Expression rhs = binaryNode.rhs(); - rhs.accept(this); + final LvarType rhsType = visitExpression(rhs); if(isLogical) { jumpToLabel((JoinPredecessor)rhs, joinLabel); } joinOnLabel(joinLabel); - if(isAssignment && lhs instanceof IdentNode) { + final LvarType type = toLvarType(binaryNode.setOperands(lhsType.typeExpression, rhsType.typeExpression).getType()); + + if(binaryNode.isAssignment() && lhs instanceof IdentNode) { if(binaryNode.isSelfModifying()) { - onSelfAssignment((IdentNode)lhs, binaryNode, lhsTypeOnLoad); + onSelfAssignment((IdentNode)lhs, type); } else { - onAssignment((IdentNode)lhs, rhs); + onAssignment((IdentNode)lhs, type); } } + typeStack.push(type); return false; } @@ -475,6 +509,17 @@ } @Override + public boolean enterCallNode(final CallNode callNode) { + visitExpression(callNode.getFunction()); + visitExpressions(callNode.getArgs()); + final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); + if (evalArgs != null) { + visitExpressions(evalArgs.getArgs()); + } + return pushExpressionType(callNode); + } + + @Override public boolean enterContinueNode(final ContinueNode continueNode) { return enterJumpStatement(continueNode); } @@ -483,6 +528,7 @@ if(!reachable) { return false; } + assertTypeStackIsEmpty(); final BreakableNode target = jump.getTarget(lc); jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target)); doesNotContinueSequentially(); @@ -495,6 +541,7 @@ } private void enterDoWhileLoop(final WhileNode loopNode) { + assertTypeStackIsEmpty(); final JoinPredecessorExpression test = loopNode.getTest(); final Block body = loopNode.getBody(); final Label continueLabel = loopNode.getContinueLabel(); @@ -512,7 +559,7 @@ if(!reachable) { break; } - test.accept(this); + visitExpressionOnEmptyStack(test); jumpToLabel(test, breakLabel); if(isAlwaysFalse(test)) { break; @@ -535,6 +582,45 @@ } @Override + public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { + if (reachable) { + visitExpressionOnEmptyStack(expressionStatement.getExpression()); + } + return false; + } + + private void assertTypeStackIsEmpty() { + assert typeStack.isEmpty(); + } + + @Override + protected Node leaveDefault(final Node node) { + assert !(node instanceof Expression); // All expressions were handled + assert !(node instanceof Statement) || typeStack.isEmpty(); // No statements leave with a non-empty stack + return node; + } + + private LvarType visitExpressionOnEmptyStack(final Expression expr) { + assertTypeStackIsEmpty(); + return visitExpression(expr); + } + + private LvarType visitExpression(final Expression expr) { + final int stackSize = typeStack.size(); + expr.accept(this); + assert typeStack.size() == stackSize + 1; + return typeStack.pop(); + } + + private void visitExpressions(final List exprs) { + for(final Expression expr: exprs) { + if (expr != null) { + visitExpression(expr); + } + } + } + + @Override public boolean enterForNode(final ForNode forNode) { if(!reachable) { return false; @@ -543,7 +629,7 @@ final Expression init = forNode.getInit(); if(forNode.isForIn()) { final JoinPredecessorExpression iterable = forNode.getModify(); - iterable.accept(this); + visitExpression(iterable); enterTestFirstLoop(forNode, null, init, // If we're iterating over property names, and we can discern from the runtime environment // of the compilation that the object being iterated over must use strings for property @@ -552,16 +638,18 @@ !compiler.useOptimisticTypes() || (!forNode.isForEach() && compiler.hasStringPropertyIterator(iterable.getExpression()))); } else { if(init != null) { - init.accept(this); + visitExpressionOnEmptyStack(init); } enterTestFirstLoop(forNode, forNode.getModify(), null, false); } + assertTypeStackIsEmpty(); return false; } @Override public boolean enterFunctionNode(final FunctionNode functionNode) { if(alreadyEnteredTopLevelFunction) { + typeStack.push(LvarType.OBJECT); return false; } int pos = 0; @@ -603,11 +691,20 @@ } @Override + public boolean enterGetSplitState(final GetSplitState getSplitState) { + return pushExpressionType(getSplitState); + } + + @Override public boolean enterIdentNode(final IdentNode identNode) { final Symbol symbol = identNode.getSymbol(); if(symbol.isBytecodeLocal()) { symbolIsUsed(symbol); - setIdentifierLvarType(identNode, getLocalVariableType(symbol)); + final LvarType type = getLocalVariableType(symbol); + setIdentifierLvarType(identNode, type); + typeStack.push(type); + } else { + pushExpressionType(identNode); } return false; } @@ -622,11 +719,12 @@ final Block pass = ifNode.getPass(); final Block fail = ifNode.getFail(); - test.accept(this); + visitExpressionOnEmptyStack(test); final Map afterTestLvarTypes = localVariableTypes; if(!isAlwaysFalse(test)) { pass.accept(this); + assertTypeStackIsEmpty(); } final Map passLvarTypes = localVariableTypes; final boolean reachableFromPass = reachable; @@ -635,6 +733,7 @@ localVariableTypes = afterTestLvarTypes; if(!isAlwaysTrue(test) && fail != null) { fail.accept(this); + assertTypeStackIsEmpty(); final boolean reachableFromFail = reachable; reachable |= reachableFromPass; if(!reachable) { @@ -667,15 +766,54 @@ } @Override - public boolean enterPropertyNode(final PropertyNode propertyNode) { - // Avoid falsely adding property keys to the control flow graph - if(propertyNode.getValue() != null) { - propertyNode.getValue().accept(this); + public boolean enterIndexNode(final IndexNode indexNode) { + visitExpression(indexNode.getBase()); + visitExpression(indexNode.getIndex()); + return pushExpressionType(indexNode); + } + + @Override + public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinExpr) { + final Expression expr = joinExpr.getExpression(); + if (expr != null) { + expr.accept(this); + } else { + typeStack.push(LvarType.UNDEFINED); } return false; } @Override + public boolean enterLiteralNode(final LiteralNode literalNode) { + if (literalNode instanceof ArrayLiteralNode) { + final List expressions = ((ArrayLiteralNode)literalNode).getElementExpressions(); + if (expressions != null) { + visitExpressions(expressions); + } + } + pushExpressionType(literalNode); + return false; + } + + @Override + public boolean enterObjectNode(final ObjectNode objectNode) { + for(final PropertyNode propertyNode: objectNode.getElements()) { + // Avoid falsely adding property keys to the control flow graph + final Expression value = propertyNode.getValue(); + if (value != null) { + visitExpression(value); + } + } + return pushExpressionType(objectNode); + } + + @Override + public boolean enterPropertyNode(final PropertyNode propertyNode) { + // Property nodes are only accessible through object literals, and we handled that case above + throw new AssertionError(); + } + + @Override public boolean enterReturnNode(final ReturnNode returnNode) { if(!reachable) { return false; @@ -684,9 +822,9 @@ final Expression returnExpr = returnNode.getExpression(); final Type returnExprType; if(returnExpr != null) { - returnExpr.accept(this); - returnExprType = getType(returnExpr); + returnExprType = visitExpressionOnEmptyStack(returnExpr).type; } else { + assertTypeStackIsEmpty(); returnExprType = Type.UNDEFINED; } returnType = Type.widestReturnType(returnType, returnExprType); @@ -695,6 +833,12 @@ } @Override + public boolean enterRuntimeNode(final RuntimeNode runtimeNode) { + visitExpressions(runtimeNode.getArgs()); + return pushExpressionType(runtimeNode); + } + + @Override public boolean enterSplitReturn(final SplitReturn splitReturn) { doesNotContinueSequentially(); return false; @@ -706,8 +850,7 @@ return false; } - final Expression expr = switchNode.getExpression(); - expr.accept(this); + visitExpressionOnEmptyStack(switchNode.getExpression()); final List cases = switchNode.getCases(); if(cases.isEmpty()) { @@ -724,7 +867,7 @@ for(final CaseNode caseNode: cases) { final Expression test = caseNode.getTest(); if(!isInteger && test != null) { - test.accept(this); + visitExpressionOnEmptyStack(test); if(!tagUsed) { symbolIsUsed(switchNode.getTag(), LvarType.OBJECT); tagUsed = true; @@ -769,29 +912,42 @@ final Expression trueExpr = ternaryNode.getTrueExpression(); final Expression falseExpr = ternaryNode.getFalseExpression(); - test.accept(this); + visitExpression(test); final Map testExitLvarTypes = localVariableTypes; + final LvarType trueType; if(!isAlwaysFalse(test)) { - trueExpr.accept(this); + trueType = visitExpression(trueExpr); + } else { + trueType = null; } final Map trueExitLvarTypes = localVariableTypes; localVariableTypes = testExitLvarTypes; + final LvarType falseType; if(!isAlwaysTrue(test)) { - falseExpr.accept(this); + falseType = visitExpression(falseExpr); + } else { + falseType = null; } final Map falseExitLvarTypes = localVariableTypes; localVariableTypes = getUnionTypes(trueExitLvarTypes, falseExitLvarTypes); setConversion((JoinPredecessor)trueExpr, trueExitLvarTypes, localVariableTypes); setConversion((JoinPredecessor)falseExpr, falseExitLvarTypes, localVariableTypes); + + typeStack.push(trueType != null ? falseType != null ? widestLvarType(trueType, falseType) : trueType : assertNotNull(falseType)); return false; } + private static T assertNotNull(final T t) { + assert t != null; + return t; + } + private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify, final Expression iteratorValues, final boolean iteratorValuesAreObject) { final JoinPredecessorExpression test = loopNode.getTest(); if(isAlwaysFalse(test)) { - test.accept(this); + visitExpressionOnEmptyStack(test); return; } @@ -804,7 +960,7 @@ jumpToLabel(loopNode, repeatLabel, beforeLoopTypes); final Map beforeRepeatTypes = localVariableTypes; if(test != null) { - test.accept(this); + visitExpressionOnEmptyStack(test); } if(!isAlwaysTrue(test)) { jumpToLabel(test, breakLabel); @@ -827,7 +983,7 @@ break; } if(modify != null) { - modify.accept(this); + visitExpressionOnEmptyStack(modify); jumpToLabel(modify, repeatLabel); joinOnLabel(repeatLabel); } @@ -853,7 +1009,7 @@ return false; } - throwNode.getExpression().accept(this); + visitExpressionOnEmptyStack(throwNode.getExpression()); jumpToCatchBlock(throwNode); doesNotContinueSequentially(); return false; @@ -892,7 +1048,7 @@ onAssignment(exception, LvarType.OBJECT); final Expression condition = catchNode.getExceptionCondition(); if(condition != null) { - condition.accept(this); + visitExpression(condition); } final Map afterConditionTypes = localVariableTypes; final Block catchBody = catchNode.getBody(); @@ -927,14 +1083,11 @@ @Override public boolean enterUnaryNode(final UnaryNode unaryNode) { final Expression expr = unaryNode.getExpression(); - expr.accept(this); - - if(unaryNode.isSelfModifying()) { - if(expr instanceof IdentNode) { - final IdentNode ident = (IdentNode)expr; - onSelfAssignment(ident, unaryNode, getLocalVariableTypeIfBytecode(ident.getSymbol())); - } + final LvarType unaryType = toLvarType(unaryNode.setExpression(visitExpression(expr).typeExpression).getType()); + if(unaryNode.isSelfModifying() && expr instanceof IdentNode) { + onSelfAssignment((IdentNode)expr, unaryType); } + typeStack.push(unaryType); return false; } @@ -945,8 +1098,7 @@ } final Expression init = varNode.getInit(); if(init != null) { - init.accept(this); - onAssignment(varNode.getName(), init); + onAssignment(varNode.getName(), visitExpression(init)); } return false; } @@ -964,6 +1116,15 @@ return false; } + @Override + public boolean enterWithNode(final WithNode withNode) { + if (reachable) { + visitExpression(withNode.getExpression()); + withNode.getBody().accept(this); + } + return false; + }; + private Map getBreakTargetTypes(final BreakableNode target) { // Remove symbols defined in the the blocks that are being broken out of. Map types = localVariableTypes; @@ -1002,18 +1163,6 @@ } /** - * Gets the type for a local variable if it is a bytecode local, otherwise null. Can be used in circumstances where - * the type is irrelevant if the symbol is not a bytecode local. Note that for bytecode locals, it delegates to - * {@link #getLocalVariableType(Symbol)}, so it will still assert that the type for such variable is already - * defined (that is, not null). - * @param symbol the symbol representing the variable. - * @return the current variable type, if it is a bytecode local, otherwise null. - */ - private LvarType getLocalVariableTypeIfBytecode(final Symbol symbol) { - return symbol.isBytecodeLocal() ? getLocalVariableType(symbol) : null; - } - - /** * Gets the type for a variable represented by a symbol, or null if the type is not know. This is the least strict * of all local variable type getters, and as such its use is discouraged except in initialization scenarios (where * a just-defined symbol might still be null). @@ -1154,6 +1303,7 @@ */ private void leaveBreakable(final BreakableNode breakable) { joinOnLabel(breakable.getBreakLabel()); + assertTypeStackIsEmpty(); } @Override @@ -1329,10 +1479,6 @@ return conv == null || !conv.isLive(); } - private void onAssignment(final IdentNode identNode, final Expression rhs) { - onAssignment(identNode, toLvarType(getType(rhs))); - } - private void onAssignment(final IdentNode identNode, final LvarType type) { final Symbol symbol = identNode.getSymbol(); assert symbol != null : identNode.getName(); @@ -1400,13 +1546,12 @@ jumpToCatchBlock(identNode); } - private void onSelfAssignment(final IdentNode identNode, final Expression assignment, final LvarType typeOnLoad) { + private void onSelfAssignment(final IdentNode identNode, final LvarType type) { final Symbol symbol = identNode.getSymbol(); assert symbol != null : identNode.getName(); if(!symbol.isBytecodeLocal()) { return; } - final LvarType type = toLvarType(getType(assignment, symbol, typeOnLoad.type)); // Self-assignment never produce either a boolean or undefined assert type != null && type != LvarType.UNDEFINED && type != LvarType.BOOLEAN; setType(symbol, type); @@ -1466,7 +1611,6 @@ * @param symbol the symbol representing the variable * @param type the type */ - @SuppressWarnings("unused") private void setType(final Symbol symbol, final LvarType type) { if(getLocalVariableTypeOrNull(symbol) == type) { return; @@ -1486,77 +1630,4 @@ private void symbolIsUsed(final Symbol symbol) { symbolIsUsed(symbol, getLocalVariableType(symbol)); } - - /** - * Gets the type of the expression, dependent on the current types of the local variables. - * - * @param expr the expression - * @return the current type of the expression dependent on the current types of the local variables. - */ - private Type getType(final Expression expr) { - return expr.getType(getSymbolToType()); - } - - /** - * Returns a function object from symbols to their types, used by the expressions to evaluate their type. - * {@link BinaryNode} specifically uses identity of the function to cache type calculations. This method makes - * sure to return the same function object while the local variable types don't change, and create a new function - * object if the local variable types have been changed. - * @return a function object representing a mapping from symbols to their types. - */ - private Function getSymbolToType() { - if(symbolToType.isStale()) { - symbolToType = new SymbolToType(); - } - return symbolToType; - } - - private class SymbolToType implements Function { - private final Object boundTypes = localVariableTypes; - @Override - public Type apply(final Symbol t) { - return getLocalVariableType(t).type; - } - - boolean isStale() { - return boundTypes != localVariableTypes; - } - } - - /** - * Gets the type of the expression, dependent on the current types of the local variables and a single overridden - * symbol type. Used by type calculation on compound operators to ensure the type of the LHS at the time it was - * loaded (which can potentially be different after RHS evaluation, e.g. "var x; x += x = 0;") is preserved for - * the calculation. - * - * @param expr the expression - * @param overriddenSymbol the overridden symbol - * @param overriddenType the overridden type - * @return the current type of the expression dependent on the current types of the local variables and the single - * potentially overridden type. - */ - private Type getType(final Expression expr, final Symbol overriddenSymbol, final Type overriddenType) { - return expr.getType(getSymbolToType(overriddenSymbol, overriddenType)); - } - - private Function getSymbolToType(final Symbol overriddenSymbol, final Type overriddenType) { - return getLocalVariableType(overriddenSymbol).type == overriddenType ? getSymbolToType() : - new SymbolToTypeOverride(overriddenSymbol, overriddenType); - } - - private class SymbolToTypeOverride implements Function { - private final Function originalSymbolToType = getSymbolToType(); - private final Symbol overriddenSymbol; - private final Type overriddenType; - - SymbolToTypeOverride(final Symbol overriddenSymbol, final Type overriddenType) { - this.overriddenSymbol = overriddenSymbol; - this.overriddenType = overriddenType; - } - - @Override - public Type apply(final Symbol symbol) { - return symbol == overriddenSymbol ? overriddenType : originalSymbolToType.apply(symbol); - } - } } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/BaseNode.java --- a/src/jdk/nashorn/internal/ir/BaseNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/BaseNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -27,7 +27,6 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -98,7 +97,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return type == null ? getMostPessimisticType() : type; } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/BinaryNode.java --- a/src/jdk/nashorn/internal/ir/BinaryNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/BinaryNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -31,7 +31,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -57,9 +56,7 @@ private final int programPoint; private final Type type; - private transient Type cachedType; - private transient Object cachedTypeFunction; @Ignore private static final Set CAN_OVERFLOW = @@ -143,24 +140,6 @@ } } - private static final Function UNKNOWN_LOCALS = new Function() { - @Override - public Type apply(final Symbol t) { - return null; - } - }; - - /** - * Return the widest possible type for this operation. This is used for compile time - * static type inference - * - * @return Type - */ - @Override - public Type getWidestOperationType() { - return getWidestOperationType(UNKNOWN_LOCALS); - } - /** * Return the widest possible operand type for this operation. * @@ -181,14 +160,15 @@ } } - private Type getWidestOperationType(final Function localVariableTypes) { + @Override + public Type getWidestOperationType() { switch (tokenType()) { case ADD: case ASSIGN_ADD: { // Compare this logic to decideType(Type, Type); it's similar, but it handles the optimistic type // calculation case while this handles the conservative case. - final Type lhsType = lhs.getType(localVariableTypes); - final Type rhsType = rhs.getType(localVariableTypes); + final Type lhsType = lhs.getType(); + final Type rhsType = rhs.getType(); if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) { // Will always fit in an int, as the value range is [0, 1, 2]. If we didn't treat them specially here, // they'd end up being treated as generic INT operands and their sum would be conservatively considered @@ -238,8 +218,8 @@ case SUB: case ASSIGN_MUL: case ASSIGN_SUB: { - final Type lhsType = lhs.getType(localVariableTypes); - final Type rhsType = rhs.getType(localVariableTypes); + final Type lhsType = lhs.getType(); + final Type rhsType = rhs.getType(); if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) { return Type.INT; } @@ -253,20 +233,20 @@ return Type.UNDEFINED; } case ASSIGN: { - return rhs.getType(localVariableTypes); + return rhs.getType(); } case INSTANCEOF: { return Type.BOOLEAN; } case COMMALEFT: { - return lhs.getType(localVariableTypes); + return lhs.getType(); } case COMMARIGHT: { - return rhs.getType(localVariableTypes); + return rhs.getType(); } case AND: case OR:{ - return Type.widestReturnType(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes)); + return Type.widestReturnType(lhs.getType(), rhs.getType()); } default: if (isComparison()) { @@ -487,7 +467,7 @@ /** * Set the right hand side expression for this node - * @param rhs new left hand side expression + * @param rhs new right hand side expression * @return a node equivalent to this one except for the requested change. */ public BinaryNode setRHS(final Expression rhs) { @@ -497,6 +477,19 @@ return new BinaryNode(this, lhs, rhs, type, programPoint); } + /** + * Set both the left and the right hand side expression for this node + * @param lhs new left hand side expression + * @param rhs new left hand side expression + * @return a node equivalent to this one except for the requested change. + */ + public BinaryNode setOperands(final Expression lhs, final Expression rhs) { + if (this.lhs == lhs && this.rhs == rhs) { + return this; + } + return new BinaryNode(this, lhs, rhs, type, programPoint); + } + @Override public int getProgramPoint() { return programPoint; @@ -541,24 +534,22 @@ } @Override - public Type getType(final Function localVariableTypes) { - if(localVariableTypes == cachedTypeFunction) { - return cachedType; + public Type getType() { + if (cachedType == null) { + cachedType = getTypeUncached(); } - cachedType = getTypeUncached(localVariableTypes); - cachedTypeFunction = localVariableTypes; return cachedType; } - private Type getTypeUncached(final Function localVariableTypes) { + private Type getTypeUncached() { if(type == OPTIMISTIC_UNDECIDED_TYPE) { - return decideType(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes)); + return decideType(lhs.getType(), rhs.getType()); } - final Type widest = getWidestOperationType(localVariableTypes); + final Type widest = getWidestOperationType(); if(type == null) { return widest; } - return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes)))); + return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(), rhs.getType()))); } private static Type decideType(final Type lhsType, final Type rhsType) { diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/CallNode.java --- a/src/jdk/nashorn/internal/ir/CallNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/CallNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -30,7 +30,6 @@ import java.io.Serializable; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -154,7 +153,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return optimisticType == null ? Type.OBJECT : optimisticType; } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/Expression.java --- a/src/jdk/nashorn/internal/ir/Expression.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/Expression.java Mon Dec 29 19:40:21 2014 -0800 @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; @@ -39,14 +38,7 @@ static final String OPT_IDENTIFIER = "%"; - private static final Function UNKNOWN_LOCALS = new Function() { - @Override - public Type apply(final Symbol t) { - return null; - } - }; - - Expression(final long token, final int start, final int finish) { + protected Expression(final long token, final int start, final int finish) { super(token, start, finish); } @@ -63,18 +55,7 @@ * * @return the type of the expression. */ - public final Type getType() { - return getType(UNKNOWN_LOCALS); - } - - /** - * Returns the type of the expression under the specified symbol-to-type mapping. By default delegates to - * {@link #getType()} but expressions whose type depends on their subexpressions' types and expressions whose type - * depends on symbol type ({@link IdentNode}) will have a special implementation. - * @param localVariableTypes a mapping from symbols to their types, used for type calculation. - * @return the type of the expression under the specified symbol-to-type mapping. - */ - public abstract Type getType(final Function localVariableTypes); + public abstract Type getType(); /** * Returns {@code true} if this expression depends exclusively on state that is constant diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/FunctionNode.java --- a/src/jdk/nashorn/internal/ir/FunctionNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -36,7 +36,6 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; @@ -1143,7 +1142,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return FUNCTION_TYPE; } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/GetSplitState.java --- a/src/jdk/nashorn/internal/ir/GetSplitState.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/GetSplitState.java Mon Dec 29 19:40:21 2014 -0800 @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import java.util.function.Function; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -47,7 +46,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.INT; } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/IdentNode.java --- a/src/jdk/nashorn/internal/ir/IdentNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/IdentNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -30,7 +30,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -118,14 +117,13 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { if(type != null) { return type; } else if(symbol != null && symbol.isScope()) { return Type.OBJECT; } - final Type symbolType = localVariableTypes.apply(symbol); - return symbolType == null ? Type.UNDEFINED : symbolType; + return Type.UNDEFINED; } /** diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java --- a/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java Mon Dec 29 19:40:21 2014 -0800 @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -71,8 +70,8 @@ } @Override - public Type getType(final Function localVariableTypes) { - return expression.getType(localVariableTypes); + public Type getType() { + return expression.getType(); } @Override diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/LiteralNode.java --- a/src/jdk/nashorn/internal/ir/LiteralNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -29,7 +29,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.types.ArrayType; import jdk.nashorn.internal.codegen.types.Type; @@ -109,7 +108,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.typeFor(value.getClass()); } @@ -164,16 +163,6 @@ } /** - * Get the array value of the node - * - * @return the array value - */ - public Node[] getArray() { - assert false : "not an array node"; - return null; - } - - /** * Fetch String value of node. * * @return String value of node. @@ -325,7 +314,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.BOOLEAN; } @@ -389,7 +378,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return type; } @@ -519,7 +508,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.OBJECT; } @@ -589,7 +578,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.OBJECT; } @@ -840,9 +829,13 @@ this.units = units; } - @Override - public Node[] getArray() { - return value; + /** + * Returns a list of array element expressions. Note that empty array elements manifest themselves as + * null. + * @return a list of array element expressions. + */ + public List getElementExpressions() { + return Collections.unmodifiableList(Arrays.asList(value)); } /** @@ -879,7 +872,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.typeFor(NativeArray.class); } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/ObjectNode.java --- a/src/jdk/nashorn/internal/ir/ObjectNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/ObjectNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -27,7 +27,6 @@ import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -69,7 +68,7 @@ } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.OBJECT; } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/RuntimeNode.java --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -30,7 +30,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -460,7 +459,7 @@ * Return type for the ReferenceNode */ @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return request.getReturnType(); } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/TernaryNode.java --- a/src/jdk/nashorn/internal/ir/TernaryNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/TernaryNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -122,8 +121,8 @@ } @Override - public Type getType(final Function localVariableTypes) { - return Type.widestReturnType(getTrueExpression().getType(localVariableTypes), getFalseExpression().getType(localVariableTypes)); + public Type getType() { + return Type.widestReturnType(getTrueExpression().getType(), getFalseExpression().getType()); } diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/UnaryNode.java --- a/src/jdk/nashorn/internal/ir/UnaryNode.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/UnaryNode.java Mon Dec 29 19:40:21 2014 -0800 @@ -33,7 +33,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -123,23 +122,11 @@ return isAssignment(); } - private static final Function UNKNOWN_LOCALS = new Function() { - @Override - public Type apply(final Symbol t) { - return null; - } - }; - - @Override public Type getWidestOperationType() { - return getWidestOperationType(UNKNOWN_LOCALS); - } - - private Type getWidestOperationType(final Function localVariableTypes) { switch (tokenType()) { case ADD: - final Type operandType = getExpression().getType(localVariableTypes); + final Type operandType = getExpression().getType(); if(operandType == Type.BOOLEAN) { return Type.INT; } else if(operandType.isObject()) { @@ -326,12 +313,12 @@ } @Override - public Type getType(final Function localVariableTypes) { - final Type widest = getWidestOperationType(localVariableTypes); + public Type getType() { + final Type widest = getWidestOperationType(); if(type == null) { return widest; } - return Type.narrowest(widest, Type.widest(type, expression.getType(localVariableTypes))); + return Type.narrowest(widest, Type.widest(type, expression.getType())); } @Override diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/ir/debug/JSONWriter.java --- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Mon Dec 29 19:40:21 2014 -0800 @@ -28,7 +28,6 @@ import static jdk.nashorn.internal.runtime.Source.sourceFor; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BinaryNode; @@ -553,8 +552,7 @@ type("ArrayExpression"); comma(); - final Node[] value = literalNode.getArray(); - array("elements", Arrays.asList(value)); + array("elements", ((LiteralNode.ArrayLiteralNode)literalNode).getElementExpressions()); } else { type("Literal"); comma(); diff -r 634ef69dfeb2 -r 59e4cf23697e src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java Wed Dec 17 14:46:42 2014 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java Mon Dec 29 19:40:21 2014 -0800 @@ -99,7 +99,7 @@ MethodType newMethodType = descriptor.getMethodType().changeParameterType(0, callable.getClass()); if (isCall) { // R(callable.class, T1, ...) => R(callable.class, boundThis.class, ...) - newMethodType = newMethodType.changeParameterType(1, boundThis.getClass()); + newMethodType = newMethodType.changeParameterType(1, boundThis == null? Object.class : boundThis.getClass()); } // R(callable.class[, boundThis.class], T2, ...) => R(callable.class[, boundThis.class], boundArg0.class, ..., boundArgn.class, T2, ...) for(int i = boundArgs.length; i-- > 0;) { diff -r 634ef69dfeb2 -r 59e4cf23697e test/script/basic/JDK-8067774.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8067774.js Mon Dec 29 19:40:21 2014 -0800 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8067774: Use a stack of types when calculating local variable types + * + * @test + * @run + */ + +print((function (p) { + var a, b; + + a = p ? ((b = 1), b) : 0; + + return a; +})(true)); diff -r 634ef69dfeb2 -r 59e4cf23697e test/script/basic/JDK-8067774.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8067774.js.EXPECTED Mon Dec 29 19:40:21 2014 -0800 @@ -0,0 +1,1 @@ +1 diff -r 634ef69dfeb2 -r 59e4cf23697e test/script/trusted/JDK-8067854.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/trusted/JDK-8067854.js Mon Dec 29 19:40:21 2014 -0800 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8067854: bound java static method throws NPE when 'null' is used for this argument + * + * @test + * @run + */ + +getProp = java.lang.System.getProperty; + +// bind this and an argument. "null" for this as getProperty is a +// static method of java.lang.System +getHome = Function.prototype.bind.call(getProp, null, "java.home"); + +if (getHome() != getProp("java.home")) { + fail("getHome() failed to get java.home"); +}