changeset 1153:50ee57606272 jdk9-b44

Merge
author lana
date Tue, 23 Dec 2014 13:57:28 -0800
parents c327a2247264 5f6a840fc19d
children 3c2bbeda038a
files
diffstat 42 files changed, 943 insertions(+), 372 deletions(-) [+]
line wrap: on
line diff
--- a/make/build.xml	Thu Dec 18 19:57:57 2014 -0800
+++ b/make/build.xml	Tue Dec 23 13:57:28 2014 -0800
@@ -209,7 +209,7 @@
   </target>
 
   <target name="javadoc" depends="jar">
-    <javadoc destdir="${dist.javadoc.dir}" use="yes" overview="src/overview.html" 
+    <javadoc destdir="${dist.javadoc.dir}" use="yes" overview="${src.dir}/overview.html" 
         extdirs="${nashorn.ext.path}" windowtitle="${nashorn.product.name} ${nashorn.version}"
         additionalparam="-quiet" failonerror="true">
       <classpath>
--- a/make/nbproject/ide-targets.xml	Thu Dec 18 19:57:57 2014 -0800
+++ b/make/nbproject/ide-targets.xml	Tue Dec 23 13:57:28 2014 -0800
@@ -32,9 +32,8 @@
         </nbjpdastart>
         <java classname="jdk.nashorn.tools.Shell" classpath="${run.test.classpath}" dir="samples" fork="true">
 	    <jvmarg line="-Dnashorn.optimistic"/>
-            <jvmarg line="${ext.class.path}"/> 
+            <jvmarg line="${boot.class.path}"/> 
             <jvmarg line="${run.test.jvmargs}"/>
-            <arg value="../samples/test.js"/>
             <jvmarg value="-Xdebug"/>
             <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
         </java>
--- a/make/nbproject/project.xml	Thu Dec 18 19:57:57 2014 -0800
+++ b/make/nbproject/project.xml	Tue Dec 23 13:57:28 2014 -0800
@@ -38,10 +38,6 @@
                     <encoding>UTF-8</encoding>
                 </source-folder>
                 <source-folder>
-                    <label>../src</label>
-                    <location>../src</location>
-                </source-folder>
-                <source-folder>
                     <label>../test/src</label>
                     <location>../test/src</location>
                 </source-folder>
@@ -50,21 +46,25 @@
                     <location>../buildtools/nasgen/src</location>
                 </source-folder>
                 <source-folder>
+                    <label>../src/jdk.scripting.nashorn/share/classes</label>
+                    <location>../src/jdk.scripting.nashorn/share/classes</location>
+                </source-folder>
+                <source-folder>
                     <label>../test/src</label>
                     <type>java</type>
                     <location>../test/src</location>
                     <encoding>UTF-8</encoding>
                 </source-folder>
                 <source-folder>
-                    <label>../src</label>
+                    <label>../buildtools/nasgen/src</label>
                     <type>java</type>
-                    <location>../src</location>
+                    <location>../buildtools/nasgen/src</location>
                     <encoding>UTF-8</encoding>
                 </source-folder>
                 <source-folder>
-                    <label>../buildtools/nasgen/src</label>
+                    <label>../src/jdk.scripting.nashorn/share/classes</label>
                     <type>java</type>
-                    <location>../buildtools/nasgen/src</location>
+                    <location>../src/jdk.scripting.nashorn/share/classes</location>
                     <encoding>UTF-8</encoding>
                 </source-folder>
             </folders>
@@ -132,12 +132,12 @@
                         <location>../test/src</location>
                     </source-folder>
                     <source-folder style="packages">
-                        <label>../src</label>
-                        <location>../src</location>
+                        <label>../buildtools/nasgen/src</label>
+                        <location>../buildtools/nasgen/src</location>
                     </source-folder>
                     <source-folder style="packages">
-                        <label>../buildtools/nasgen/src</label>
-                        <location>../buildtools/nasgen/src</location>
+                        <label>../src/jdk.scripting.nashorn/share/classes</label>
+                        <location>../src/jdk.scripting.nashorn/share/classes</location>
                     </source-folder>
                     <source-file>
                         <location>build.xml</location>
@@ -159,11 +159,7 @@
             <compilation-unit>
                 <package-root>../test/src</package-root>
                 <unit-tests/>
-                <classpath mode="compile">../test/lib/testng.jar:../build/classes:../src</classpath>
-                <source-level>1.7</source-level>
-            </compilation-unit>
-            <compilation-unit>
-                <package-root>../src</package-root>
+                <classpath mode="compile">../test/lib/testng.jar:../build/classes:../src/jdk.scripting.nashorn/share/classes</classpath>
                 <source-level>1.7</source-level>
             </compilation-unit>
             <compilation-unit>
@@ -171,6 +167,10 @@
                 <classpath mode="compile">../build/classes:../src</classpath>
                 <source-level>1.7</source-level>
             </compilation-unit>
+            <compilation-unit>
+                <package-root>../src/jdk.scripting.nashorn/share/classes</package-root>
+                <source-level>1.7</source-level>
+            </compilation-unit>
         </java-data>
     </configuration>
 </project>
--- a/make/project.properties	Thu Dec 18 19:57:57 2014 -0800
+++ b/make/project.properties	Tue Dec 23 13:57:28 2014 -0800
@@ -24,7 +24,7 @@
 application.title=nashorn
 
 # location of JDK embedded ASM sources
-jdk.asm.src.dir=../jdk/src/share/classes/jdk/internal/org/objectweb/asm
+jdk.asm.src.dir=../jdk/src/java.base/share/classes/jdk/internal/org/objectweb/asm
 
 # source and target levels
 build.compiler=modern
--- a/samples/browser_dom.js	Thu Dec 18 19:57:57 2014 -0800
+++ b/samples/browser_dom.js	Tue Dec 23 13:57:28 2014 -0800
@@ -1,4 +1,4 @@
-#// Usage: jjs -fx browser.js
+#// Usage: jjs -fx browser_dom.js
 
 /*
  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
@@ -32,7 +32,7 @@
  */
 
 if (!$OPTIONS._fx) {
-    print("Usage: jjs -fx browser.js");
+    print("Usage: jjs -fx browser_dom.js");
     exit(1);
 }
 
@@ -40,7 +40,6 @@
 var ChangeListener = Java.type("javafx.beans.value.ChangeListener");
 var Scene     = Java.type("javafx.scene.Scene");
 var WebView   = Java.type("javafx.scene.web.WebView");
-var EventListener = Java.type("org.w3c.dom.events.EventListener");
 
 // JavaFX start method
 function start(stage) {
@@ -74,10 +73,10 @@
                var btn = document.createElement("button");
                var n = 0;
                // attach a button handler - nashorn function!
-               btn.onclick = new EventListener(function() {
+               btn.onclick = function() {
                    n++; print("You clicked " + n + " time(s)");
                    print("you clicked OK " + wv.engine.executeScript("okCount"));
-               });
+               };
                // attach text to button
                var t = document.createTextNode("Click Me!"); 
                btn.appendChild(t);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/samples/time_color.fx	Tue Dec 23 13:57:28 2014 -0800
@@ -0,0 +1,89 @@
+#// Usage: jjs -fx time_color.js [-- true/false]
+
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ *   - Neither the name of Oracle nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// A simple javafx program that changes background color
+// of scene based on current time value (once per sec).
+// inspired by http://whatcolourisit.scn9a.org/
+
+if (!$OPTIONS._fx) {
+    print("Usage: jjs -fx time_color.js");
+    print("       jjs -fx time_color.js -- true");
+    exit(1);
+}
+
+// JavaFX classes used
+var Color = Java.type("javafx.scene.paint.Color");
+var Group = Java.type("javafx.scene.Group");
+var Label = Java.type("javafx.scene.control.Label");
+var Platform = Java.type("javafx.application.Platform");
+var Scene = Java.type("javafx.scene.Scene");
+var Timer = Java.type("java.util.Timer");
+
+// execute function periodically once per given time in millisec
+function setInterval(func, ms) {
+    // New timer, run as daemon so the application can quit
+    var timer = new Timer("setInterval", true);
+    timer.schedule(function() Platform.runLater(func), ms, ms);	
+    return timer;
+}
+
+// do you want to flip hour/min/sec for RGB?
+var flip = arguments.length > 0? "true".equals(arguments[0]) : false;
+
+// JavaFX start method
+function start(stage) {
+    start.title = "Time Color";
+    var root = new Group();
+    var label = new Label("time");
+    label.textFill = Color.WHITE;
+    root.children.add(label); 
+    stage.scene = new Scene(root, 700, 500);
+
+    setInterval(function() {
+        var d = new Date();
+        var hours = d.getHours();
+	var mins = d.getMinutes();
+	var secs = d.getSeconds();
+
+        if (hours < 10) hours = "0" + hours;
+        if (mins < 10) mins = "0" + mins;
+        if (secs < 10) secs = "0" + secs;
+
+	var hex = flip?
+            "#" + secs + mins + hours : "#" + hours + mins + secs;
+        label.text = "Color: " + hex;
+        stage.scene.fill = Color.web(hex);
+    }, 1000);
+
+    stage.show();
+}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java	Tue Dec 23 13:57:28 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<? 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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java	Tue Dec 23 13:57:28 2014 -0800
@@ -67,7 +67,7 @@
 import jdk.nashorn.internal.runtime.options.Options;
 
 /**
- * Static utility that encapsulates persistence of type information for functions compiled with optimistic
+ * <p>Static utility that encapsulates persistence of type information for functions compiled with optimistic
  * typing. With this feature enabled, when a JavaScript function is recompiled because it gets deoptimized,
  * the type information for deoptimization is stored in a cache file. If the same function is compiled in a
  * subsequent JVM invocation, the type information is used for initial compilation, thus allowing the system
@@ -83,6 +83,7 @@
  * {@code nashorn.typeInfo.cleanupDelaySeconds} system property. You can also specify the word
  * {@code unlimited} as the value for {@code nashorn.typeInfo.maxFiles} in which case the type info cache is
  * allowed to grow without limits.
+ * </p>
  */
 public final class OptimisticTypesPersistence {
     // Default is 0, for disabling the feature when not specified. A reasonable default when enabled is
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java	Tue Dec 23 13:57:28 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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return type == null ? getMostPessimisticType() : type;
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java	Tue Dec 23 13:57:28 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<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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java	Tue Dec 23 13:57:28 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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return optimisticType == null ? Type.OBJECT : optimisticType;
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java	Tue Dec 23 13:57:28 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<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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java	Tue Dec 23 13:57:28 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;
@@ -1101,7 +1100,7 @@
     }
 
     @Override
-    public Type getType(final Function<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return FUNCTION_TYPE;
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java	Tue Dec 23 13:57:28 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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return Type.INT;
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java	Tue Dec 23 13:57:28 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<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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java	Tue Dec 23 13:57:28 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<Symbol, Type> localVariableTypes) {
-        return expression.getType(localVariableTypes);
+    public Type getType() {
+        return expression.getType();
     }
 
     @Override
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java	Tue Dec 23 13:57:28 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<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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java	Tue Dec 23 13:57:28 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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return Type.OBJECT;
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java	Tue Dec 23 13:57:28 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<Symbol, Type> localVariableTypes) {
+    public Type getType() {
         return request.getReturnType();
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java	Tue Dec 23 13:57:28 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<Symbol, Type> localVariableTypes) {
-        return Type.widestReturnType(getTrueExpression().getType(localVariableTypes), getFalseExpression().getType(localVariableTypes));
+    public Type getType() {
+        return Type.widestReturnType(getTrueExpression().getType(), getFalseExpression().getType());
     }
 
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java	Tue Dec 23 13:57:28 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<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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java	Tue Dec 23 13:57:28 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();
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java	Tue Dec 23 13:57:28 2014 -0800
@@ -275,7 +275,7 @@
         final PropertyDescriptor newLenDesc = desc;
 
         // Step 3c and 3d - get new length and convert to long
-        final long newLen = NativeArray.validLength(newLenDesc.getValue(), true);
+        final long newLen = NativeArray.validLength(newLenDesc.getValue());
 
         // Step 3e
         newLenDesc.setValue(newLen);
@@ -348,8 +348,8 @@
         final PropertyDescriptor oldLenDesc = (PropertyDescriptor) super.getOwnPropertyDescriptor("length");
 
         // Step 2
-        // get old length and convert to long
-        final long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true);
+        // get old length and convert to long. Always a Long/Uint32 but we take the safe road.
+        final long oldLen = JSType.toUint32(oldLenDesc.getValue());
 
         // Step 3
         if ("length".equals(key)) {
@@ -471,7 +471,7 @@
     @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
     public static void length(final Object self, final Object length) {
         if (isArray(self)) {
-            ((ScriptObject)self).setLength(validLength(length, true));
+            ((ScriptObject)self).setLength(validLength(length));
         }
     }
 
@@ -495,18 +495,13 @@
         length(self, length);  // Same as instance setter but we can't make nasgen use the same method for prototype
     }
 
-    static long validLength(final Object length, final boolean reject) {
+    static long validLength(final Object length) {
+        // ES5 15.4.5.1, steps 3.c and 3.d require two ToNumber conversions here
         final double doubleLength = JSType.toNumber(length);
-        if (!Double.isNaN(doubleLength) && JSType.isRepresentableAsLong(doubleLength)) {
-            final long len = (long) doubleLength;
-            if (len >= 0 && len <= JSType.MAX_UINT) {
-                return len;
-            }
-        }
-        if (reject) {
+        if (doubleLength != JSType.toUint32(length)) {
             throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length));
         }
-        return -1;
+        return (long) doubleLength;
     }
 
     /**
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeRegExpExecResult.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeRegExpExecResult.java	Tue Dec 23 13:57:28 2014 -0800
@@ -88,7 +88,7 @@
     @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
     public static void length(final Object self, final Object length) {
         if (self instanceof ScriptObject) {
-            ((ScriptObject)self).setLength(NativeArray.validLength(length, true));
+            ((ScriptObject)self).setLength(NativeArray.validLength(length));
         }
     }
 }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java	Tue Dec 23 13:57:28 2014 -0800
@@ -86,7 +86,7 @@
      * @param source the script source
      * @param mainClassName the main class name
      * @param classBytes map of class names to class bytes
-     * @param initializers compilation id -> FunctionInitializer map
+     * @param initializers compilation id -&gt; FunctionInitializer map
      * @param constants constants array
      * @param compilationId compilation id
      */
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java	Tue Dec 23 13:57:28 2014 -0800
@@ -180,10 +180,10 @@
     /** Div exact wrapper for potentially integer division that turns into float point */
     public static final Call DIV_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
 
-    /** Div zero wrapper for long division that handles (0/0) >>> 0 == 0 */
+    /** Div zero wrapper for long division that handles (0/0) &gt;&gt;&gt; 0 == 0 */
     public static final Call DIV_ZERO_LONG        = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", long.class, long.class, long.class);
 
-    /** Mod zero wrapper for long division that handles (0%0) >>> 0 == 0 */
+    /** Mod zero wrapper for long division that handles (0%0) &gt;&gt;&gt; 0 == 0 */
     public static final Call REM_ZERO_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", long.class, long.class, long.class);
 
     /** Mod exact wrapper for potentially integer remainders that turns into float point */
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StoredScript.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StoredScript.java	Tue Dec 23 13:57:28 2014 -0800
@@ -58,7 +58,7 @@
      * @param compilationId compilation id
      * @param mainClassName main class name
      * @param classBytes map of class names to class bytes
-     * @param initializers initializer map, id -> FunctionInitializer
+     * @param initializers initializer map, id -&gt; FunctionInitializer
      * @param constants constants array
      */
     public StoredScript(final int compilationId, final String mainClassName, final Map<String, byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, final Object[] constants) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java	Tue Dec 23 13:57:28 2014 -0800
@@ -275,7 +275,7 @@
     /**
      * Align an array size up to the nearest array chunk size
      * @param size size required
-     * @return size given, always >= size
+     * @return size given, always &gt;= size
      */
     protected final static int alignUp(final int size) {
         return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java	Tue Dec 23 13:57:28 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;) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java	Tue Dec 23 13:57:28 2014 -0800
@@ -40,6 +40,7 @@
 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
 import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
+import jdk.nashorn.internal.runtime.ConsString;
 import jdk.nashorn.internal.runtime.JSType;
 
 /**
@@ -118,20 +119,21 @@
     private GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request, final LinkerServices linkerServices) throws Exception {
         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
         final int c = desc.getNameTokenCount();
+        GuardedInvocation inv;
+        try {
+            inv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
+        } catch (Throwable th) {
+            inv = null;
+        }
 
         switch (operator) {
             case "getProp":
             case "getElem":
             case "getMethod":
-                if (c > 2) {
-                    return findGetMethod(desc);
-                }
-            // For indexed get, we want GuardedInvocation from beans linker and pass it.
-            // BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access.
-            return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices));
+                return c > 2? findGetMethod(desc, inv) : findGetIndexMethod(inv);
             case "setProp":
             case "setElem":
-                return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
+                return c > 2? findSetMethod(desc, inv) : findSetIndexMethod();
             case "call":
                 return findCallMethod(desc);
             default:
@@ -139,7 +141,10 @@
         }
     }
 
-    private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
+    private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final GuardedInvocation inv) {
+        if (inv != null) {
+            return inv;
+        }
         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
         final MethodHandle getter = MH.insertArguments(JSOBJECT_GETMEMBER, 1, name);
         return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
@@ -150,7 +155,10 @@
         return inv.replaceMethods(getter, inv.getGuard());
     }
 
-    private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) {
+    private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final GuardedInvocation inv) {
+        if (inv != null) {
+            return inv;
+        }
         final MethodHandle getter = MH.insertArguments(JSOBJECT_SETMEMBER, 1, desc.getNameToken(2));
         return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
     }
@@ -178,12 +186,12 @@
             if (index > -1) {
                 return JSOBJECT_GETSLOT.invokeExact(jsobj, index);
             }
-        } else if (key instanceof String) {
-            final String name = (String)key;
+        } else if (key instanceof String || key instanceof ConsString) {
+            final String name = key.toString();
             if (name.indexOf('(') != -1) {
-                return fallback.invokeExact(jsobj, key);
+                return fallback.invokeExact(jsobj, (Object) name);
             }
-            return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
+            return JSOBJECT_GETMEMBER.invokeExact(jsobj, name);
         }
         return null;
     }
@@ -194,8 +202,8 @@
             JSOBJECT_SETSLOT.invokeExact(jsobj, (int)key, value);
         } else if (key instanceof Number) {
             JSOBJECT_SETSLOT.invokeExact(jsobj, getIndex((Number)key), value);
-        } else if (key instanceof String) {
-            JSOBJECT_SETMEMBER.invokeExact(jsobj, (String)key, value);
+        } else if (key instanceof String || key instanceof ConsString) {
+            JSOBJECT_SETMEMBER.invokeExact(jsobj, key.toString(), value);
         }
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Tue Dec 23 13:57:28 2014 -0800
@@ -42,6 +42,7 @@
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
+import jdk.nashorn.internal.runtime.ConsString;
 import jdk.nashorn.internal.runtime.JSType;
 
 /**
@@ -185,11 +186,11 @@
             if (index > -1) {
                 return ((JSObject)jsobj).getSlot(index);
             }
-        } else if (key instanceof String) {
-            final String name = (String)key;
+        } else if (key instanceof String || key instanceof ConsString) {
+            final String name = key.toString();
             // get with method name and signature. delegate it to beans linker!
             if (name.indexOf('(') != -1) {
-                return fallback.invokeExact(jsobj, key);
+                return fallback.invokeExact(jsobj, (Object) name);
             }
             return ((JSObject)jsobj).getMember(name);
         }
@@ -202,8 +203,8 @@
             ((JSObject)jsobj).setSlot((Integer)key, value);
         } else if (key instanceof Number) {
             ((JSObject)jsobj).setSlot(getIndex((Number)key), value);
-        } else if (key instanceof String) {
-            ((JSObject)jsobj).setMember((String)key, value);
+        } else if (key instanceof String || key instanceof ConsString) {
+            ((JSObject)jsobj).setMember(key.toString(), value);
         }
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java	Tue Dec 23 13:57:28 2014 -0800
@@ -26,17 +26,23 @@
 package jdk.nashorn.internal.runtime.linker;
 
 import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.SwitchPoint;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
 import jdk.internal.dynalink.support.Guards;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.FindProperty;
 import jdk.nashorn.internal.runtime.GlobalConstants;
+import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.UserAccessorProperty;
 
 /**
@@ -46,6 +52,11 @@
  */
 public final class PrimitiveLookup {
 
+    /** Method handle to link setters on primitive base. See ES5 8.7.2. */
+    private static final MethodHandle PRIMITIVE_SETTER = findOwnMH("primitiveSetter",
+            MH.type(void.class, ScriptObject.class, Object.class, Object.class, boolean.class, Object.class));
+
+
     private PrimitiveLookup() {
     }
 
@@ -87,40 +98,58 @@
                                                     final ScriptObject wrappedReceiver, final MethodHandle wrapFilter,
                                                     final MethodHandle protoFilter) {
         final CallSiteDescriptor desc = request.getCallSiteDescriptor();
+        final String name;
+        final FindProperty find;
 
-        //checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem)
-        //if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be.
-        //so in that case we can skip creation of primitive wrapper and start our search with the prototype.
         if (desc.getNameTokenCount() > 2) {
-            final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
-            final FindProperty find = wrappedReceiver.findProperty(name, true);
+            name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+            find = wrappedReceiver.findProperty(name, true);
+        } else {
+            name = null;
+            find = null;
+        }
 
-            if (find == null) {
-                // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it.
-                return null;
-            }
+        final String firstOp = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
 
-            final SwitchPoint sp = find.getProperty().getBuiltinSwitchPoint(); //can use this instead of proto filter
-            if (sp instanceof Context.BuiltinSwitchPoint && !sp.hasBeenInvalidated()) {
-                return new GuardedInvocation(GlobalConstants.staticConstantGetter(find.getObjectValue()), guard, sp, null);
-            }
+        switch (firstOp) {
+        case "getProp":
+        case "getElem":
+        case "getMethod":
+            //checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem)
+            //if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be.
+            //so in that case we can skip creation of primitive wrapper and start our search with the prototype.
+            if (name != null) {
+                if (find == null) {
+                    // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it.
+                    return null;
+                }
 
-            if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
-                // If property is found in the prototype object bind the method handle directly to
-                // the proto filter instead of going through wrapper instantiation below.
-                final ScriptObject proto = wrappedReceiver.getProto();
-                final GuardedInvocation link = proto.lookup(desc, request);
+                final SwitchPoint sp = find.getProperty().getBuiltinSwitchPoint(); //can use this instead of proto filter
+                if (sp instanceof Context.BuiltinSwitchPoint && !sp.hasBeenInvalidated()) {
+                    return new GuardedInvocation(GlobalConstants.staticConstantGetter(find.getObjectValue()), guard, sp, null);
+                }
 
-                if (link != null) {
-                    final MethodHandle invocation = link.getInvocation(); //this contains the builtin switchpoint
+                if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
+                    // If property is found in the prototype object bind the method handle directly to
+                    // the proto filter instead of going through wrapper instantiation below.
+                    final ScriptObject proto = wrappedReceiver.getProto();
+                    final GuardedInvocation link = proto.lookup(desc, request);
 
-                    final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class));
-                    final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter);
-                    final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter);
-
-                    return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard));
+                    if (link != null) {
+                        final MethodHandle invocation = link.getInvocation(); //this contains the builtin switchpoint
+                        final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class));
+                        final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter);
+                        final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter);
+                        return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard));
+                    }
                 }
             }
+            break;
+        case "setProp":
+        case "setElem":
+            return getPrimitiveSetter(name, guard, wrapFilter, NashornCallSiteDescriptor.isStrict(desc));
+        default:
+            break;
         }
 
         final GuardedInvocation link = wrappedReceiver.lookup(desc, request);
@@ -138,4 +167,41 @@
 
         return null;
     }
+
+    private static GuardedInvocation getPrimitiveSetter(final String name, final MethodHandle guard,
+                                                        final MethodHandle wrapFilter, final boolean isStrict) {
+        MethodHandle filter = MH.asType(wrapFilter, wrapFilter.type().changeReturnType(ScriptObject.class));
+        final MethodHandle target;
+
+        if (name == null) {
+            filter = MH.dropArguments(filter, 1, Object.class, Object.class);
+            target = MH.insertArguments(PRIMITIVE_SETTER, 3, isStrict);
+        } else {
+            filter = MH.dropArguments(filter, 1, Object.class);
+            target = MH.insertArguments(PRIMITIVE_SETTER, 2, name, isStrict);
+        }
+
+        return new GuardedInvocation(MH.foldArguments(target, filter), guard);
+    }
+
+
+    @SuppressWarnings("unused")
+    private static void primitiveSetter(final ScriptObject wrappedSelf, final Object self, final Object key,
+                                        final boolean strict, final Object value) {
+        // See ES5.1 8.7.2 PutValue (V, W)
+        final String name = JSType.toString(key);
+        final FindProperty find = wrappedSelf.findProperty(name, true);
+        if (find == null || !(find.getProperty() instanceof UserAccessorProperty) || !find.getProperty().isWritable()) {
+            if (strict) {
+                throw typeError("property.not.writable", name, ScriptRuntime.safeToString(self));
+            }
+            return;
+        }
+        // property found and is a UserAccessorProperty
+        find.setValue(value, strict);
+    }
+
+    private static MethodHandle findOwnMH(final String name, final MethodType type) {
+        return MH.findStatic(MethodHandles.lookup(), PrimitiveLookup.class, name, type);
+    }
 }
--- a/test/script/basic/JDK-8055762.js	Thu Dec 18 19:57:57 2014 -0800
+++ b/test/script/basic/JDK-8055762.js	Tue Dec 23 13:57:28 2014 -0800
@@ -74,9 +74,12 @@
         }
     };
 
+    var a = "a";
     print(obj["foo"]);
+    print(obj[a + "bc"]);
     print(obj[2]);
     obj.bar = 23;
+    obj[a + "bc"] = 23;
     obj[3] = 23;
     obj.func("hello");
 }
--- a/test/script/basic/JDK-8055762.js.EXPECTED	Thu Dec 18 19:57:57 2014 -0800
+++ b/test/script/basic/JDK-8055762.js.EXPECTED	Tue Dec 23 13:57:28 2014 -0800
@@ -1,5 +1,7 @@
 FOO
+ABC
 0
 bar set to 23
+abc set to 23
 [3] set to 23
 func called with hello
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8066215.js	Tue Dec 23 13:57:28 2014 -0800
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2010, 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-8066215: Fuzzing bug: length valueOf bug
+ *
+ * @test
+ * @run
+ */
+
+function defineLength(arr, length) {
+    Object.defineProperty(arr, "length", {
+        value: {
+            valueOf: function() {
+                print("value retrieved: " + length);
+                return length;
+            }
+        }
+    });
+    print("done: " + arr.length + ", " + typeof arr.length);
+}
+
+var a = [];
+defineLength(a, 3);
+defineLength(a, 6);
+defineLength(a, 3);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8066215.js.EXPECTED	Tue Dec 23 13:57:28 2014 -0800
@@ -0,0 +1,9 @@
+value retrieved: 3
+value retrieved: 3
+done: 3, number
+value retrieved: 6
+value retrieved: 6
+done: 6, number
+value retrieved: 3
+value retrieved: 3
+done: 3, number
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8066226.js	Tue Dec 23 13:57:28 2014 -0800
@@ -0,0 +1,132 @@
+/*
+ * 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-8066226: Fuzzing bug: parameter counts differ in TypeConverterFactory
+ *
+ * @test
+ * @run
+ */
+
+Object.defineProperty(Object.prototype, "accessor", {
+    set: function(value) {
+        print("Setting accessor on " + this + " to " + value);
+    }
+});
+
+Object.defineProperty(Object.prototype, "getterOnly", {
+    get: function() {
+        return 1;
+    }
+});
+
+function set(o) {
+    print("set(" + o + ")");
+    o.foo = 1;
+    o.constructor = 1;
+    o.accessor = 1;
+    o.getterOnly = 1;
+    print();
+}
+
+function setStrict(o) {
+    "use strict";
+    print("setStrict(" + o + ")")
+    try {
+        o.foo = 1;
+    } catch (e) {
+        print(e);
+    }
+    try {
+        o.constructor = 1;
+    } catch (e) {
+        print(e);
+    }
+    try {
+        o.accessor = 1;
+    } catch (e) {
+        print(e);
+    }
+    try {
+        o.getterOnly = 1;
+    } catch (e) {
+        print(e);
+    }
+    print();
+}
+
+function setAttr(o, id) {
+    print("setAttr(" + o + ", " + id + ")")
+    o[id] = 1;
+    print();
+}
+
+function setAttrStrict(o, id) {
+    "use strict";
+    print("setAttrStrict(" + o + ", " + id + ")")
+    try {
+        o[id] = 1;
+    } catch (e) {
+        print(e);
+    }
+    print();
+}
+
+set(1);
+set("str");
+set(true);
+set({});
+set([]);
+
+setStrict(1);
+setStrict("str");
+setStrict(true);
+setStrict({});
+setStrict([]);
+
+setAttr(1, "foo");
+setAttr(1, "constructor");
+setAttr(1, "accessor");
+setAttr(1, "getterOnly");
+setAttr("str", "foo");
+setAttr("str", "constructor");
+setAttr("str", "accessor");
+setAttr("str", "getterOnly");
+setAttr(true, "foo");
+setAttr(true, "constructor");
+setAttr(true, "accessor");
+setAttr(true, "getterOnly");
+
+setAttrStrict(1, "foo");
+setAttrStrict(1, "constructor");
+setAttrStrict(1, "accessor");
+setAttrStrict(1, "getterOnly");
+setAttrStrict("str", "foo");
+setAttrStrict("str", "constructor");
+setAttrStrict("str", "accessor");
+setAttrStrict("str", "getterOnly");
+setAttrStrict(true, "foo");
+setAttrStrict(true, "constructor");
+setAttrStrict(true, "accessor");
+setAttrStrict(true, "getterOnly");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8066226.js.EXPECTED	Tue Dec 23 13:57:28 2014 -0800
@@ -0,0 +1,104 @@
+set(1)
+Setting accessor on 1 to 1
+
+set(str)
+Setting accessor on str to 1
+
+set(true)
+Setting accessor on true to 1
+
+set([object Object])
+Setting accessor on [object Object] to 1
+
+set()
+Setting accessor on  to 1
+
+setStrict(1)
+TypeError: "foo" is not a writable property of 1
+TypeError: "constructor" is not a writable property of 1
+Setting accessor on 1 to 1
+TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter
+
+setStrict(str)
+TypeError: "foo" is not a writable property of str
+TypeError: "constructor" is not a writable property of str
+Setting accessor on str to 1
+TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter
+
+setStrict(true)
+TypeError: "foo" is not a writable property of true
+TypeError: "constructor" is not a writable property of true
+Setting accessor on true to 1
+TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter
+
+setStrict([object Object])
+Setting accessor on [object Object] to 1
+TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter
+
+setStrict()
+Setting accessor on  to 1
+TypeError: Cannot set property "getterOnly" of [object Array] that has only a getter
+
+setAttr(1, foo)
+
+setAttr(1, constructor)
+
+setAttr(1, accessor)
+Setting accessor on 1 to 1
+
+setAttr(1, getterOnly)
+
+setAttr(str, foo)
+
+setAttr(str, constructor)
+
+setAttr(str, accessor)
+Setting accessor on str to 1
+
+setAttr(str, getterOnly)
+
+setAttr(true, foo)
+
+setAttr(true, constructor)
+
+setAttr(true, accessor)
+Setting accessor on true to 1
+
+setAttr(true, getterOnly)
+
+setAttrStrict(1, foo)
+TypeError: "foo" is not a writable property of 1
+
+setAttrStrict(1, constructor)
+TypeError: "constructor" is not a writable property of 1
+
+setAttrStrict(1, accessor)
+Setting accessor on 1 to 1
+
+setAttrStrict(1, getterOnly)
+TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter
+
+setAttrStrict(str, foo)
+TypeError: "foo" is not a writable property of str
+
+setAttrStrict(str, constructor)
+TypeError: "constructor" is not a writable property of str
+
+setAttrStrict(str, accessor)
+Setting accessor on str to 1
+
+setAttrStrict(str, getterOnly)
+TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter
+
+setAttrStrict(true, foo)
+TypeError: "foo" is not a writable property of true
+
+setAttrStrict(true, constructor)
+TypeError: "constructor" is not a writable property of true
+
+setAttrStrict(true, accessor)
+Setting accessor on true to 1
+
+setAttrStrict(true, getterOnly)
+TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8067774.js	Tue Dec 23 13:57:28 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));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8067774.js.EXPECTED	Tue Dec 23 13:57:28 2014 -0800
@@ -0,0 +1,1 @@
+1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/trusted/JDK-8067854.js	Tue Dec 23 13:57:28 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");
+}
--- a/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java	Thu Dec 18 19:57:57 2014 -0800
+++ b/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java	Tue Dec 23 13:57:28 2014 -0800
@@ -109,6 +109,35 @@
         }
     }
 
+    // @bug 8062030: Nashorn bug retrieving array property after key string concatenation
+    @Test
+    // ConsString attribute access on a JSObject
+    public void consStringTest() {
+        final ScriptEngineManager m = new ScriptEngineManager();
+        final ScriptEngine e = m.getEngineByName("nashorn");
+        try {
+            final MapWrapperObject obj = new MapWrapperObject();
+            e.put("obj", obj);
+            e.put("f", "f");
+            e.eval("obj[f + 'oo'] = 'bar';");
+
+            assertEquals(obj.getMap().get("foo"), "bar");
+            assertEquals(e.eval("obj[f + 'oo']"), "bar");
+            assertEquals(e.eval("obj['foo']"), "bar");
+            assertEquals(e.eval("f + 'oo' in obj"), Boolean.TRUE);
+            assertEquals(e.eval("'foo' in obj"), Boolean.TRUE);
+            e.eval("delete obj[f + 'oo']");
+            assertFalse(obj.getMap().containsKey("foo"));
+            assertEquals(e.eval("obj[f + 'oo']"), null);
+            assertEquals(e.eval("obj['foo']"), null);
+            assertEquals(e.eval("f + 'oo' in obj"), Boolean.FALSE);
+            assertEquals(e.eval("'foo' in obj"), Boolean.FALSE);
+        } catch (final Exception exp) {
+            exp.printStackTrace();
+            fail(exp.getMessage());
+        }
+    }
+
     public static class BufferObject extends AbstractJSObject {
         private final IntBuffer buf;