8067774: Use a stack of types when calculating local variable types
authorattila
Thu, 18 Dec 2014 12:10:10 +0100
changeset 28130 433d6755c5f8
parent 28129 698b5aa53174
child 28131 1bb8bc01c120
8067774: Use a stack of types when calculating local variable types Reviewed-by: lagergren, sundar
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java
nashorn/test/script/basic/JDK-8067774.js
nashorn/test/script/basic/JDK-8067774.js.EXPECTED
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<? extends LexicalContext> 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<Symbol, LvarType> localVariableTypes = new IdentityHashMap<>();
+    // Stack for evaluated expression types.
+    private final Deque<LvarType> typeStack = new ArrayDeque<>();
 
     // Whether the current point in the AST is reachable code
     private boolean reachable = true;
@@ -375,8 +417,6 @@
     private final Map<IdentNode, LvarType> identifierLvarTypes = new IdentityHashMap<>();
     private final Map<Symbol, SymbolConversions> 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<Expression> 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<Symbol, LvarType> afterTestLvarTypes = localVariableTypes;
         if(!isAlwaysFalse(test)) {
             pass.accept(this);
+            assertTypeStackIsEmpty();
         }
         final Map<Symbol, LvarType> 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<Expression> 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<CaseNode> 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<Symbol, LvarType> testExitLvarTypes = localVariableTypes;
+        final LvarType trueType;
         if(!isAlwaysFalse(test)) {
-            trueExpr.accept(this);
+            trueType = visitExpression(trueExpr);
+        } else {
+            trueType = null;
         }
         final Map<Symbol, LvarType> trueExitLvarTypes = localVariableTypes;
         localVariableTypes = testExitLvarTypes;
+        final LvarType falseType;
         if(!isAlwaysTrue(test)) {
-            falseExpr.accept(this);
+            falseType = visitExpression(falseExpr);
+        } else {
+            falseType = null;
         }
         final Map<Symbol, LvarType> 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> 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<Symbol, LvarType> 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<Symbol, LvarType> 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<Symbol, LvarType> getBreakTargetTypes(final BreakableNode target) {
         // Remove symbols defined in the the blocks that are being broken out of.
         Map<Symbol, LvarType> 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<Symbol, Type> getSymbolToType() {
-        if(symbolToType.isStale()) {
-            symbolToType = new SymbolToType();
-        }
-        return symbolToType;
-    }
-
-    private class SymbolToType implements Function<Symbol, Type> {
-        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<Symbol, Type> getSymbolToType(final Symbol overriddenSymbol, final Type overriddenType) {
-        return getLocalVariableType(overriddenSymbol).type == overriddenType ? getSymbolToType() :
-            new SymbolToTypeOverride(overriddenSymbol, overriddenType);
-    }
-
-    private class SymbolToTypeOverride implements Function<Symbol, Type> {
-        private final Function<Symbol, Type> 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);
-        }
-    }
 }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return type == null ? getMostPessimisticType() : type;
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<TokenType> CAN_OVERFLOW =
@@ -143,24 +140,6 @@
         }
     }
 
-    private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
-        @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<Symbol, Type> 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<Symbol, Type> 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<Symbol, Type> 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) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return optimisticType == null ? Type.OBJECT : optimisticType;
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
-        @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<Symbol, Type> localVariableTypes);
+    public abstract Type getType();
 
     /**
      * Returns {@code true} if this expression depends exclusively on state that is constant
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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;
@@ -1101,7 +1100,7 @@
     }
 
     @Override
-    public Type getType(final Function<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return FUNCTION_TYPE;
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return Type.INT;
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> 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;
     }
 
     /**
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> localVariableTypes) {
-        return expression.getType(localVariableTypes);
+    public Type getType() {
+        return expression.getType();
     }
 
     @Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> 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<Symbol, Type> localVariableTypes) {
+        public Type getType() {
             return Type.BOOLEAN;
         }
 
@@ -389,7 +378,7 @@
         }
 
         @Override
-        public Type getType(final Function<Symbol, Type> localVariableTypes) {
+        public Type getType() {
             return type;
         }
 
@@ -519,7 +508,7 @@
         }
 
         @Override
-        public Type getType(final Function<Symbol, Type> localVariableTypes) {
+        public Type getType() {
             return Type.OBJECT;
         }
 
@@ -589,7 +578,7 @@
         }
 
         @Override
-        public Type getType(final Function<Symbol, Type> 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<Expression> getElementExpressions() {
+            return Collections.unmodifiableList(Arrays.asList(value));
         }
 
         /**
@@ -879,7 +872,7 @@
         }
 
         @Override
-        public Type getType(final Function<Symbol, Type> localVariableTypes) {
+        public Type getType() {
             return Type.typeFor(NativeArray.class);
         }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return Type.OBJECT;
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return request.getReturnType();
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> localVariableTypes) {
-        return Type.widestReturnType(getTrueExpression().getType(localVariableTypes), getFalseExpression().getType(localVariableTypes));
+    public Type getType() {
+        return Type.widestReturnType(getTrueExpression().getType(), getFalseExpression().getType());
     }
 
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
-        @Override
-        public Type apply(final Symbol t) {
-            return null;
-        }
-    };
-
-
     @Override
     public Type getWidestOperationType() {
-        return getWidestOperationType(UNKNOWN_LOCALS);
-    }
-
-    private Type getWidestOperationType(final Function<Symbol, Type> 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<Symbol, Type> 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
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java	Thu Dec 18 16:33:33 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java	Thu Dec 18 12:10:10 2014 +0100
@@ -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();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8067774.js	Thu Dec 18 12:10:10 2014 +0100
@@ -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));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8067774.js.EXPECTED	Thu Dec 18 12:10:10 2014 +0100
@@ -0,0 +1,1 @@
+1