changeset 56:59970b70ebb5

8007062: Split Lower up into Lower/Attr/FinalizeTypes. Integrate AccessSpecalizer into FinalizeTypes. Summary: Lower suffered from being a "God class" trying to do everything at once. As Nashorn code generation has grown, so has Lower. It does several post processing passes, tries to do several things at once even though all type information isn't in place, adjusting state afterwards and so on. It also performs control flow analysis, type attribution and constant folding, and everything else code generation related before byte code emission. I have now separated the compilation process into Lower (create low level nodes from high level ones, copy code such as finally block inlining etc), Attr (assign types and symbols to all nodes - freeze slot and scope information) and FinalizeTypes (insert explicit casts, specialize invoke dynamic types for scope accesses). I've removed the kludgy AccessSpecializer, as this now integrates naturally with typing. Everything is now much easier to read and each module performs only one thing. I have added separate loggers for the separate tiers. In the process I have also fixed: (1) problems with type coercion (see test/script/basic/typecoercion.js, basically our coercion was too late and our symbol inference was erroneous. This only manifested itself in very rare occasions where toNumber coercion has side effects, such as for example when valueOf is overridden) (2) copying literal nodes (literal copy did not use the superclass copy, which made all the Node specific fields not to be copied (3) erroneous literal tokenization (literals shouldn't always just inherit token information from whatever node that creates them) (4) splitter weighnodes - unary nodes were considered weightless (4) removed the hateful and kludgy "VarNode.shouldAppend", which really isn't needed when we have an attribution phase that determines self reference symbols (the only thing it was used for) (5) duplicate line number issues in the parser (6) convert bug in CodeGenerator for intermediate results of scope accesses (see test/script/basic/access-specializer.js) ... Several of these things just stopped being problems with the new architecture "can't happen anymore" and are not bug fixes per se. All tests run. No performance regressions exist that I've been able to measure. Some increases in performance were measured, but in the statistical margin of error (which is very wide as HotSpot currently has warmup issues with LambdaForms/invoke dynamic). Compile speed has not measurably increased. Reviewed-by: jlaskey, attila
author lagergren
date Wed, 30 Jan 2013 12:26:45 +0100
parents 755404d7d189
children ca6d5e4b8170
files docs/DEVELOPER_README src/jdk/nashorn/api/scripting/Formatter.java src/jdk/nashorn/api/scripting/NashornScriptEngine.java src/jdk/nashorn/internal/codegen/AccessSpecializer.java src/jdk/nashorn/internal/codegen/Attr.java src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/Compiler.java src/jdk/nashorn/internal/codegen/FinalizeTypes.java src/jdk/nashorn/internal/codegen/FoldConstants.java src/jdk/nashorn/internal/codegen/Lower.java src/jdk/nashorn/internal/codegen/MethodEmitter.java src/jdk/nashorn/internal/codegen/SharedScopeCall.java src/jdk/nashorn/internal/codegen/WeighNodes.java src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java src/jdk/nashorn/internal/ir/Block.java src/jdk/nashorn/internal/ir/CallNode.java src/jdk/nashorn/internal/ir/CatchNode.java src/jdk/nashorn/internal/ir/ExecuteNode.java src/jdk/nashorn/internal/ir/FunctionNode.java src/jdk/nashorn/internal/ir/LiteralNode.java src/jdk/nashorn/internal/ir/Node.java src/jdk/nashorn/internal/ir/RuntimeNode.java src/jdk/nashorn/internal/ir/Symbol.java src/jdk/nashorn/internal/ir/TryNode.java src/jdk/nashorn/internal/ir/VarNode.java src/jdk/nashorn/internal/ir/debug/ASTWriter.java src/jdk/nashorn/internal/objects/NativeJSON.java src/jdk/nashorn/internal/objects/NativeString.java src/jdk/nashorn/internal/parser/AbstractParser.java src/jdk/nashorn/internal/parser/Parser.java src/jdk/nashorn/internal/parser/TokenType.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/DebugLogger.java src/jdk/nashorn/internal/runtime/OptionsObject.java src/jdk/nashorn/internal/runtime/PropertyMap.java src/jdk/nashorn/internal/runtime/ScriptObject.java src/jdk/nashorn/internal/runtime/ScriptingFunctions.java src/jdk/nashorn/internal/runtime/arrays/ArrayIterator.java src/jdk/nashorn/internal/runtime/arrays/ArrayLikeIterator.java src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java src/jdk/nashorn/internal/runtime/options/Options.java src/jdk/nashorn/tools/Shell.java test/script/basic/access-specializer.js test/script/basic/compile-octane.js.EXPECTED test/script/basic/run-octane.js test/script/basic/typecoerce.js test/script/basic/typecoerce.js.EXPECTED
diffstat 49 files changed, 4828 insertions(+), 3768 deletions(-) [+]
line wrap: on
line diff
--- a/docs/DEVELOPER_README	Tue Jan 29 14:25:39 2013 -0400
+++ b/docs/DEVELOPER_README	Wed Jan 30 12:26:45 2013 +0100
@@ -36,16 +36,6 @@
 equivalent to enabling the access logger with "info" level.
 
 
-SYSTEM PROPERTY: -Dnashorn.compiler.ints.disable
-
-This flag prevents ints and longs (non double values) from being used
-for any primitive representation in the lowered IR. This is default
-false, i.e Lower will attempt to use integer variables as long as it
-can. For example, var x = 17 would try to use x as an integer, unless
-other operations occur later that require coercion to wider type, for
-example x *= 17.1;
-
-
 SYSTEM PROPERTY: -Dnashorn.compiler.intarithmetic 
 
 Arithmetic operations in Nashorn (except bitwise ones) typically
@@ -195,7 +185,8 @@
 slots, possibly through code copying and versioning.
 
 
-SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=<x>
+SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=[<x>[,*]], 
+  -Dnashorn.compiler.symbol.stacktrace=[<x>[,*]]
 
 When this property is set, creation and manipulation of any symbol
 named "x" will show information about when the compiler changes its
@@ -203,8 +194,16 @@
 data. This is useful if, for example, a symbol shows up as an Object,
 when you believe it should be a primitive. Usually there is an
 explanation for this, for example that it exists in the global scope
-and type analysis has to be more conservative. In that case, the stack
-trace upon type change to object will usually tell us why.
+and type analysis has to be more conservative. 
+
+Several symbols names to watch can be specified by comma separation.
+
+If no variable name is specified (and no equals sign), all symbols
+will be watched
+
+By using "stacktrace" instead of or together with "trace", stack
+traces will be displayed upon symbol changes according to the same
+semantics.
 
 
 SYSTEM PROPERTY: nashorn.lexer.xmlliterals
@@ -349,6 +348,9 @@
 2. The loggers.
 ===============
 
+It is very simple to create your own logger. Use the DebugLogger class
+and give the subsystem name as a constructor argument.
+
 The Nashorn loggers can be used to print per-module or per-subsystem
 debug information with different levels of verbosity. The loggers for
 a given subsystem are available are enabled by using
@@ -382,7 +384,7 @@
 controlled from the Context. Log messages are, for example, about
 things like new compile units being allocated. The compiler has global
 settings that all the tiers of codegen (e.g. Lower and CodeGenerator)
-use.
+use.s
 
 
 * codegen
@@ -429,6 +431,18 @@
 
 * lower
 
+This is the first lowering pass.
+
+Lower is a code generation pass that turns high level IR nodes into
+lower level one, for example substituting comparisons to RuntimeNodes
+and inlining finally blocks.
+
+Lower is also responsible for determining control flow information
+like end points.
+
+
+* attr
+
 The lowering annotates a FunctionNode with symbols for each identifier
 and transforms high level constructs into lower level ones, that the
 CodeGenerator consumes.
@@ -439,14 +453,11 @@
 this logger. This will probably change.
 
 
-* access
+* finalize
 
-The --log=access option is equivalent to setting the system variable
-"nashorn.callsiteaccess.debug" to true. There are several levels of
-the access logger, usually the default level "info" is enough
-
-It is very simple to create your own logger. Use the DebugLogger class
-and give the subsystem name as a constructor argument.
+This --log=finalize log option outputs information for type finalization,
+the third tier of the compiler. This means things like placement of 
+specialized scope nodes or explicit conversions. 
 
 
 * fields
--- a/src/jdk/nashorn/api/scripting/Formatter.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/api/scripting/Formatter.java	Wed Jan 30 12:26:45 2013 +0100
@@ -48,6 +48,9 @@
  */
 public final class Formatter {
 
+    private Formatter() {
+    }
+
     /**
      * Method which converts javascript types to java types for the
      * String.format method (jrunscript function sprintf).
@@ -149,7 +152,9 @@
         if (s != null) {
             try {
                 index = Integer.parseInt(s.substring(0, s.length() - 1));
-            } catch (NumberFormatException e) { }
+            } catch (final NumberFormatException e) {
+                //ignored
+            }
         } else {
             index = 0;
         }
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Wed Jan 30 12:26:45 2013 +0100
@@ -327,6 +327,7 @@
         try {
             final InputStream is = AccessController.doPrivileged(
                     new PrivilegedExceptionAction<InputStream>() {
+                        @Override
                         public InputStream run() throws Exception {
                             final URL url = NashornScriptEngine.class.getResource(script);
                             return url.openStream();
--- a/src/jdk/nashorn/internal/codegen/AccessSpecializer.java	Tue Jan 29 14:25:39 2013 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,399 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.
- */
-
-package jdk.nashorn.internal.codegen;
-
-import java.util.HashSet;
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.AccessNode;
-import jdk.nashorn.internal.ir.Assignment;
-import jdk.nashorn.internal.ir.BinaryNode;
-import jdk.nashorn.internal.ir.CallNode;
-import jdk.nashorn.internal.ir.IdentNode;
-import jdk.nashorn.internal.ir.IndexNode;
-import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReferenceNode;
-import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.ir.TypeOverride;
-import jdk.nashorn.internal.ir.UnaryNode;
-import jdk.nashorn.internal.ir.VarNode;
-import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.parser.Token;
-import jdk.nashorn.internal.parser.TokenType;
-import jdk.nashorn.internal.runtime.Debug;
-import jdk.nashorn.internal.runtime.DebugLogger;
-
-/**
- * This is a post pass for Lower, that removes casts for accessors to objects in
- * Scope, and replaces the accessor type with a narrower one with possible.
- *
- * Any node that implements TypeOverride will be subject to access specialization.
- *
- * TypeOverride basically means "type inference has determined that the field x
- * is an object, but if you do x & 17, it can be read as an int, which we hope
- * coincides with its internal representation. In that case there is no boxing
- * that may or may not be removed by the JVM and less data bandwidth.
- *
- * Ideally this should not be a post pass, but it requires slot AND scope info, and has
- * to be run where it is, which is called from {@link CodeGenerator}.
- *
- * @see TypeOverride
- */
-
-final class AccessSpecializer extends NodeOperatorVisitor {
-    /** Debug logger for access specialization. Enable it with --log=access:level
-        or -Dnashorn.callsiteaccess.debug */
-    private static final DebugLogger LOG   = new DebugLogger("access", "nashorn.callsiteaccess.debug");
-    private static final boolean     DEBUG = LOG.isEnabled();
-
-    @Override
-    public Node leave(final VarNode varNode) {
-        if (varNode.isAssignment()) {
-            return leaveAssign(varNode);
-        }
-
-        return varNode;
-    }
-
-    @Override
-    public Node leave(final CallNode callNode) {
-        final Node function = callNode.getFunction();
-        if (function instanceof ReferenceNode) {
-            changeType(callNode, ((ReferenceNode)function).getReference().getType());
-        }
-        return callNode;
-    }
-
-    @Override
-    public Node leaveASSIGN(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
-        return propagateResultType(binaryNode, binaryNode.lhs().getType());
-    }
-
-    @Override
-    public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
-        return propagateResultType(binaryNode, binaryNode.rhs().getType());
-    }
-
-    @Override
-    public Node leaveCONVERT(final UnaryNode unaryNode) {
-        final Type castTo = unaryNode.getType();
-
-        Node rhs = unaryNode.rhs();
-
-        // Go through all conversions until we find the first non-convert node.
-        while (rhs.tokenType() == TokenType.CONVERT) {
-            rhs = ((UnaryNode)rhs).rhs();
-        }
-
-        // If this node can be type changed
-        if (canHaveCallSiteType(rhs) && isSupportedCallSiteType(castTo)) {
-            /*
-             * Just add a callsite type and throw away the cast, appropriate
-             * getter/setter is selected, which will do the conversion for us
-             */
-            changeType(rhs, castTo);
-            LOG.fine("*** cast: converting " + debugNode(unaryNode) + " to " + debugNode(rhs));
-
-            return rhs;
-        }
-
-        // Micro optimization for node hygiene and pattern detection - remove unnecessary same type cast
-        if (unaryNode.getType().isEquivalentTo(rhs.getType())) {
-            return rhs;
-        }
-
-        return unaryNode;
-    }
-
-    @Override
-    public Node leaveDECINC(final UnaryNode unaryNode) {
-        assert unaryNode.isAssignment();
-
-        final Node dest = unaryNode.getAssignmentDest();
-        if (canHaveCallSiteType(dest) && isSupportedCallSiteType(unaryNode.getType())) {
-            changeTypeInAssignment(dest, unaryNode.getType());
-        }
-
-        return unaryNode;
-    }
-
-    /**
-     * Is this a node that can have its type overridden. This is true for
-     * AccessNodes, IndexNodes and IdentNodes
-     *
-     * @param node the node to check
-     * @return true if node can have a callsite type
-     */
-    private static boolean canHaveCallSiteType(final Node node) {
-        return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType();
-    }
-
-    /**
-     * Is the specialization type supported. Currently we treat booleans as objects
-     * and have no special boolean type accessor, thus booleans are ignored.
-     * TODO - support booleans? NASHORN-590
-     *
-     * @param castTo the type to check
-     * @return true if call site type is supported
-     */
-    private static boolean isSupportedCallSiteType(final Type castTo) {
-        return castTo.isNumeric(); // don't specializable for boolean
-    }
-
-    /**
-     * Turn a node into a covert node
-     *
-     * @param  node the node
-     * @return the node as a convert node
-     */
-    private static Node convert(final Node node) {
-        return new UnaryNode(node.getSource(),
-                Token.recast(node.getToken(), TokenType.CONVERT),
-                node);
-    }
-
-    private static Node leaveAssign(final Node node) {
-        assert node.isAssignment() : node + " is not an assignment";
-
-
-        final Node lhs = ((Assignment<?>)node).getAssignmentDest();
-        Node       rhs = ((Assignment<?>)node).getAssignmentSource();
-
-        /**
-         * Nodes with local variable slots  are assumed to be of their optimal type
-         * already and aren't affected here. This is not strictly true, for instance
-         * with doubles instead of in a bounded loop. TODO - range check: NASHORN-363
-         *
-         * This is also not strictly true for var y = x = 55; where y has no other uses
-         * Then y can be an int, but lower conservatively does an object of the assign to
-         * scope
-         */
-        final Symbol lhsSymbol = lhs.getSymbol();
-
-        if (lhsSymbol.hasSlot() && !lhsSymbol.isScope()) {
-            LOG.finest(lhs.getSymbol() + " has slot!");
-            if (!lhs.getType().isEquivalentTo(rhs.getType())) {
-                LOG.finest("\tslot assignment: " +lhs.getType()+ " " +rhs.getType() + " " + debugNode(node));
-
-                final Node c = convert(rhs);
-                c.setSymbol(lhsSymbol);
-                ((Assignment<?>)node).setAssignmentSource(c);
-
-                LOG.fine("*** slot assignment turned to : " + debugNode(node));
-            } else {
-                LOG.finest("aborted - type equivalence between lhs and rhs");
-            }
-
-            return node;
-        }
-
-        // e.g. __DIR__, __LINE__, __FILE__ - don't try to change these
-        if (lhs instanceof IdentNode && ((IdentNode)lhs).isSpecialIdentity()) {
-            return node;
-        }
-
-        /**
-         * Try to cast to the type of the right hand side, now pruned. E.g. an (object)17 should
-         * now be just 17, an int.
-         */
-        Type castTo = rhs.getType();
-
-        // If LHS can't get a new type, neither can rhs - retain the convert
-        if (!canHaveCallSiteType(lhs)) {
-            return node;
-        }
-
-        // Take the narrowest type of the entire cast sequence
-        while (rhs.tokenType() == TokenType.CONVERT) {
-            rhs    = ((UnaryNode)rhs).rhs();
-            castTo = Type.narrowest(rhs.getType(), castTo); //e.g. (object)(int) -> int even though object is outermost
-        }
-
-        // If castTo is wider than widestOperationType, castTo can be further slowed down
-        final Type widestOperationType = node.getWidestOperationType();
-        LOG.finest("node wants to be " + castTo + " and its widest operation is " + widestOperationType);
-
-        if (widestOperationType != castTo && Type.widest(castTo, widestOperationType) == castTo) {
-            LOG.info("###" + node + " castTo was " + castTo + " but could be downgraded to " + node.getWidestOperationType());
-            castTo = node.getWidestOperationType();
-            if (rhs instanceof TypeOverride) {
-                changeType(rhs, castTo);
-            }
-        }
-
-        /*
-         * If this is a self modifying op, we can't be narrower than the widest optype
-         * or e.g. x = x + 12 and x += 12 will turn into different things
-         */
-        if (node.isSelfModifying()) {
-            castTo = Type.widest(widestOperationType, castTo);
-        }
-
-        // We only specialize for numerics, not for booleans.
-        if (isSupportedCallSiteType(castTo)) {
-            if (rhs.getType() != castTo) {
-                LOG.finest("cast was necessary, abort: " + node + " " + rhs.getType() + " != " + castTo);
-                return node;
-            }
-
-            LOG.finest("assign: " + debugNode(node));
-
-            changeTypeInAssignment(lhs, castTo);
-            ((Assignment<?>)node).setAssignmentSource(rhs);
-
-            LOG.info("### modified to " + debugNode(node) + " (given type override " + castTo + ")");
-
-            propagateResultType(node, castTo);
-        }
-
-        return node;
-    }
-
-    private static Node propagateResultType(final Node node, final Type type) {
-        //warning! this CANNOT be done for non temporaries as they are used in other computations
-        if (isSupportedCallSiteType(type)) {
-            if (node.getSymbol().isTemp()) {
-                LOG.finest("changing temporary type: " + debugNode(node) + " to " + type);
-                node.getSymbol().setTypeOverride(type);
-                LOG.info("### node modified to " + debugNode(node) + " (given type override " + type + ")");
-            }
-        }
-        return node;
-    }
-
-    private static void changeTypeInAssignment(final Node dest, final Type newType) {
-        if (changeType(dest, newType)) {
-            LOG.finest("changed assignment " + dest + " " + dest.getSymbol());
-            assert !newType.isObject();
-
-            final HashSet<Node> exclude = new HashSet<>();
-
-            dest.accept(new NodeVisitor() {
-
-                private void setCanBePrimitive(final Symbol symbol) {
-                    LOG.fine("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
-                    symbol.setCanBePrimitive(newType);
-                }
-
-                @Override
-                public Node enter(final IdentNode identNode) {
-                    if (!exclude.contains(identNode)) {
-                        setCanBePrimitive(identNode.getSymbol());
-                    }
-                    return null;
-                }
-
-                @Override
-                public Node enter(final AccessNode accessNode) {
-                    setCanBePrimitive(accessNode.getProperty().getSymbol());
-                    return null;
-                }
-
-                @Override
-                public Node enter(final IndexNode indexNode) {
-                    exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
-                    return indexNode;
-                }
-
-            });
-        }
-    }
-
-    private static boolean changeType(final Node node, final Type newType) {
-        if (!node.getType().equals(newType)) {
-            ((TypeOverride)node).setType(newType);
-            return true;
-        }
-        return false;
-    }
-
-    private static String debugNode(final Node node) {
-        if (DEBUG) {
-            return node.toString();
-        }
-        return "";
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/codegen/Attr.java	Wed Jan 30 12:26:45 2013 +0100
@@ -0,0 +1,1584 @@
+/*
+ * Copyright (c) 2010, 2013, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
+import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
+import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
+import static jdk.nashorn.internal.ir.Symbol.IS_LET;
+import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
+import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
+import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.CallNode;
+import jdk.nashorn.internal.ir.CallNode.EvalArgs;
+import jdk.nashorn.internal.ir.CaseNode;
+import jdk.nashorn.internal.ir.CatchNode;
+import jdk.nashorn.internal.ir.ForNode;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.ObjectNode;
+import jdk.nashorn.internal.ir.PropertyNode;
+import jdk.nashorn.internal.ir.ReferenceNode;
+import jdk.nashorn.internal.ir.ReturnNode;
+import jdk.nashorn.internal.ir.RuntimeNode;
+import jdk.nashorn.internal.ir.RuntimeNode.Request;
+import jdk.nashorn.internal.ir.SwitchNode;
+import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.TernaryNode;
+import jdk.nashorn.internal.ir.TryNode;
+import jdk.nashorn.internal.ir.UnaryNode;
+import jdk.nashorn.internal.ir.VarNode;
+import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.parser.TokenType;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
+import jdk.nashorn.internal.runtime.DebugLogger;
+import jdk.nashorn.internal.runtime.ECMAException;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.Source;
+
+/**
+ * This is the attribution pass of the code generator. Attr takes Lowered IR,
+ * that is, IR where control flow has been computed and high level to low level
+ * substitions for operations have been performed.
+ *
+ * After Attr, every symbol will have a conservative correct type.
+ *
+ * Any expression that requires temporary storage as part of computation will
+ * also be detected here and give a temporary symbol
+ *
+ * Types can be narrowed after Attr by Access Specialization in FinalizeTypes,
+ * but in general, this is where the main symbol type information is
+ * computed.
+ */
+
+final class Attr extends NodeOperatorVisitor {
+    /** Current compiler. */
+    private final Compiler compiler;
+
+    /** Current source. */
+    private final Source source;
+
+    /**
+     * Local definitions in current block (to discriminate from function
+     * declarations always defined in the function scope. This is for
+     * "can be undefined" analysis.
+     */
+    private Set<String> localDefs;
+
+    /**
+     * Local definitions in current block to guard against cases like
+     * NASHORN-467 when things can be undefined as they are used before
+     * their local var definition. *sigh* JavaScript...
+     */
+    private Set<String> localUses;
+
+    private static final DebugLogger LOG = new DebugLogger("attr");
+
+    /**
+     * Constructor.
+     *
+     * @param compiler the compiler
+     */
+    Attr(final Compiler compiler) {
+        this.compiler = compiler;
+        this.source   = compiler.getSource();
+    }
+
+    @Override
+    protected Node enterDefault(final Node node) {
+        return start(node);
+    }
+
+    @Override
+    protected Node leaveDefault(final Node node) {
+        return end(node);
+    }
+
+    @Override
+    public Node leave(final AccessNode accessNode) {
+        newTemporary(Type.OBJECT, accessNode);  //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
+        end(accessNode);
+        return accessNode;
+    }
+
+    @Override
+    public Node enter(final Block block) {
+        start(block);
+
+        final Set<String> savedLocalDefs = localDefs;
+        final Set<String> savedLocalUses = localUses;
+
+        block.setFrame(getCurrentFunctionNode().pushFrame());
+
+        try {
+            // a block starts out by copying the local defs and local uses
+            // from the outer level. But we need the copies, as when we
+            // leave the block the def and use sets given upon entry must
+            // be restored
+            localDefs = new HashSet<>(savedLocalDefs);
+            localUses = new HashSet<>(savedLocalUses);
+
+            for (final Node statement : block.getStatements()) {
+                statement.accept(this);
+            }
+        } finally {
+            localDefs = savedLocalDefs;
+            localUses = savedLocalUses;
+
+            getCurrentFunctionNode().popFrame();
+        }
+
+        end(block);
+
+        return null;
+    }
+
+    @Override
+    public Node enter(final CallNode callNode) {
+        start(callNode);
+
+        callNode.getFunction().accept(this);
+
+        final List<Node> acceptedArgs = new ArrayList<>(callNode.getArgs().size());
+        for (final Node arg : callNode.getArgs()) {
+            LOG.info("Doing call arg " + arg);
+            acceptedArgs.add(arg.accept(this));
+        }
+        callNode.setArgs(acceptedArgs);
+
+        final EvalArgs evalArgs = callNode.getEvalArgs();
+        if (evalArgs != null) {
+            evalArgs.setCode(evalArgs.getCode().accept(this));
+
+            final IdentNode thisNode = new IdentNode(getCurrentFunctionNode().getThisNode());
+            assert thisNode.getSymbol() != null; //should copy attributed symbol and that's it
+            evalArgs.setThis(thisNode);
+        }
+
+        newTemporary(Type.OBJECT, callNode); // object type here, access specialization in FinalizeTypes may narrow it later
+        newType(callNode.getFunction().getSymbol(), Type.OBJECT);
+
+        end(callNode);
+
+        return null;
+    }
+
+    @Override
+    public Node enter(final CatchNode catchNode) {
+        final IdentNode exception = catchNode.getException();
+        final Block     block     = getCurrentBlock();
+
+        start(catchNode);
+
+        // define block-local exception variable
+        final Symbol def = block.defineSymbol(exception.getName(), IS_VAR | IS_LET, exception);
+        newType(def, Type.OBJECT);
+        addLocalDef(exception.getName());
+
+        return catchNode;
+    }
+
+    @Override
+    public Node enter(final FunctionNode functionNode) {
+        start(functionNode, false);
+
+        clearLocalDefs();
+        clearLocalUses();
+
+        functionNode.setFrame(functionNode.pushFrame());
+
+        initThis(functionNode);
+        initCallee(functionNode);
+        if (functionNode.isVarArg()) {
+            initVarArg(functionNode);
+        }
+
+        initParameters(functionNode);
+        initScope(functionNode);
+        initReturn(functionNode);
+
+        // Add all nested functions as symbols in this function
+        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
+            final IdentNode ident = nestedFunction.getIdent();
+            if (ident != null && nestedFunction.isStatement()) {
+                final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction);
+                newType(functionSymbol, Type.typeFor(ScriptFunction.class));
+            }
+        }
+
+        if (functionNode.isScript()) {
+            initFromPropertyMap(compiler.getContext(), functionNode);
+        }
+
+        // Add function name as local symbol
+        if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) {
+            final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode);
+            newType(selfSymbol, Type.OBJECT);
+            selfSymbol.setNode(functionNode);
+        }
+
+        /*
+         * This pushes all declarations (except for non-statements, i.e. for
+         * node temporaries) to the top of the function scope. This way we can
+         * get around problems like
+         *
+         * while (true) {
+         *   break;
+         *   if (true) {
+         *     var s;
+         *   }
+         * }
+         *
+         * to an arbitrary nesting depth.
+         *
+         * @see NASHORN-73
+         */
+
+        final List<Symbol> declaredSymbols = new ArrayList<>();
+        for (final VarNode decl : functionNode.getDeclarations()) {
+            final IdentNode ident = decl.getName();
+            // any declared symbols that aren't visited need to be typed as well, hence the list
+            declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident)));
+        }
+
+        // Every nested function needs a definition in the outer function with its name. Add these.
+        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
+            final VarNode varNode = nestedFunction.getFunctionVarNode();
+            if (varNode != null) {
+                varNode.accept(this);
+                assert varNode.isFunctionVarNode() : varNode + " should be function var node";
+            }
+        }
+
+        for (final Node statement : functionNode.getStatements()) {
+            if (statement instanceof VarNode && ((VarNode)statement).isFunctionVarNode()) {
+                continue; //var nodes have already been processed, skip or they will generate additional defs/uses and false "can be undefined"
+            }
+            statement.accept(this);
+        }
+
+        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
+            LOG.info("Going into nested function " + functionNode.getName() + " -> " + nestedFunction.getName());
+            nestedFunction.accept(this);
+        }
+
+        //unknown parameters are promoted to object type.
+        finalizeParameters(functionNode);
+        finalizeTypes(functionNode);
+        for (final Symbol symbol : declaredSymbols) {
+            if (symbol.getSymbolType().isUnknown()) {
+                symbol.setType(Type.OBJECT);
+                symbol.setCanBeUndefined();
+            }
+        }
+
+        if (functionNode.getReturnType().isUnknown()) {
+            LOG.info("Unknown return type promoted to object");
+            functionNode.setReturnType(Type.OBJECT);
+        }
+
+        if (functionNode.getSelfSymbolInit() != null) {
+            LOG.info("Accepting self symbol init " + functionNode.getSelfSymbolInit() + " for " + functionNode.getName());
+            final Node init = functionNode.getSelfSymbolInit();
+            final List<Node> newStatements = new ArrayList<>();
+            newStatements.add(init);
+            newStatements.addAll(functionNode.getStatements());
+            functionNode.setStatements(newStatements);
+            functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this));
+        }
+
+        functionNode.popFrame();
+
+        end(functionNode, false);
+
+        return null;
+    }
+
+    @Override
+    public Node leaveCONVERT(final UnaryNode unaryNode) {
+        assert false : "There should be no convert operators in IR during Attribution";
+        end(unaryNode);
+        return unaryNode;
+    }
+
+    @Override
+    public Node enter(final IdentNode identNode) {
+        final String name = identNode.getName();
+
+        start(identNode);
+
+        if (identNode.isPropertyName()) {
+            // assign a pseudo symbol to property name
+            final Symbol pseudoSymbol = pseudoSymbol(name);
+            LOG.info("IdentNode is property name -> assigning pseudo symbol " + pseudoSymbol);
+            LOG.unindent();
+            identNode.setSymbol(pseudoSymbol);
+            return null;
+        }
+
+        final Block  block     = getCurrentBlock();
+        final Symbol oldSymbol = identNode.getSymbol();
+
+        Symbol symbol = block.findSymbol(name);
+
+        //If an existing symbol with the name is found, use that otherwise, declare a new one
+        if (symbol != null) {
+            LOG.info("Existing symbol = " + symbol);
+            if (isFunctionExpressionSelfReference(symbol)) {
+                final FunctionNode functionNode = (FunctionNode)symbol.getNode();
+                assert functionNode.getCalleeNode() != null;
+
+                final VarNode var = new VarNode(source, functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
+                //newTemporary(Type.OBJECT, var); //ScriptFunction? TODO
+
+                functionNode.setNeedsSelfSymbol(var);
+            }
+
+            if (!identNode.isInitializedHere()) { // NASHORN-448
+                // here is a use outside the local def scope
+                if (!isLocalDef(name)) {
+                    newType(symbol, Type.OBJECT);
+                    symbol.setCanBeUndefined();
+                }
+            }
+
+            identNode.setSymbol(symbol);
+            if (!getCurrentFunctionNode().isLocal(symbol)) {
+                // non-local: we need to put symbol in scope (if it isn't already)
+                if (!symbol.isScope()) {
+                    final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction());
+                    for (final Block lookupBlock : lookupBlocks) {
+                        final Symbol refSymbol = lookupBlock.findSymbol(name);
+                        if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f()
+                            LOG.finest("Found a ref symbol that must be scope " + refSymbol);
+                            refSymbol.setIsScope();
+                        }
+                    }
+                }
+            }
+        } else {
+            LOG.info("No symbol exists. Declare undefined: " + symbol);
+            symbol = block.useSymbol(name, identNode);
+            // we have never seen this before, it can be undefined
+            newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
+            symbol.setCanBeUndefined();
+        }
+
+        if (symbol != oldSymbol && !identNode.isInitializedHere()) {
+            symbol.increaseUseCount();
+        }
+        addLocalUse(identNode.getName());
+
+        end(identNode);
+
+        return null;
+    }
+
+    @Override
+    public Node leave(final IndexNode indexNode) {
+        newTemporary(Type.OBJECT, indexNode); //TORO
+        return indexNode;
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    public Node enter(final LiteralNode literalNode) {
+        try {
+            start(literalNode);
+            assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
+
+            if (literalNode instanceof ArrayLiteralNode) {
+                final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
+                final Node[]           array            = arrayLiteralNode.getValue();
+
+                for (int i = 0; i < array.length; i++) {
+                    final Node element = array[i];
+                    if (element != null) {
+                        array[i] = element.accept(this);
+                    }
+                }
+                arrayLiteralNode.analyze();
+                //array literal node now has an element type and all elements are attributed
+            } else {
+                assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
+            }
+
+            getCurrentFunctionNode().newLiteral(literalNode);
+        } finally {
+            end(literalNode);
+        }
+        return null;
+    }
+
+    @Override
+    public Node leave(final ObjectNode objectNode) {
+        newTemporary(Type.OBJECT, objectNode);
+        end(objectNode);
+        return objectNode;
+    }
+
+    @Override
+    public Node enter(final PropertyNode propertyNode) {
+        // assign a pseudo symbol to property name, see NASHORN-710
+        propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
+        end(propertyNode);
+        return propertyNode;
+    }
+
+    @Override
+    public Node enter(final ReferenceNode referenceNode) {
+        final FunctionNode functionNode = referenceNode.getReference();
+        if (functionNode != null) {
+            functionNode.addReferencingParentBlock(getCurrentBlock());
+        }
+        end(referenceNode);
+
+        return referenceNode;
+    }
+
+    @Override
+    public Node leave(final ReferenceNode referenceNode) {
+        newTemporary(Type.OBJECT, referenceNode); //reference node type is always an object, i.e. the scriptFunction. the function return type varies though
+        end(referenceNode);
+        return referenceNode;
+    }
+
+    @Override
+    public Node leave(final ReturnNode returnNode) {
+        final Node expr = returnNode.getExpression();
+
+        if (expr != null) {
+            //we can't do parameter specialization if we return something that hasn't been typed yet
+            final Symbol symbol = expr.getSymbol();
+            if (expr.getType().isUnknown() && symbol.isParam()) {
+                symbol.setType(Type.OBJECT);
+            }
+            getCurrentFunctionNode().setReturnType(Type.widest(getCurrentFunctionNode().getReturnType(), symbol.getSymbolType()));
+            LOG.info("Returntype is now " + getCurrentFunctionNode().getReturnType());
+        }
+
+        end(returnNode);
+
+        return returnNode;
+    }
+
+    @Override
+    public Node leave(final SwitchNode switchNode) {
+        Type type = Type.UNKNOWN;
+
+        for (final CaseNode caseNode : switchNode.getCases()) {
+            final Node test = caseNode.getTest();
+            if (test != null) {
+                if (test instanceof LiteralNode) {
+                    //go down to integers if we can
+                    final LiteralNode<?> lit = (LiteralNode<?>)test;
+                    if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) {
+                        if (JSType.isRepresentableAsInt(lit.getNumber())) {
+                            caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
+                        }
+                    }
+                }
+
+                type = Type.widest(type, caseNode.getTest().getType());
+            }
+        }
+
+        //only optimize for all integers
+        if (!type.isInteger()) {
+            type = Type.OBJECT;
+        }
+
+        switchNode.setTag(newInternal(compiler.uniqueName(SWITCH_TAG_PREFIX.tag()), type));
+
+        end(switchNode);
+
+        return switchNode;
+    }
+
+    @Override
+    public Node leave(final TryNode tryNode) {
+        tryNode.setException(exceptionSymbol());
+
+        if (tryNode.getFinallyBody() != null) {
+            tryNode.setFinallyCatchAll(exceptionSymbol());
+        }
+
+        end(tryNode);
+
+        return tryNode;
+    }
+
+    @Override
+    public Node enter(final VarNode varNode) {
+        start(varNode);
+
+        final IdentNode ident = varNode.getName();
+        final String    name  = ident.getName();
+
+        final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident);
+        assert symbol != null;
+
+        LOG.info("VarNode " + varNode + " set symbol " + symbol);
+        varNode.setSymbol(symbol);
+
+        // NASHORN-467 - use before definition of vars - conservative
+        if (localUses.contains(ident.getName())) {
+            newType(symbol, Type.OBJECT);
+            symbol.setCanBeUndefined();
+        }
+
+        if (varNode.getInit() != null) {
+            varNode.getInit().accept(this);
+        }
+
+        return varNode;
+    }
+
+    @Override
+    public Node leave(final VarNode varNode) {
+        final Node      init  = varNode.getInit();
+        final IdentNode ident = varNode.getName();
+        final String    name  = ident.getName();
+
+        if (init != null) {
+            addLocalDef(name);
+        }
+
+        if (init == null) {
+            // var x; with no init will be treated like a use of x by
+            // visit(IdentNode) unless we remove the name
+            // from the localdef list.
+            removeLocalDef(name);
+            return varNode;
+        }
+
+        final Symbol  symbol   = varNode.getSymbol();
+        final boolean isScript = symbol.getBlock().getFunction().isScript(); //see NASHORN-56
+        if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
+            // Forbid integers as local vars for now as we have no way to treat them as undefined
+            newType(symbol, init.getType());
+        } else {
+            newType(symbol, Type.OBJECT);
+        }
+
+        assert varNode.hasType() : varNode;
+
+        end(varNode);
+
+        return varNode;
+    }
+
+    @Override
+    public Node leaveADD(final UnaryNode unaryNode) {
+        newTemporary(arithType(), unaryNode);
+        end(unaryNode);
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveBIT_NOT(final UnaryNode unaryNode) {
+        newTemporary(Type.INT, unaryNode);
+        end(unaryNode);
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveDECINC(final UnaryNode unaryNode) {
+        // @see assignOffset
+        ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs());
+        final Type type = arithType();
+        newType(unaryNode.rhs().getSymbol(), type);
+        newTemporary(type, unaryNode);
+        end(unaryNode);
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveDELETE(final UnaryNode unaryNode) {
+        final FunctionNode   currentFunctionNode = getCurrentFunctionNode();
+        final boolean        strictMode          = currentFunctionNode.isStrictMode();
+        final Node           rhs                 = unaryNode.rhs();
+        final Node           strictFlagNode      = LiteralNode.newInstance(unaryNode, strictMode).accept(this);
+
+        Request request = Request.DELETE;
+        final RuntimeNode runtimeNode;
+        final List<Node> args = new ArrayList<>();
+
+        if (rhs instanceof IdentNode) {
+            // If this is a declared variable or a function parameter, delete always fails (except for globals).
+            final String name = ((IdentNode)rhs).getName();
+
+            final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !rhs.getSymbol().isTopLevel());
+
+            if (failDelete && rhs.getSymbol().isThis()) {
+                return LiteralNode.newInstance(unaryNode, true).accept(this);
+            }
+            final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this);
+
+            args.add(currentFunctionNode.getScopeNode());
+            args.add(literalNode);
+            args.add(strictFlagNode);
+
+            if (failDelete) {
+                request = Request.FAIL_DELETE;
+            }
+        } else if (rhs instanceof AccessNode) {
+            final Node      base     = ((AccessNode)rhs).getBase();
+            final IdentNode property = ((AccessNode)rhs).getProperty();
+
+            args.add(base);
+            args.add(LiteralNode.newInstance(unaryNode, property.getName()).accept(this));
+            args.add(strictFlagNode);
+
+        } else if (rhs instanceof IndexNode) {
+            final Node base  = ((IndexNode)rhs).getBase();
+            final Node index = ((IndexNode)rhs).getIndex();
+
+            args.add(base);
+            args.add(index);
+            args.add(strictFlagNode);
+
+        } else {
+            return LiteralNode.newInstance(unaryNode, true).accept(this);
+        }
+
+        runtimeNode = new RuntimeNode(unaryNode, request, args);
+        assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this
+
+        runtimeNode.accept(this);
+        return runtimeNode;
+    }
+
+
+    @Override
+    public Node leaveNEW(final UnaryNode unaryNode) {
+        newTemporary(Type.OBJECT, unaryNode);
+        end(unaryNode);
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveNOT(final UnaryNode unaryNode) {
+        newTemporary(Type.BOOLEAN, unaryNode);
+        end(unaryNode);
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveTYPEOF(final UnaryNode unaryNode) {
+        final Node rhs    = unaryNode.rhs();
+
+        RuntimeNode runtimeNode;
+
+        List<Node> args = new ArrayList<>();
+        if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
+            args.add(getCurrentFunctionNode().getScopeNode());
+            args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
+        } else {
+            args.add(rhs);
+            args.add(LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
+        }
+
+        runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
+        assert runtimeNode.getSymbol() == unaryNode.getSymbol();
+
+        runtimeNode.accept(this);
+
+        end(unaryNode);
+
+        return runtimeNode;
+    }
+
+    @Override
+    public Node leave(final RuntimeNode runtimeNode) {
+        newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
+        return runtimeNode;
+    }
+
+    @Override
+    public Node leaveSUB(final UnaryNode unaryNode) {
+        newTemporary(arithType(), unaryNode);
+        end(unaryNode);
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveVOID(final UnaryNode unaryNode) {
+        final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.VOID);
+        runtimeNode.accept(this);
+        assert runtimeNode.getSymbol().getSymbolType().isObject();
+        end(unaryNode);
+        return runtimeNode;
+    }
+
+    /**
+     * Add is a special binary, as it works not only on arithmetic, but for
+     * strings etc as well.
+     */
+    @Override
+    public Node leaveADD(final BinaryNode binaryNode) {
+        final Node lhs = binaryNode.lhs();
+        final Node rhs = binaryNode.rhs();
+
+        ensureTypeNotUnknown(lhs);
+        ensureTypeNotUnknown(rhs);
+        newTemporary(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
+
+        end(binaryNode);
+
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveAND(final BinaryNode binaryNode) {
+        newTemporary(Type.OBJECT, binaryNode);
+        end(binaryNode);
+        return binaryNode;
+    }
+
+    /**
+     * This is a helper called before an assignment.
+     * @param binaryNode assignment node
+     */
+    private Node enterAssignmentNode(final BinaryNode binaryNode) {
+        start(binaryNode);
+
+        final Node lhs = binaryNode.lhs();
+
+        if (lhs instanceof IdentNode) {
+            final Block     block = getCurrentBlock();
+            final IdentNode ident = (IdentNode)lhs;
+            final String    name  = ident.getName();
+
+            Symbol symbol = getCurrentBlock().findSymbol(name);
+
+            if (symbol == null) {
+                symbol = block.defineSymbol(name, IS_GLOBAL, ident);
+                binaryNode.setSymbol(symbol);
+            } else if (!getCurrentFunctionNode().isLocal(symbol)) {
+                symbol.setIsScope();
+            }
+
+            addLocalDef(name);
+        }
+
+        return binaryNode;
+    }
+
+    @Override
+    public Node enterASSIGN(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN(final BinaryNode binaryNode) {
+        return leaveAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
+        final Node lhs = binaryNode.lhs();
+        final Node rhs = binaryNode.rhs();
+
+        final Type widest = Type.widest(lhs.getType(), rhs.getType());
+        //Type.NUMBER if we can't prove that the add doesn't overflow. todo
+        return leaveSelfModifyingAssignmentNode(binaryNode, widest.isNumeric() ? Type.NUMBER : Type.OBJECT);
+    }
+
+    @Override
+    public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+        return enterAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode);
+    }
+
+    @Override
+    public Node leaveBIT_AND(final BinaryNode binaryNode) {
+        newTemporary(Type.INT, binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveBIT_OR(final BinaryNode binaryNode) {
+        newTemporary(Type.INT, binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveBIT_XOR(final BinaryNode binaryNode) {
+        newTemporary(Type.INT, binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
+        newTemporary(binaryNode.rhs().getType(), binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
+        newTemporary(binaryNode.lhs().getType(), binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveDIV(final BinaryNode binaryNode) {
+        return leaveBinaryArithmetic(binaryNode);
+    }
+
+    private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) {
+        final Node lhs = binaryNode.lhs();
+        final Node rhs = binaryNode.rhs();
+
+        newTemporary(Type.BOOLEAN, binaryNode);
+        ensureTypeNotUnknown(lhs);
+        ensureTypeNotUnknown(rhs);
+
+        end(binaryNode);
+        return binaryNode;
+    }
+
+    //leave a binary node and inherit the widest type of lhs , rhs
+    private Node leaveBinaryArithmetic(final BinaryNode binaryNode) {
+        if (!Compiler.shouldUseIntegerArithmetic()) {
+            newTemporary(Type.NUMBER, binaryNode);
+            return binaryNode;
+        }
+        newTemporary(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType(), Type.NUMBER), binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveEQ(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.EQ);
+    }
+
+    @Override
+    public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.EQ_STRICT);
+    }
+
+    @Override
+    public Node leaveGE(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.GE);
+    }
+
+    @Override
+    public Node leaveGT(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.GT);
+    }
+
+    @Override
+    public Node leaveIN(final BinaryNode binaryNode) {
+        try {
+            return new RuntimeNode(binaryNode, Request.IN).accept(this);
+        } finally {
+            end(binaryNode);
+        }
+    }
+
+    @Override
+    public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
+        try {
+            return new RuntimeNode(binaryNode, Request.INSTANCEOF).accept(this);
+        } finally {
+            end(binaryNode);
+        }
+    }
+
+    @Override
+    public Node leaveLE(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.LE);
+    }
+
+    @Override
+    public Node leaveLT(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.LT);
+    }
+
+    @Override
+    public Node leaveMOD(final BinaryNode binaryNode) {
+        return leaveBinaryArithmetic(binaryNode);
+    }
+
+    @Override
+    public Node leaveMUL(final BinaryNode binaryNode) {
+        return leaveBinaryArithmetic(binaryNode);
+    }
+
+    @Override
+    public Node leaveNE(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.NE);
+    }
+
+    @Override
+    public Node leaveNE_STRICT(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.NE_STRICT);
+    }
+
+    @Override
+    public Node leaveOR(final BinaryNode binaryNode) {
+        newTemporary(Type.OBJECT, binaryNode);
+        end(binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveSAR(final BinaryNode binaryNode) {
+        newTemporary(Type.INT, binaryNode);
+        end(binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveSHL(final BinaryNode binaryNode) {
+        newTemporary(Type.INT, binaryNode);
+        end(binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveSHR(final BinaryNode binaryNode) {
+        newTemporary(Type.LONG, binaryNode);
+        end(binaryNode);
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveSUB(final BinaryNode binaryNode) {
+        return leaveBinaryArithmetic(binaryNode);
+    }
+
+    @Override
+    public Node leave(final ForNode forNode) {
+        if (forNode.isForIn()) {
+            forNode.setIterator(newInternal(getCurrentFunctionNode(), compiler.uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
+            /*
+             * Iterators return objects, so we need to widen the scope of the
+             * init variable if it, for example, has been assigned double type
+             * see NASHORN-50
+             */
+            newType(forNode.getInit().getSymbol(), Type.OBJECT);
+        }
+
+        end(forNode);
+
+        return forNode;
+    }
+
+    @Override
+    public Node leave(final TernaryNode ternaryNode) {
+        final Node lhs  = ternaryNode.rhs();
+        final Node rhs  = ternaryNode.third();
+
+        ensureTypeNotUnknown(lhs);
+        ensureTypeNotUnknown(rhs);
+
+        final Type type = Type.widest(lhs.getType(), rhs.getType());
+        newTemporary(type, ternaryNode);
+
+        end(ternaryNode);
+
+        return ternaryNode;
+    }
+
+    private static void initThis(final FunctionNode functionNode) {
+        final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null);
+        newType(thisSymbol, Type.OBJECT);
+        thisSymbol.setNeedsSlot(true);
+        functionNode.getThisNode().setSymbol(thisSymbol);
+        LOG.info("Initialized scope symbol: " + thisSymbol);
+    }
+
+    private static void initScope(final FunctionNode functionNode) {
+        final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
+        newType(scopeSymbol, Type.typeFor(ScriptObject.class));
+        scopeSymbol.setNeedsSlot(true);
+        functionNode.getScopeNode().setSymbol(scopeSymbol);
+        LOG.info("Initialized scope symbol: " + scopeSymbol);
+    }
+
+    private static void initReturn(final FunctionNode functionNode) {
+        final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
+        newType(returnSymbol, Type.OBJECT);
+        returnSymbol.setNeedsSlot(true);
+        functionNode.getResultNode().setSymbol(returnSymbol);
+        LOG.info("Initialized return symbol: " + returnSymbol);
+        //return symbol is always object as it's the __return__ thing. What returnType is is another matter though
+    }
+
+    private static void initVarArg(final FunctionNode functionNode) {
+        assert functionNode.getCalleeNode() != null;
+
+        final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
+        newType(varArgsSymbol, Type.OBJECT_ARRAY);
+        varArgsSymbol.setNeedsSlot(true);
+        functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
+        LOG.info("Initialized varargs symbol: " + varArgsSymbol);
+
+        final String    argumentsName   = functionNode.getArgumentsNode().getName();
+        final Symbol    argumentsSymbol = functionNode.defineSymbol(argumentsName, IS_PARAM | IS_INTERNAL, null);
+        newType(argumentsSymbol, Type.OBJECT);
+        argumentsSymbol.setNeedsSlot(true);
+        functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
+        LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol);
+    }
+
+    private static void initCallee(final FunctionNode functionNode) {
+        assert functionNode.getCalleeNode() != null : functionNode + " has no callee";
+        final Symbol calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
+        newType(calleeSymbol, Type.typeFor(ScriptFunction.class));
+        calleeSymbol.setNeedsSlot(true);
+        functionNode.getCalleeNode().setSymbol(calleeSymbol);
+        LOG.info("Initialized callee symbol " + calleeSymbol);
+    }
+
+    /**
+     * Initialize parameters for function node. This may require specializing
+     * types if a specialization profile is known
+     *
+     * @param functionNode the function node
+     */
+    private void initParameters(final FunctionNode functionNode) {
+        //If a function is specialized, we don't need to tag either it return
+        // type or its parameters with the widest (OBJECT) type for safety.
+        functionNode.setReturnType(Type.UNKNOWN);
+
+        for (final IdentNode ident : functionNode.getParameters()) {
+            addLocalDef(ident.getName());
+            final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident);
+            if (paramSymbol != null) {
+                newType(paramSymbol, Type.UNKNOWN);
+            }
+
+            LOG.info("Initialized param " + paramSymbol);
+        }
+    }
+
+    /**
+     * This has to run before fix assignment types, store any type specializations for
+     * paramters, then turn then to objects for the generic version of this method
+     *
+     * @param functionNode functionNode
+     */
+    private static void finalizeParameters(final FunctionNode functionNode) {
+        boolean nonObjectParams = false;
+        List<Type> paramSpecializations = new ArrayList<>();
+
+        for (final IdentNode ident : functionNode.getParameters()) {
+            final Symbol paramSymbol = ident.getSymbol();
+            if (paramSymbol != null) {
+                Type type = paramSymbol.getSymbolType();
+                if (type.isUnknown()) {
+                    type = Type.OBJECT;
+                }
+                paramSpecializations.add(type);
+                if (!type.isObject()) {
+                    nonObjectParams = true;
+                }
+                newType(paramSymbol, Type.OBJECT);
+            }
+        }
+
+        if (!nonObjectParams) {
+            paramSpecializations = null;
+            // Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters
+            // here. If the callee has parameter specializations, we can regenerate it with those particular types for speed.
+        } else {
+            LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations);
+        }
+
+        // parameters should not be slots for a vararg function, make sure this is the case
+        if (functionNode.isVarArg()) {
+            for (final IdentNode param : functionNode.getParameters()) {
+                param.getSymbol().setNeedsSlot(false);
+            }
+        }
+    }
+
+    /**
+     * Move any properties from a global map into the scope of this method
+     * @param context      context
+     * @param functionNode the function node for which to init scope vars
+     */
+    private static void initFromPropertyMap(final Context context, final FunctionNode functionNode) {
+        // For a script, add scope symbols as defined in the property map
+        assert functionNode.isScript();
+
+        final PropertyMap map = Context.getGlobalMap();
+
+        for (final Property property : map.getProperties()) {
+            final String key    = property.getKey();
+            final Symbol symbol = functionNode.defineSymbol(key, IS_GLOBAL, null);
+            newType(symbol, Type.OBJECT);
+            LOG.info("Added global symbol from property map " + symbol);
+        }
+    }
+
+    private static void ensureTypeNotUnknown(final Node node) {
+
+        final Symbol symbol = node.getSymbol();
+
+        LOG.info("Ensure type not unknown for: " + symbol);
+
+        /*
+         * Note that not just unknowns, but params need to be blown
+         * up to objects, because we can have something like
+         *
+         * function f(a) {
+         *    var b = ~a; //b and a are inferred to be int
+         *    return b;
+         * }
+         *
+         * In this case, it would be correct to say that "if you have
+         * an int at the callsite, just pass it".
+         *
+         * However
+         *
+         * function f(a) {
+         *    var b = ~a;      //b and a are inferred to be int
+         *    return b == 17;  //b is still inferred to be int.
+         * }
+         *
+         * can be called with f("17") and if we assume that b is an
+         * int and don't blow it up to an object in the comparison, we
+         * are screwed. I hate JavaScript.
+         *
+         * This check has to be done for any operation that might take
+         * objects as parameters, for example +, but not *, which is known
+         * to coerce types into doubles
+         */
+        if (node.getType().isUnknown() || symbol.isParam()) {
+            newType(symbol, Type.OBJECT);
+            symbol.setCanBeUndefined();
+         }
+    }
+
+    private static Symbol pseudoSymbol(final String name) {
+        return new Symbol(name, 0, Type.OBJECT);
+    }
+
+    private Symbol exceptionSymbol() {
+        return newInternal(compiler.uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
+    }
+
+    /**
+     * In an assignment, recursively make sure that there are slots for
+     * everything that has to be laid out as temporary storage, which is the
+     * case if we are assign-op:ing a BaseNode subclass. This has to be
+     * recursive to handle things like multi dimensional arrays as lhs
+     *
+     * see NASHORN-258
+     *
+     * @param functionNode   the current function node (has to be passed as it changes in the visitor below)
+     * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes
+     */
+    private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) {
+        assignmentDest.accept(new NodeVisitor() {
+            @Override
+            public Node leave(final IndexNode indexNode) {
+                final Node index = indexNode.getIndex();
+                index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant());
+                return indexNode;
+            }
+        });
+    }
+
+    /**
+     * Return the type that arithmetic ops should use. Until we have implemented better type
+     * analysis (range based) or overflow checks that are fast enough for int arithmetic,
+     * this is the number type
+     * @return the arithetic type
+     */
+    private static Type arithType() {
+        return Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER;
+    }
+
+    /**
+     * If types have changed, we can have failed to update vars. For example
+     *
+     * var x = 17; //x is int
+     * x = "apa";  //x is object. This will be converted fine
+     *
+     * @param functionNode
+     */
+    private static void finalizeTypes(final FunctionNode functionNode) {
+        final Set<Node> changed = new HashSet<>();
+        do {
+            changed.clear();
+            functionNode.accept(new NodeVisitor() {
+
+                private void widen(final Node node, final Type to) {
+                    if (node instanceof LiteralNode) {
+                        return;
+                    }
+                    Type from = node.getType();
+                    if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
+                        LOG.fine("Had to post pass widen '" + node + "' " + Debug.id(node) + " from " + node.getType() + " to " + to);
+                        newType(node.getSymbol(), to);
+                        changed.add(node);
+                    }
+                }
+
+                /**
+                 * Eg.
+                 *
+                 * var d = 17;
+                 * var e;
+                 * e = d; //initially typed as int for node type, should retype as double
+                 * e = object;
+                 *
+                 * var d = 17;
+                 * var e;
+                 * e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric
+                 * e = object;
+                 *
+                 */
+                @SuppressWarnings("fallthrough")
+                @Override
+                public Node leave(final BinaryNode binaryNode) {
+                    final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+                    switch (binaryNode.tokenType()) {
+                    default:
+                        if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
+                            break;
+                        }
+                        widen(binaryNode.lhs(), widest);
+                    case ADD:
+                        widen(binaryNode, widest);
+                        break;
+                    }
+                    return binaryNode;
+                }
+            });
+        } while (!changed.isEmpty());
+    }
+
+    /**
+     * This assign helper is called after an assignment, when all children of
+     * the assign has been processed. It fixes the types and recursively makes
+     * sure that everyhing has slots that should have them in the chain.
+     *
+     * @param binaryNode assignment node
+     */
+    private Node leaveAssignmentNode(final BinaryNode binaryNode) {
+        final Node lhs = binaryNode.lhs();
+        final Node rhs = binaryNode.rhs();
+
+        final Type type;
+        if (rhs.getType().isNumeric()) {
+            type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+        } else {
+            type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
+        }
+        newTemporary(type, binaryNode);
+        newType(lhs.getSymbol(), type);
+        end(binaryNode);
+        return binaryNode;
+    }
+
+    private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) {
+        return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType());
+    }
+
+    private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type destType) {
+        //e.g. for -=, Number, no wider, destType (binaryNode.getWidestOperationType())  is the coerce type
+        final Node lhs = binaryNode.lhs();
+
+        newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
+        newTemporary(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
+
+        ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode);
+
+        end(binaryNode);
+        return binaryNode;
+    }
+
+    private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) {
+        if (currentFunction.findParentFunction() == topFunction) {
+            final List<Block> blocks = new LinkedList<>();
+
+            blocks.add(currentFunction.getParent());
+            blocks.addAll(currentFunction.getReferencingParentBlocks());
+            return blocks;
+        }
+        /*
+         * assumption: all parent blocks of an inner function will always be in the same outer function;
+         * therefore we can simply skip through intermediate functions.
+         * @see FunctionNode#addReferencingParentBlock(Block)
+         */
+        return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction);
+    }
+
+    private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
+        if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
+            return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName());
+        }
+        return false;
+    }
+
+    private static Symbol newTemporary(final FunctionNode functionNode, final Type type, final Node node) {
+        LOG.info("New TEMPORARY added to " + functionNode.getName() + " type=" + type);
+        return functionNode.newTemporary(type, node);
+    }
+
+    private Symbol newTemporary(final Type type, final Node node) {
+        return newTemporary(getCurrentFunctionNode(), type, node);
+    }
+
+    private Symbol newInternal(final FunctionNode functionNode, final String name, final Type type) {
+        final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
+        iter.setType(type); // NASHORN-73
+        return iter;
+    }
+
+    private Symbol newInternal(final String name, final Type type) {
+        return newInternal(getCurrentFunctionNode(), name, type);
+    }
+
+    private static void newType(final Symbol symbol, final Type type) {
+        final Type oldType = symbol.getSymbolType();
+        symbol.setType(type);
+
+        if (symbol.getSymbolType() != oldType) {
+            LOG.info("New TYPE " + type + " for " + symbol + " (was " + oldType + ")");
+        }
+
+        if (symbol.isParam()) {
+            symbol.setType(type);
+            LOG.info("Param type change " + symbol);
+        }
+    }
+
+    private void clearLocalDefs() {
+        localDefs = new HashSet<>();
+    }
+
+    private boolean isLocalDef(final String name) {
+        return localDefs.contains(name);
+    }
+
+    private void addLocalDef(final String name) {
+        LOG.info("Adding local def of symbol: '" + name + "'");
+        localDefs.add(name);
+    }
+
+    private void removeLocalDef(final String name) {
+        LOG.info("Removing local def of symbol: '" + name + "'");
+        localDefs.remove(name);
+    }
+
+    private void clearLocalUses() {
+        localUses = new HashSet<>();
+    }
+
+    private void addLocalUse(final String name) {
+        LOG.info("Adding local use of symbol: '" + name + "'");
+        localUses.add(name);
+    }
+
+    private static String name(final Node node) {
+        final String cn = node.getClass().getName();
+        int lastDot = cn.lastIndexOf('.');
+        if (lastDot == -1) {
+            return cn;
+        }
+        return cn.substring(lastDot + 1);
+    }
+
+    private Node start(final Node node) {
+        return start(node, true);
+    }
+
+    private Node start(final Node node, final boolean printNode) {
+        final StringBuilder sb = new StringBuilder();
+
+        sb.append("[ENTER ").
+            append(name(node)).
+            append("] ").
+            append(printNode ? node.toString() : "").
+            append(" in '").
+            append(getCurrentFunctionNode().getName()).
+            append("'");
+        LOG.info(sb.toString());
+        LOG.indent();
+
+        return node;
+    }
+
+    private Node end(final Node node) {
+        return end(node, true);
+    }
+
+    private Node end(final Node node, final boolean printNode) {
+        final StringBuilder sb = new StringBuilder();
+
+        sb.append("[LEAVE ").
+            append(name(node)).
+            append("] ").
+            append(printNode ? node.toString() : "").
+            append(" in '").
+            append(getCurrentFunctionNode().getName());
+
+        if (node.getSymbol() == null) {
+            sb.append(" <NO SYMBOL>");
+        } else {
+            sb.append(" <symbol=").append(node.getSymbol()).append('>');
+        }
+
+        LOG.unindent();
+        LOG.info(sb.toString());
+
+        return node;
+    }
+}
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Wed Jan 30 12:26:45 2013 +0100
@@ -56,7 +56,6 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.TreeMap;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
@@ -524,28 +523,6 @@
         return method;
     }
 
-    /**
-     * Create a new function object, including generating its stub code.
-     *
-     * @param functionNode  FunctionNode to utilize.
-     */
-    private void newFunctionObject(final FunctionNode functionNode) {
-         // Turn thisProperties into keys and symbols for the FunctionAnalyzer
-        final Map<String, Node> thisProperties = functionNode.getThisProperties();
-
-        final List<String> keys    = new ArrayList<>();
-        final List<Symbol> symbols = new ArrayList<>();
-
-        /* TODO - Predefine known properties.
-        for (final Entry<String, Node> entry : thisProperties.entrySet()) {
-            keys.add(entry.getKey());
-            symbols.add(entry.getValue().getSymbol());
-        }
-        */
-
-        new FunctionObjectCreator(this, functionNode, keys, symbols).makeObject(method);
-    }
-
     @Override
     public Node enter(final CallNode callNode) {
         if (callNode.testResolved()) {
@@ -603,12 +580,12 @@
 
                 final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
                 // load evaluated code
-                load(evalArgs.code);
+                load(evalArgs.getCode());
                 method.convert(Type.OBJECT);
                 // special/extra 'eval' arguments
-                load(evalArgs.evalThis);
-                method.load(evalArgs.location);
-                method.load(evalArgs.strictMode);
+                load(evalArgs.getThis());
+                method.load(evalArgs.getLocation());
+                method.load(evalArgs.getStrictMode());
                 method.convert(Type.OBJECT);
 
                 // direct call to Global.directEval
@@ -683,7 +660,7 @@
                 }
 
                 if (callee.needsCallee()) { // TODO: always true
-                    newFunctionObject(callee); // TODO: if callee not needed, function object is used only to pass scope (could be optimized). if neither the scope nor the function object is needed by the callee, we can pass null instead.
+                    new FunctionObjectCreator(CodeGenerator.this, callee).makeObject(method); // TODO: if callee not needed, function object is used only to pass scope (could be optimized). if neither the scope nor the function object is needed by the callee, we can pass null instead.
                 }
 
                 loadArgs(args, signature, isVarArg, argCount);
@@ -1300,6 +1277,7 @@
     @SuppressWarnings("rawtypes")
     @Override
     public Node enter(final LiteralNode literalNode) {
+        assert literalNode.getSymbol() != null : literalNode + " has no symbol";
         load(literalNode).store(literalNode.getSymbol());
         return null;
     }
@@ -1410,7 +1388,7 @@
             return null;
         }
 
-        newFunctionObject(referenceNode.getReference());
+        new FunctionObjectCreator(this, referenceNode.getReference()).makeObject(method);
 
         return null;
     }
@@ -1561,15 +1539,12 @@
         /*
          * First check if this should be something other than a runtime node
          * AccessSpecializer might have changed the type
+         *
+         * TODO - remove this - Access Specializer will always know after Attr/Lower
          */
-        if (runtimeNode.isPrimitive()) {
-
+        if (runtimeNode.isPrimitive() && !runtimeNode.isFinal()) {
             final Node lhs = runtimeNode.getArgs().get(0);
-            Node rhs = null;
-
-            if (runtimeNode.getArgs().size() > 1) {
-                rhs = runtimeNode.getArgs().get(1);
-            }
+            final Node rhs = runtimeNode.getArgs().size() > 1 ? runtimeNode.getArgs().get(1) : null;
 
             final Type   type   = runtimeNode.getType();
             final Symbol symbol = runtimeNode.getSymbol();
@@ -1590,7 +1565,15 @@
             case GT:
                 return enterCmp(lhs, rhs, Condition.GT, type, symbol);
             case ADD:
-                return enterNumericAdd(lhs, rhs, type, symbol);
+                Type widest = Type.widest(lhs.getType(), rhs.getType());
+                load(lhs);
+                method.convert(widest);
+                load(rhs);
+                method.convert(widest);
+                method.add();
+                method.convert(type);
+                method.store(symbol);
+                return null;
             default:
                 // it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar
                 // assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state";
@@ -1605,7 +1588,7 @@
             return null;
         }
 
-        if (specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
+        if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
             return null;
         }
 
@@ -2029,7 +2012,6 @@
         if (needsScope) {
             method.loadScope();
         }
-
         load(init);
 
         if (needsScope) {
@@ -2324,12 +2306,11 @@
     }
 
     private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) {
-        assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type);
+        assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type) : lhs.getType() + " != " + rhs.getType() + " != " + type + " " + new ASTWriter(lhs) + " " + new ASTWriter(rhs);
         load(lhs);
         load(rhs);
         method.add();
         method.store(symbol);
-
         return null;
     }
 
@@ -3152,17 +3133,19 @@
             /**
              * Take the original target args from the stack and use them
              * together with the value to be stored to emit the store code
+             *
+             * The case that targetSymbol is in scope (!hasSlot) and we actually
+             * need to do a conversion on non-equivalent types exists, but is
+             * very rare. See for example test/script/basic/access-specializer.js
              */
-            if (targetSymbol.hasSlot()) {
-                method.convert(target.getType());
-            }
+            method.convert(target.getType());
 
             target.accept(new NodeVisitor(compileUnit, method) {
                 @Override
                 public Node enter(final IdentNode node) {
                     final Symbol symbol = target.getSymbol();
                     if (symbol.isScope()) {
-                        if(symbol.isFastScope(currentFunction)) {
+                        if (symbol.isFastScope(currentFunction)) {
                             storeFastScopeVar(target.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
                         } else {
                             method.dynamicSet(target.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
--- a/src/jdk/nashorn/internal/codegen/Compiler.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Wed Jan 30 12:26:45 2013 +0100
@@ -70,8 +70,16 @@
         INITIALIZED,
         /** method has been parsed */
         PARSED,
+        /** constant folding pass */
+        CONSTANT_FOLDED,
         /** method has been lowered */
         LOWERED,
+        /** method hass been attributed */
+        ATTR,
+        /** method has been split */
+        SPLIT,
+        /** method has had its types finalized */
+        FINALIZED,
         /** method has been emitted to bytecode */
         EMITTED
     }
@@ -136,25 +144,6 @@
     private static final boolean LAZY_JIT = false;
 
     /**
-     * Should we use integers for literals and all operations
-     * that are based in constant and parameter assignment as
-     * long as they can be proven not to overflow? With this enabled
-     * var x = 17 would tag x as an integer, rather than a double,
-     * but as soon as it is used in an operation that may potentially
-     * overflow, such as an add, we conservatively widen it to double
-     * (number type). This is because overflow checks in the code
-     * are likely much more expensive that method specialization
-     *
-     * @return true if numbers should start as ints, false if they should
-     *   start as doubles
-     */
-    static boolean shouldUseIntegers() {
-        return USE_INTS;
-    }
-
-    private static final boolean USE_INTS;
-
-    /**
      * Should we use integers for arithmetic operations as well?
      * TODO: We currently generate no overflow checks so this is
      * disabled
@@ -165,13 +154,12 @@
      *   operands by default.
      */
     static boolean shouldUseIntegerArithmetic() {
-        return Compiler.shouldUseIntegers() && USE_INT_ARITH;
+        return USE_INT_ARITH;
     }
 
     private static final boolean USE_INT_ARITH;
 
     static {
-        USE_INTS       = !Options.getBooleanProperty("nashorn.compiler.ints.disable");
         USE_INT_ARITH  =  Options.getBooleanProperty("nashorn.compiler.intarithmetic");
 
         assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
@@ -338,46 +326,93 @@
         try {
             strict |= functionNode.isStrictMode();
 
-            if (!state.contains(State.LOWERED)) {
-                debugPrintAST();
+            /*
+             * These are the compile phases:
+             *
+             * Constant folding pass
+             *   Simple constant folding that will make elementary constructs go away
+             *
+             * Lower (Control flow pass)
+             *   Finalizes the control flow. Clones blocks for finally constructs and
+             *   similar things. Establishes termination criteria for nodes
+             *   Guarantee return instructions to method making sure control flow
+             *   cannot fall off the end. Replacing high level nodes with lower such
+             *   as runtime nodes where applicable.
+             *
+             * Attr
+             *   Assign symbols and types to all nodes.
+             *
+             * Splitter
+             *   Split the AST into several compile units based on a size heuristic
+             *   Splitter needs attributed AST for weight calculations (e.g. is
+             *   a + b a ScriptRuntime.ADD with call overhead or a dadd with much
+             *   less). Split IR can lead to scope information being changed.
+             *
+             * Contract: all variables must have slot assignments and scope assignments
+             * before lowering.
+             *
+             * FinalizeTypes
+             *   This pass finalizes the types for nodes. If Attr created wider types than
+             *   known during the first pass, convert nodes are inserted or access nodes
+             *   are specialized where scope accesses.
+             *
+             *   Runtime nodes may be removed and primitivized or reintroduced depending
+             *   on information that was established in Attr.
+             *
+             * CodeGeneration
+             *   Emit bytecode
+             *
+             */
+
+            debugPrintAST();
+
+            if (!state.contains(State.FINALIZED)) {
+                LOG.info("Folding constants in '" + functionNode.getName() + "'");
+                functionNode.accept(new FoldConstants());
+                state.add(State.CONSTANT_FOLDED);
+
                 LOG.info("Lowering '" + functionNode.getName() + "'");
                 functionNode.accept(new Lower(this));
                 state.add(State.LOWERED);
 
+                LOG.info("Attributing types '" + functionNode.getName() + "'");
+                functionNode.accept(new Attr(this));
+                state.add(State.ATTR);
+
+                this.scriptName = computeNames();
+
+                // Main script code always goes to this compile unit. Note that since we start this with zero weight
+                // and add script code last this class may end up slightly larger than others, but reserving one class
+                // just for the main script seems wasteful.
+                final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0L);
+                LOG.info("Splitting '" + functionNode.getName() + "'");
+                new Splitter(this, functionNode, scriptCompileUnit).split();
+                state.add(State.SPLIT);
+                assert functionNode.getCompileUnit() == scriptCompileUnit;
+
+                assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode();
+                if (functionNode.isStrictMode()) {
+                    strict = true;
+                }
+
+                LOG.info("Finalizing types for '" + functionNode.getName() + "'");
+                functionNode.accept(new FinalizeTypes(this));
+                state.add(State.FINALIZED);
+
+                // print ast and parse if --print-lower-ast and/or --print-lower-parse are selected
+                debugPrintAST();
+                debugPrintParse();
+
                 if (errors.hasErrors()) {
                     return false;
                 }
             }
 
-            scriptName = computeNames();
-
-            // Main script code always goes to this compile unit. Note that since we start this with zero weight
-            // and add script code last this class may end up slightly larger than others, but reserving one class
-            // just for the main script seems wasteful.
-            final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0l);
-            LOG.info("Splitting '" + functionNode.getName() + "'");
-            new Splitter(this, functionNode, scriptCompileUnit).split();
-            assert functionNode.getCompileUnit() == scriptCompileUnit;
-
-            /** Compute compile units */
-            assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode();
-            if (functionNode.isStrictMode()) {
-                strict = true;
-            }
-
-            LOG.info("Adjusting slot and scope information for symbols for '" + functionNode.getName() + "'");
-            functionNode.accept(new Lower.FinalizeSymbols());
-
-            LOG.info("Specializing callsite types for '" + functionNode.getName() + "'");
-            functionNode.accept(new AccessSpecializer());
-
             try {
                 LOG.info("Emitting bytecode for '" + functionNode.getName() + "'");
                 final CodeGenerator codegen = new CodeGenerator(this);
                 functionNode.accept(codegen);
                 codegen.generateScopeCalls();
-                debugPrintAST();
-                debugPrintParse();
             } catch (final VerifyError e) {
                 if (context._verify_code || context._print_code) {
                     context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Wed Jan 30 12:26:45 2013 +0100
@@ -0,0 +1,919 @@
+/*
+ * Copyright (c) 2010, 2013, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import java.util.HashSet;
+import java.util.List;
+
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.Assignment;
+import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.CallNode;
+import jdk.nashorn.internal.ir.CallNode.EvalArgs;
+import jdk.nashorn.internal.ir.CaseNode;
+import jdk.nashorn.internal.ir.CatchNode;
+import jdk.nashorn.internal.ir.DoWhileNode;
+import jdk.nashorn.internal.ir.ExecuteNode;
+import jdk.nashorn.internal.ir.ForNode;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.IfNode;
+import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.ReferenceNode;
+import jdk.nashorn.internal.ir.ReturnNode;
+import jdk.nashorn.internal.ir.RuntimeNode;
+import jdk.nashorn.internal.ir.RuntimeNode.Request;
+import jdk.nashorn.internal.ir.SwitchNode;
+import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.TernaryNode;
+import jdk.nashorn.internal.ir.ThrowNode;
+import jdk.nashorn.internal.ir.TypeOverride;
+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.NodeOperatorVisitor;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.parser.Token;
+import jdk.nashorn.internal.parser.TokenType;
+import jdk.nashorn.internal.runtime.Debug;
+import jdk.nashorn.internal.runtime.DebugLogger;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.Source;
+
+/**
+ * Lower to more primitive operations. After lowering, an AST has symbols and
+ * types. Lowering may also add specialized versions of methods to the script if
+ * the optimizer is turned on.
+ *
+ * Any expression that requires temporary storage as part of computation will
+ * also be detected here and give a temporary symbol
+ *
+ * For any op that we process in FinalizeTypes it is an absolute guarantee
+ * that scope and slot information is correct. This enables e.g. AccessSpecialization
+ * and frame optimizations
+ */
+
+final class FinalizeTypes extends NodeOperatorVisitor {
+
+    /** Current source. */
+    private final Source source;
+
+    private static final DebugLogger LOG = new DebugLogger("finalize");
+
+    /**
+     * Constructor.
+     *
+     * @param compiler the compiler
+     */
+    FinalizeTypes(final Compiler compiler) {
+        this.source = compiler.getSource();
+    }
+
+    @Override
+    public Node leave(final CallNode callNode) {
+        final EvalArgs evalArgs = callNode.getEvalArgs();
+        if (evalArgs != null) {
+            evalArgs.setCode(evalArgs.getCode().accept(this));
+        }
+
+        // AccessSpecializer - call return type may change the access for this location
+        final Node function = callNode.getFunction();
+        if (function instanceof ReferenceNode) {
+            setTypeOverride(callNode, ((ReferenceNode)function).getReference().getType());
+        }
+        return callNode;
+    }
+
+    private Node leaveUnary(final UnaryNode unaryNode) {
+        unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType()));
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveADD(final UnaryNode unaryNode) {
+        return leaveUnary(unaryNode);
+    }
+
+    @Override
+    public Node leaveBIT_NOT(final UnaryNode unaryNode) {
+        return leaveUnary(unaryNode);
+    }
+
+    @Override
+    public Node leaveCONVERT(final UnaryNode unaryNode) {
+        assert unaryNode.rhs().tokenType() != TokenType.CONVERT : "convert(convert encountered. check its origin and remove it";
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveDECINC(final UnaryNode unaryNode) {
+        specialize(unaryNode);
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveNEW(final UnaryNode unaryNode) {
+        assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
+        ((CallNode)unaryNode.rhs()).setIsNew();
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveSUB(final UnaryNode unaryNode) {
+        return leaveUnary(unaryNode);
+    }
+
+    /**
+     * Add is a special binary, as it works not only on arithmetic, but for
+     * strings etc as well.
+     */
+    @Override
+    public Node leaveADD(final BinaryNode binaryNode) {
+        final Node lhs = binaryNode.lhs();
+        final Node rhs = binaryNode.rhs();
+
+        final Type type = binaryNode.getType();
+
+        if (type.isObject()) {
+            if (!isAddString(binaryNode)) {
+                return new RuntimeNode(binaryNode, Request.ADD);
+            }
+        }
+
+        binaryNode.setLHS(convert(lhs, type));
+        binaryNode.setRHS(convert(rhs, type));
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveAND(final BinaryNode binaryNode) {
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveASSIGN(final BinaryNode binaryNode) {
+        Type destType = specialize(binaryNode);
+        if (destType == null) {
+            destType = binaryNode.getType();
+        }
+        binaryNode.setRHS(convert(binaryNode.rhs(), destType));
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
+        return leaveASSIGN(binaryNode);
+    }
+
+    @Override
+    public Node leaveBIT_AND(BinaryNode binaryNode) {
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : binaryNode.getSymbol();
+        return leaveBinary(binaryNode, Type.INT, Type.INT);
+    }
+
+    @Override
+    public Node leaveBIT_OR(BinaryNode binaryNode) {
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
+        return leaveBinary(binaryNode, Type.INT, Type.INT);
+    }
+
+    @Override
+    public Node leaveBIT_XOR(BinaryNode binaryNode) {
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
+        return leaveBinary(binaryNode, Type.INT, Type.INT);
+    }
+
+    @Override
+    public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
+        assert binaryNode.getSymbol() != null;
+        binaryNode.setRHS(discard(binaryNode.rhs()));
+        // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
+        // in that case, update the node type as well
+        propagateType(binaryNode, binaryNode.lhs().getType());
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
+        assert binaryNode.getSymbol() != null;
+        binaryNode.setLHS(discard(binaryNode.lhs()));
+        // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
+        // in that case, update the node type as well
+        propagateType(binaryNode, binaryNode.rhs().getType());
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveDIV(final BinaryNode binaryNode) {
+        return leaveBinaryArith(binaryNode);
+    }
+
+
+    @Override
+    public Node leaveEQ(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.EQ);
+    }
+
+    @Override
+    public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.EQ_STRICT);
+    }
+
+    @Override
+    public Node leaveGE(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.GE);
+    }
+
+    @Override
+    public Node leaveGT(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.GT);
+    }
+
+    @Override
+    public Node leaveLE(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.LE);
+    }
+
+    @Override
+    public Node leaveLT(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.LT);
+    }
+
+    @Override
+    public Node leaveMOD(final BinaryNode binaryNode) {
+        return leaveBinaryArith(binaryNode);
+    }
+
+    @Override
+    public Node leaveMUL(final BinaryNode binaryNode) {
+        return leaveBinaryArith(binaryNode);
+    }
+
+    @Override
+    public Node leaveNE(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.NE);
+    }
+
+    @Override
+    public Node leaveNE_STRICT(final BinaryNode binaryNode) {
+        return leaveCmp(binaryNode, Request.NE_STRICT);
+    }
+
+    @Override
+    public Node leaveOR(final BinaryNode binaryNode) {
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveSAR(final BinaryNode binaryNode) {
+        return leaveBinary(binaryNode, Type.INT, Type.INT);
+    }
+
+    @Override
+    public Node leaveSHL(final BinaryNode binaryNode) {
+        return leaveBinary(binaryNode, Type.INT, Type.INT);
+    }
+
+    @Override
+    public Node leaveSHR(final BinaryNode binaryNode) {
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong();
+        return leaveBinary(binaryNode, Type.INT, Type.INT);
+    }
+
+    @Override
+    public Node leaveSUB(final BinaryNode binaryNode) {
+        return leaveBinaryArith(binaryNode);
+    }
+
+    @Override
+    public Node enter(final Block block) {
+        updateSymbols(block);
+        return block;
+    }
+
+    @Override
+    public Node leave(final CatchNode catchNode) {
+        final Node exceptionCondition = catchNode.getExceptionCondition();
+        if (exceptionCondition != null) {
+            catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
+        }
+        return catchNode;
+    }
+
+    @Override
+    public Node enter(final DoWhileNode doWhileNode) {
+        return enter((WhileNode)doWhileNode);
+    }
+
+    @Override
+    public Node leave(final DoWhileNode doWhileNode) {
+        return leave((WhileNode)doWhileNode);
+    }
+
+    @Override
+    public Node leave(final ExecuteNode executeNode) {
+        executeNode.setExpression(discard(executeNode.getExpression()));
+        return executeNode;
+    }
+
+    @Override
+    public Node leave(final ForNode forNode) {
+        final Node init   = forNode.getInit();
+        final Node test   = forNode.getTest();
+        final Node modify = forNode.getModify();
+
+        if (forNode.isForIn()) {
+            forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
+            return forNode;
+        }
+
+        if (init != null) {
+            forNode.setInit(discard(init));
+        }
+
+        if (test != null) {
+            forNode.setTest(convert(test, Type.BOOLEAN));
+        } else {
+            assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode();
+        }
+
+        if (modify != null) {
+            forNode.setModify(discard(modify));
+        }
+
+        return forNode;
+    }
+
+    @Override
+    public Node enter(final FunctionNode functionNode) {
+        updateSymbols(functionNode);
+        return functionNode;
+    }
+
+    @Override
+    public Node leave(final IfNode ifNode) {
+        final Node test = convert(ifNode.getTest(), Type.BOOLEAN);
+        ifNode.setTest(test);
+        return ifNode;
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    public Node enter(final LiteralNode literalNode) {
+        if (literalNode instanceof ArrayLiteralNode) {
+            final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
+            final Node[]           array            = arrayLiteralNode.getValue();
+            final Type             elementType      = arrayLiteralNode.getElementType();
+
+            for (int i = 0; i < array.length; i++) {
+                final Node element = array[i];
+                if (element != null) {
+                    array[i] = convert(element.accept(this), elementType);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public Node leave(final ReturnNode returnNode) {
+        final Node expr = returnNode.getExpression();
+        if (expr != null) {
+            returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
+        }
+        return returnNode;
+    }
+
+    @Override
+    public Node leave(final RuntimeNode runtimeNode) {
+        final List<Node> args = runtimeNode.getArgs();
+        for (final Node arg : args) {
+            assert !arg.getType().isUnknown();
+        }
+        return runtimeNode;
+    }
+
+    @Override
+    public Node leave(final SwitchNode switchNode) {
+        final Node           expression  = switchNode.getExpression();
+        final List<CaseNode> cases       = switchNode.getCases();
+        final boolean        allInteger  = switchNode.getTag().getSymbolType().isInteger();
+
+        if (!allInteger) {
+            switchNode.setExpression(convert(expression, Type.OBJECT));
+            for (final CaseNode caseNode : cases) {
+                final Node test = caseNode.getTest();
+                if (test != null) {
+                    caseNode.setTest(convert(test, Type.OBJECT));
+                }
+            }
+        }
+
+        return switchNode;
+    }
+
+    @Override
+    public Node leave(final TernaryNode ternaryNode) {
+        ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN));
+        return ternaryNode;
+    }
+
+    @Override
+    public Node leave(final ThrowNode throwNode) {
+        throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
+        return throwNode;
+    }
+
+    @Override
+    public Node leave(final VarNode varNode) {
+        final Node rhs = varNode.getInit();
+        if (rhs != null) {
+            Type destType = specialize(varNode);
+            if (destType == null) {
+                destType = varNode.getType();
+            }
+            assert varNode.hasType() : varNode + " doesn't have a type";
+            varNode.setInit(convert(rhs, destType));
+        }
+        return varNode;
+    }
+
+    @Override
+    public Node leave(final WhileNode whileNode) {
+        final Node test = whileNode.getTest();
+        if (test != null) {
+            whileNode.setTest(convert(test, Type.BOOLEAN));
+        }
+        return whileNode;
+    }
+
+    @Override
+    public Node leave(final WithNode withNode) {
+        withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
+        return withNode;
+    }
+
+    private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
+        if (!symbol.isScope()) {
+            LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
+        }
+        if (loseSlot && symbol.hasSlot()) {
+            LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
+        }
+    }
+
+    /**
+     * Called after a block or function node (subclass of block) is finished. Guarantees
+     * that scope and slot information is correct for every symbol
+     * @param block block for which to to finalize type info.
+     */
+    private static void updateSymbols(final Block block) {
+
+        if (!block.needsScope()) {
+            return; // nothing to do
+        }
+
+        assert !(block instanceof FunctionNode) || block.getFunction() == block;
+
+        final FunctionNode functionNode   = block.getFunction();
+        final List<Symbol> symbols        = block.getFrame().getSymbols();
+        final boolean      allVarsInScope = functionNode.varsInScope();
+        final boolean      isVarArg       = functionNode.isVarArg();
+
+        for (final Symbol symbol : symbols) {
+            if (symbol.isInternal() || symbol.isThis()) {
+                continue;
+            }
+
+            if (symbol.isVar()) {
+                if (allVarsInScope || symbol.isScope()) {
+                    updateSymbolsLog(functionNode, symbol, true);
+                    symbol.setIsScope();
+                    symbol.setNeedsSlot(false);
+                } else {
+                    assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
+                }
+            } else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
+                updateSymbolsLog(functionNode, symbol, isVarArg);
+                symbol.setIsScope();
+                symbol.setNeedsSlot(!isVarArg);
+            }
+        }
+    }
+
+    /**
+     * Exit a comparison node and do the appropriate replacements. We need to introduce runtime
+     * nodes late for comparisons as types aren't known until the last minute
+     *
+     * Both compares and adds may turn into runtimes node at this level as when we first bump
+     * into the op in Attr, we may type it according to what we know there, which may be wrong later
+     *
+     * e.g. i (int) < 5 -> normal compare
+     *     i = object
+     *  then the post pass that would add the conversion to the 5 needs to
+     *
+     * @param binaryNode binary node to leave
+     * @param request    runtime request
+     * @return lowered cmp node
+     */
+    @SuppressWarnings("fallthrough")
+    private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) {
+        final Node lhs    = binaryNode.lhs();
+        final Node rhs    = binaryNode.rhs();
+
+        Type widest = Type.widest(lhs.getType(), rhs.getType());
+
+        boolean newRuntimeNode = false, finalized = false;
+        switch (request) {
+        case EQ_STRICT:
+        case NE_STRICT:
+            if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
+                newRuntimeNode = true;
+                widest = Type.OBJECT;
+                finalized = true;
+            }
+            //fallthru
+        default:
+            if (newRuntimeNode || widest.isObject()) {
+                final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request);
+                if (finalized) {
+                    runtimeNode.setIsFinal();
+                }
+                return runtimeNode;
+            }
+            break;
+        }
+
+        binaryNode.setLHS(convert(lhs, widest));
+        binaryNode.setRHS(convert(rhs, widest));
+
+        return binaryNode;
+    }
+
+    /**
+     * Compute the binary arithmetic type given the lhs and an rhs of a binary expression
+     * @param lhsType  the lhs type
+     * @param rhsType  the rhs type
+     * @return the correct binary type
+     */
+    private static Type binaryArithType(final Type lhsType, final Type rhsType) {
+        if (!Compiler.shouldUseIntegerArithmetic()) {
+            return Type.NUMBER;
+        }
+        return Type.widest(lhsType, rhsType, Type.NUMBER);
+    }
+
+    private Node leaveBinaryArith(final BinaryNode binaryNode) {
+        final Type type = binaryArithType(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+        return leaveBinary(binaryNode, type, type);
+    }
+
+    private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
+        binaryNode.setLHS(convert(binaryNode.lhs(), lhsType));
+        binaryNode.setRHS(convert(binaryNode.rhs(), rhsType));
+        return binaryNode;
+    }
+
+    /**
+     * A symbol (and {@link Property}) can be tagged as "may be primitive". This is
+     * used a hint for dual fields that it is even worth it to try representing this
+     * field as something other than java.lang.Object.
+     *
+     * @param node node in which to tag symbols as primitive
+     * @param to   which primitive type to use for tagging
+     */
+    private static void setCanBePrimitive(final Node node, final Type to) {
+        final HashSet<Node> exclude = new HashSet<>();
+
+        node.accept(new NodeVisitor() {
+            private void setCanBePrimitive(final Symbol symbol) {
+                LOG.info("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
+                symbol.setCanBePrimitive(to);
+            }
+
+            @Override
+            public Node enter(final IdentNode identNode) {
+                if (!exclude.contains(identNode)) {
+                    setCanBePrimitive(identNode.getSymbol());
+                }
+                return null;
+            }
+
+            @Override
+            public Node enter(final AccessNode accessNode) {
+                setCanBePrimitive(accessNode.getProperty().getSymbol());
+                return null;
+            }
+
+            @Override
+            public Node enter(final IndexNode indexNode) {
+                exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
+                return indexNode;
+            }
+        });
+    }
+
+    private static Type specialize(final Assignment<?> assignment) {
+        final Node node = ((Node)assignment);
+        final Node lhs = assignment.getAssignmentDest();
+        final Node rhs = assignment.getAssignmentSource();
+
+        if (!canHaveCallSiteType(lhs)) {
+            return null;
+        }
+
+        final Type to;
+        if (node.isSelfModifying()) {
+            to = node.getWidestOperationType();
+        } else {
+            to = rhs.getType();
+        }
+
+        if (!isSupportedCallSiteType(to)) {
+            //meaningless to specialize to boolean or object
+            return null;
+        }
+
+        setTypeOverride(lhs, to);
+        propagateType(node, to);
+
+        return to;
+    }
+
+
+    /**
+     * Is this a node that can have its type overridden. This is true for
+     * AccessNodes, IndexNodes and IdentNodes
+     *
+     * @param node the node to check
+     * @return true if node can have a callsite type
+     */
+    private static boolean canHaveCallSiteType(final Node node) {
+        return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType();
+    }
+
+    /**
+     * Is the specialization type supported. Currently we treat booleans as objects
+     * and have no special boolean type accessor, thus booleans are ignored.
+     * TODO - support booleans? NASHORN-590
+     *
+     * @param castTo the type to check
+     * @return true if call site type is supported
+     */
+    private static boolean isSupportedCallSiteType(final Type castTo) {
+        return castTo.isNumeric(); // don't specializable for boolean
+    }
+
+    /**
+     * Override the type of a node for e.g. access specialization of scope
+     * objects. Normally a variable can only get a wider type and narrower type
+     * sets are ignored. Not that a variable can still be on object type as
+     * per the type analysis, but a specific access may be narrower, e.g. if it
+     * is used in an arithmetic op. This overrides a type, regardless of
+     * type environment and is used primarily by the access specializer
+     *
+     * @param node    node for which to change type
+     * @param to      new type
+     */
+    private static void setTypeOverride(final Node node, final Type to) {
+        final Type from = node.getType();
+        if (!node.getType().equals(to)) {
+            LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
+            if (!to.isObject() && from.isObject()) {
+                setCanBePrimitive(node, to);
+            }
+        }
+        LOG.info("Type override for lhs in '" + node + "' => " + to);
+        ((TypeOverride)node).setType(to);
+    }
+
+    /**
+     * Add an explicit conversion. This is needed when attribution has created types
+     * that do not mesh into an op type, e.g. a = b, where b is object and a is double
+     * at the end of Attr, needs explicit conversion logic.
+     *
+     * An explicit conversion can be one of the following:
+     *   + Convert a literal - just replace it with another literal
+     *   + Convert a scope object - just replace the type of the access, e.g. get()D->get()I
+     *   + Explicit convert placement, e.g. a = (double)b - all other cases
+     *
+     * No other part of the world after {@link Attr} may introduce new symbols. This
+     * is the only place.
+     *
+     * @param node node to convert
+     * @param to   destination type
+     * @return     conversion node
+     */
+    private Node convert(final Node node, final Type to) {
+        assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
+        assert node != null : "node is null";
+        assert node.getSymbol() != null : "node " + node + " has no symbol!";
+        assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getCurrentFunctionNode();
+
+        final Type from = node.getType();
+
+        if (Type.areEquivalent(from, to)) {
+            return node;
+        }
+
+        if (from.isObject() && to.isObject()) {
+            return node;
+        }
+
+        Node resultNode = node;
+
+        if (node instanceof LiteralNode && !to.isObject()) {
+            final LiteralNode<?> newNode = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, to).eval();
+            if (newNode != null) {
+                resultNode = newNode;
+            }
+        } else {
+            if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) {
+                setTypeOverride(node, to);
+                return resultNode;
+            }
+            resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node);
+        }
+
+        LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
+
+        //This is the only place in this file that can create new temporaries
+        //FinalizeTypes may not introduce ANY node that is not a conversion.
+        getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode);
+        resultNode.copyTerminalFlags(node);
+
+        return resultNode;
+    }
+
+    private Node discard(final Node node) {
+        node.setDiscard(true);
+
+        if (node.getSymbol() != null) {
+            final Node discard = new UnaryNode(source, Token.recast(node.getToken(), TokenType.DISCARD), node);
+            //discard never has a symbol in the discard node - then it would be a nop
+            discard.copyTerminalFlags(node);
+            return discard;
+        }
+
+        // node has no result (symbol) so we can keep it the way it is
+        return node;
+    }
+
+    /**
+     * Whenever an expression like an addition or an assignment changes type, it
+     * may be that case that {@link Attr} created a symbol for an intermediate
+     * result of the expression, say for an addition. This also has to be updated
+     * if the expression type changes.
+     *
+     * Assignments use their lhs as node symbol, and in this case we can't modify
+     * it. Then {@link CodeGenerator#Store} needs to do an explicit conversion.
+     * This is happens very rarely.
+     *
+     * @param node
+     * @param to
+     */
+    private static void propagateType(final Node node, final Type to) {
+        final Symbol symbol = node.getSymbol();
+        if (symbol.isTemp()) {
+            symbol.setTypeOverride(to);
+            LOG.info("Type override for temporary in '" + node + "' => " + to);
+        }
+    }
+
+    /**
+     * Determine if the outcome of + operator is a string.
+     *
+     * @param node  Node to test.
+     * @return true if a string result.
+     */
+    private boolean isAddString(final Node node) {
+        if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) {
+            final BinaryNode binaryNode = (BinaryNode)node;
+            final Node lhs = binaryNode.lhs();
+            final Node rhs = binaryNode.rhs();
+
+            return isAddString(lhs) || isAddString(rhs);
+        }
+
+        return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString();
+    }
+
+    /**
+     * Whenever an explicit conversion is needed and the convertee is a literal, we can
+     * just change the literal
+     */
+    static class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator<LiteralNode<?>> {
+        private final Type type;
+
+        LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) {
+            super(parent);
+            this.type = type;
+        }
+
+        @Override
+        protected LiteralNode<?> eval() {
+            final Object value = ((LiteralNode<?>)parent).getValue();
+
+            LiteralNode<?> literalNode = null;
+
+            if (type.isString()) {
+                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toString(value));
+            } else if (type.isBoolean()) {
+                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toBoolean(value));
+            } else if (type.isInteger()) {
+                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toInt32(value));
+            } else if (type.isLong()) {
+                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toLong(value));
+            } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) {
+                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toNumber(value));
+            }
+
+            if (literalNode != null) {
+                //inherit literal symbol for attr.
+                literalNode.setSymbol(parent.getSymbol());
+            }
+
+            return literalNode;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java	Wed Jan 30 12:26:45 2013 +0100
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2010, 2013, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.EmptyNode;
+import jdk.nashorn.internal.ir.ExecuteNode;
+import jdk.nashorn.internal.ir.IfNode;
+import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.TernaryNode;
+import jdk.nashorn.internal.ir.UnaryNode;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.DebugLogger;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.Source;
+
+/**
+ * Simple constant folding pass, executed before IR is starting to be lowered.
+ */
+public class FoldConstants extends NodeVisitor {
+
+    private static final DebugLogger LOG = new DebugLogger("fold");
+
+    @Override
+    public Node leave(final UnaryNode unaryNode) {
+        final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
+        if (literalNode != null) {
+            LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
+            return literalNode;
+        }
+        return unaryNode;
+    }
+
+    @Override
+    public Node leave(final BinaryNode binaryNode) {
+        final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
+        if (literalNode != null) {
+            LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
+            return literalNode;
+        }
+        return binaryNode;
+    }
+
+    @Override
+    public Node leave(final IfNode ifNode) {
+        final Node test = ifNode.getTest();
+        if (test instanceof LiteralNode) {
+            final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail();
+            if (shortCut != null) {
+                return new ExecuteNode(shortCut);
+            }
+            return new EmptyNode(ifNode);
+        }
+        return ifNode;
+    }
+
+    @Override
+    public Node leave(final TernaryNode ternaryNode) {
+        final Node test = ternaryNode.lhs();
+        if (test instanceof LiteralNode) {
+            return ((LiteralNode<?>)test).isTrue() ? ternaryNode.rhs() : ternaryNode.third();
+        }
+        return ternaryNode;
+    }
+
+    /**
+     * Helper class to evaluate constant expressions at compile time This is
+     * also a simplifier used by BinaryNode visits, UnaryNode visits and
+     * conversions.
+     */
+    abstract static class ConstantEvaluator<T extends Node> {
+        protected T            parent;
+        protected final Source source;
+        protected final long   token;
+        protected final int    finish;
+
+        protected ConstantEvaluator(final T parent) {
+            this.parent = parent;
+            this.source = parent.getSource();
+            this.token  = parent.getToken();
+            this.finish = parent.getFinish();
+        }
+
+        /**
+         * Returns a literal node that replaces the given parent node, or null if replacement
+         * is impossible
+         * @return the literal node
+         */
+        protected abstract LiteralNode<?> eval();
+    }
+
+    private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> {
+        UnaryNodeConstantEvaluator(final UnaryNode parent) {
+            super(parent);
+        }
+
+        @Override
+        protected LiteralNode<?> eval() {
+            final Node rhsNode = parent.rhs();
+
+            if (!(rhsNode instanceof LiteralNode)) {
+                return null;
+            }
+
+            final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode;
+            final boolean rhsInteger = rhs.getType().isInteger();
+
+            LiteralNode<?> literalNode;
+
+            switch (parent.tokenType()) {
+            case ADD:
+                if (rhsInteger) {
+                    literalNode = LiteralNode.newInstance(source, token, finish, rhs.getInt32());
+                } else {
+                    literalNode = LiteralNode.newInstance(source, token, finish, rhs.getNumber());
+                }
+                break;
+            case SUB:
+                if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js
+                    literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getInt32());
+                } else {
+                    literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getNumber());
+                }
+                break;
+            case NOT:
+                literalNode = LiteralNode.newInstance(source, token, finish, !rhs.getBoolean());
+                break;
+            case BIT_NOT:
+                literalNode = LiteralNode.newInstance(source, token, finish, ~rhs.getInt32());
+                break;
+            default:
+                return null;
+            }
+
+            return literalNode;
+        }
+    }
+
+    //TODO add AND and OR with one constant parameter (bitwise)
+    private static class BinaryNodeConstantEvaluator extends ConstantEvaluator<BinaryNode> {
+        BinaryNodeConstantEvaluator(final BinaryNode parent) {
+            super(parent);
+        }
+
+        @Override
+        protected LiteralNode<?> eval() {
+            LiteralNode<?> result;
+
+            result = reduceTwoLiterals();
+            if (result != null) {
+                return result;
+            }
+
+            result = reduceOneLiteral();
+            if (result != null) {
+                return result;
+            }
+
+            return null;
+        }
+
+        @SuppressWarnings("static-method")
+        private LiteralNode<?> reduceOneLiteral() {
+            //TODO handle patterns like AND, OR, numeric ops that can be strength reduced but not replaced by a single literal node etc
+            return null;
+        }
+
+        private LiteralNode<?> reduceTwoLiterals() {
+            if (!(parent.lhs() instanceof LiteralNode && parent.rhs() instanceof LiteralNode)) {
+                return null;
+            }
+
+            final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs();
+            final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs();
+
+            final Type widest = Type.widest(lhs.getType(), rhs.getType());
+
+            boolean isInteger = widest.isInteger();
+            boolean isLong    = widest.isLong();
+
+            double value;
+
+            switch (parent.tokenType()) {
+            case DIV:
+                value = lhs.getNumber() / rhs.getNumber();
+                break;
+            case ADD:
+                if ((lhs.isString() || rhs.isNumeric()) && (rhs.isString() || rhs.isNumeric())) {
+                    Object res = ScriptRuntime.ADD(lhs.getObject(), rhs.getObject());
+                    if (res instanceof Number) {
+                        value = ((Number)res).doubleValue();
+                        break;
+                    }
+                    assert res instanceof CharSequence : res + " was not a CharSequence, it was a " + res.getClass();
+                    return LiteralNode.newInstance(source, token, finish, res.toString());
+                }
+                return null;
+            case MUL:
+                value = lhs.getNumber() * rhs.getNumber();
+                break;
+            case MOD:
+                value = lhs.getNumber() % rhs.getNumber();
+                break;
+            case SUB:
+                value = lhs.getNumber() - rhs.getNumber();
+                break;
+            case SHR:
+                return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL);
+            case SAR:
+                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32());
+            case SHL:
+                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() << rhs.getInt32());
+            case BIT_XOR:
+                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() ^ rhs.getInt32());
+            case BIT_AND:
+                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() & rhs.getInt32());
+            case BIT_OR:
+                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() | rhs.getInt32());
+            case GE:
+                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject()));
+            case LE:
+                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject()));
+            case GT:
+                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject()));
+            case LT:
+                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject()));
+            case NE:
+                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject()));
+            case NE_STRICT:
+                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject()));
+            case EQ:
+                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject()));
+            case EQ_STRICT:
+                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject()));
+            default:
+                return null;
+            }
+
+            isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value);
+            isLong    &= value != 0.0 && JSType.isRepresentableAsLong(value);
+
+            if (isInteger) {
+                return LiteralNode.newInstance(source, token, finish, JSType.toInt32(value));
+            } else if (isLong) {
+                return LiteralNode.newInstance(source, token, finish, JSType.toLong(value));
+            }
+
+            return LiteralNode.newInstance(source, token, finish, value);
+        }
+    }
+}
--- a/src/jdk/nashorn/internal/codegen/Lower.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/codegen/Lower.java	Wed Jan 30 12:26:45 2013 +0100
@@ -28,47 +28,19 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
-import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.ADD;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.DELETE;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.EQ;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.EQ_STRICT;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.FAIL_DELETE;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.GE;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.GT;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.IN;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.INSTANCEOF;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.LE;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.LT;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.NE;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.NE_STRICT;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.TYPEOF;
-import static jdk.nashorn.internal.ir.RuntimeNode.Request.VOID;
-import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_LET;
-import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
-import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
-import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
-import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Deque;
-import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
-import jdk.nashorn.internal.codegen.types.Type;
+
 import jdk.nashorn.internal.ir.AccessNode;
-import jdk.nashorn.internal.ir.Assignment;
+import jdk.nashorn.internal.ir.BaseNode;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.BreakNode;
@@ -85,19 +57,13 @@
 import jdk.nashorn.internal.ir.IfNode;
 import jdk.nashorn.internal.ir.IndexNode;
 import jdk.nashorn.internal.ir.LabelNode;
+import jdk.nashorn.internal.ir.LabeledNode;
 import jdk.nashorn.internal.ir.LineNumberNode;
 import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ObjectNode;
-import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
-import jdk.nashorn.internal.ir.RuntimeNode;
-import jdk.nashorn.internal.ir.RuntimeNode.Request;
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.ir.TernaryNode;
 import jdk.nashorn.internal.ir.ThrowNode;
 import jdk.nashorn.internal.ir.TryNode;
 import jdk.nashorn.internal.ir.UnaryNode;
@@ -108,54 +74,26 @@
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.parser.TokenType;
-import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.DebugLogger;
-import jdk.nashorn.internal.runtime.ECMAException;
-import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Property;
-import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptFunction;
-import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Source;
-import jdk.nashorn.internal.runtime.Undefined;
 
 /**
- * Lower to more primitive operations. After lowering, an AST has symbols and
- * types. Lowering may also add specialized versions of methods to the script if
- * the optimizer is turned on.
+ * Lower to more primitive operations. After lowering, an AST still has no symbols
+ * and types, but several nodes have been turned into more low level constructs
+ * and control flow termination criteria have been computed.
  *
- * Any expression that requires temporary storage as part of computation will
- * also be detected here and give a temporary symbol
+ * We do things like code copying/inlining of finallies here, as it is much
+ * harder and context dependent to do any code copying after symbols have been
+ * finalized.
  */
 
 final class Lower extends NodeOperatorVisitor {
-    /** Current compiler. */
+
     private final Compiler compiler;
 
-    /** Current source. */
     private final Source source;
 
-    /** List of lowered statements */
-    private List<Node> statements;
-
-    /** All symbols that are declared locally in a function node */
-    private List<Symbol> declaredSymbolsLocal;
-
-    /**
-     * Local definitions in current block (to discriminate from function
-     * declarations always defined in the function scope. This is for
-     * "can be undefined" analysis.
-     */
-    private Set<String> localDefs;
-
-    /**
-     * Local definitions in current block to guard against cases like
-     * NASHORN-467 when things can be undefined as they are used before
-     * their local var definition. *sigh* JavaScript...
-     */
-    private Set<String> localUses;
-
     /**
      * Nesting level stack. Currently just used for loops to avoid the problem
      * with terminal bodies that end with throw/return but still do continues to
@@ -163,8 +101,11 @@
      */
     private final Deque<Node> nesting;
 
-    private static final DebugLogger LOG   = new DebugLogger("lower");
-    private static final boolean     DEBUG = LOG.isEnabled();
+    private static final DebugLogger LOG = new DebugLogger("lower");
+
+    private Node lastStatement;
+
+    private List<Node> statements;
 
     /**
      * Constructor.
@@ -174,52 +115,17 @@
     Lower(final Compiler compiler) {
         this.compiler   = compiler;
         this.source     = compiler.getSource();
+        this.nesting    = new ArrayDeque<>();
         this.statements = new ArrayList<>();
-        this.nesting    = new ArrayDeque<>();
-    }
-
-    private void nest(final Node node) {
-        nesting.push(node);
-    }
-
-    private void unnest() {
-        nesting.pop();
-    }
-
-    static void debug(final String str) {
-        if (DEBUG) {
-            LOG.info(str);
-        }
-    }
-
-    @Override
-    public Node leave(final AccessNode accessNode) {
-        //accessNode.setBase(convert(accessNode.getBase(), Type.OBJECT));
-        getCurrentFunctionNode().newTemporary(Type.OBJECT, accessNode); //This is not always an object per se, but currently the narrowing logic resides in AccessSpecializer. @see AccessSpecializer!
-
-        return accessNode;
     }
 
     @Override
     public Node enter(final Block block) {
-        /*
-         * Save the statement list from the outer construct we are currently
-         * generating and push frame
-         */
-        final List<Node>  savedStatements = statements;
-        final Set<String> savedDefs       = localDefs;
-        final Set<String> savedUses       = localUses;
-
-        block.setFrame(getCurrentFunctionNode().pushFrame());
+        final Node       savedLastStatement = lastStatement;
+        final List<Node> savedStatements    = statements;
 
         try {
-            /*
-             * Reset the statement instance var, new block
-             */
-            statements = new ArrayList<>();
-            localDefs  = new HashSet<>(savedDefs);
-            localUses  = new HashSet<>(savedUses);
-
+            this.statements = new ArrayList<>();
             for (final Node statement : block.getStatements()) {
                 statement.accept(this);
                 /*
@@ -231,28 +137,715 @@
                  *
                  * @see NASHORN-285
                  */
-                final Node lastStatement = Node.lastStatement(statements);
                 if (lastStatement != null && lastStatement.isTerminal()) {
-                    block.copyTerminalFlags(lastStatement);
+                    copyTerminal(block, lastStatement);
+                    break;
+                }
+            }
+            block.setStatements(statements);
+
+        } finally {
+            this.statements = savedStatements;
+            this.lastStatement = savedLastStatement;
+        }
+
+        return null;
+    }
+
+    @Override
+    public Node enter(final BreakNode breakNode) {
+        return enterBreakOrContinue(breakNode);
+    }
+
+    @Override
+    public Node enter(final CallNode callNode) {
+        final Node function = markerFunction(callNode.getFunction());
+        callNode.setFunction(function);
+        checkEval(callNode); //check if this is an eval call and store the information
+        return callNode;
+    }
+
+    @Override
+    public Node leave(final CaseNode caseNode) {
+        caseNode.copyTerminalFlags(caseNode.getBody());
+        return caseNode;
+    }
+
+    @Override
+    public Node leave(final CatchNode catchNode) {
+        catchNode.copyTerminalFlags(catchNode.getBody());
+        addStatement(catchNode);
+        return catchNode;
+    }
+
+    @Override
+    public Node enter(final ContinueNode continueNode) {
+        return enterBreakOrContinue(continueNode);
+    }
+
+    @Override
+    public Node enter(final DoWhileNode doWhileNode) {
+        return enter((WhileNode)doWhileNode);
+    }
+
+    @Override
+    public Node leave(final DoWhileNode doWhileNode) {
+        return leave((WhileNode)doWhileNode);
+    }
+
+    @Override
+    public Node enter(final EmptyNode emptyNode) {
+        return null;
+    }
+
+    @Override
+    public Node leave(final ExecuteNode executeNode) {
+        final Node expr = executeNode.getExpression();
+
+        if (getCurrentFunctionNode().isScript()) {
+            if (!(expr instanceof Block)) {
+                if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
+                    executeNode.setExpression(new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN),
+                            getCurrentFunctionNode().getResultNode(),
+                            expr));
+                }
+            }
+        }
+
+        copyTerminal(executeNode, executeNode.getExpression());
+        addStatement(executeNode);
+
+        return executeNode;
+    }
+
+    @Override
+    public Node enter(final ForNode forNode) {
+        nest(forNode);
+        return forNode;
+    }
+
+    @Override
+    public Node leave(final ForNode forNode) {
+        final Node  test = forNode.getTest();
+        final Block body = forNode.getBody();
+
+        if (!forNode.isForIn() && test == null) {
+            setHasGoto(forNode);
+        }
+
+        final boolean escapes = controlFlowEscapes(body);
+        if (escapes) {
+            setTerminal(body, false);
+        }
+
+        // pop the loop from the loop context
+        unnest(forNode);
+
+        if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
+            forNode.setTest(null);
+            setTerminal(forNode, !escapes);
+        }
+
+        addStatement(forNode);
+
+        return forNode;
+    }
+
+    @Override
+    public Node enter(final FunctionNode functionNode) {
+        LOG.info("START FunctionNode: " + functionNode.getName());
+
+        initFunctionNode(functionNode);
+
+        Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
+
+        nest(functionNode);
+
+        /*
+         * As we are evaluating a nested structure, we need to store the
+         * statement list for the surrounding block and restore it when the
+         * function is done
+         */
+        final List<Node> savedStatements = statements;
+        final Node savedLastStatement = lastStatement;
+
+        statements    = new ArrayList<>();
+        lastStatement = null;
+
+        // for initial eval result is the last declared function
+        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
+            final IdentNode ident = nestedFunction.getIdent();
+            if (ident != null && nestedFunction.isStatement()) {
+                initialEvalResult = new IdentNode(ident);
+            }
+        }
+
+        if (functionNode.needsSelfSymbol()) {
+            //function needs to start with var funcIdent = __callee_;
+            statements.add(functionNode.getSelfSymbolInit().accept(this));
+        }
+
+        try {
+            // Every nested function needs a definition in the outer function with its name. Add these.
+            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
+                final VarNode varNode = nestedFunction.getFunctionVarNode();
+                if (varNode != null) {
+                    final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode();
+                    if (lineNumberNode != null) {
+                        lineNumberNode.accept(this);
+                    }
+                    varNode.accept(this);
+                    varNode.setIsFunctionVarNode();
+                }
+            }
+
+            if (functionNode.isScript()) {
+                new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
+            }
+
+            //do the statements - this fills the block with code
+            for (final Node statement : functionNode.getStatements()) {
+                statement.accept(this);
+                //If there are unused terminated endpoints in the function, we need
+                // to add a "return undefined" in those places for correct semantics
+                LOG.info("Checking lastStatement="+lastStatement+" for terminal flags");
+                if (lastStatement != null && lastStatement.hasTerminalFlags()) {
+                    copyTerminal(functionNode, lastStatement);
                     break;
                 }
             }
 
-            block.setStatements(statements);
+            functionNode.setStatements(statements);
+
+            if (!functionNode.isTerminal()) {
+                guaranteeReturn(functionNode);
+            }
+
+            //lower all nested functions
+            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
+                nestedFunction.accept(this);
+            }
+
         } finally {
-            /*
-             * Restore the saved statements after block ends and pop frame
-             */
-            statements = savedStatements;
-            localDefs  = savedDefs;
-            localUses  = savedUses;
-
-            getCurrentFunctionNode().popFrame();
+            statements    = savedStatements;
+            lastStatement = savedLastStatement;
         }
 
+        LOG.info("END FunctionNode: " + functionNode.getName());
+        unnest(functionNode);
+
         return null;
     }
 
+    @Override
+    public Node enter(final IfNode ifNode) {
+        return nest(ifNode);
+    }
+
+    @Override
+    public Node leave(final IfNode ifNode) {
+        final Node pass = ifNode.getPass();
+        final Node fail = ifNode.getFail();
+
+        if (pass.isTerminal() && fail != null && fail.isTerminal()) {
+            setTerminal(ifNode,  true);
+        }
+
+        addStatement(ifNode);
+        unnest(ifNode);
+
+        return ifNode;
+    }
+
+    @Override
+    public Node enter(LabelNode labelNode) {
+        final Block body = labelNode.getBody();
+        body.accept(this);
+        copyTerminal(labelNode, body);
+        addStatement(labelNode);
+        return null;
+    }
+
+    @Override
+    public Node enter(final LineNumberNode lineNumberNode) {
+        addStatement(lineNumberNode, false); // don't put it in lastStatement cache
+        return null;
+    }
+
+    @Override
+    public Node enter(final ReturnNode returnNode) {
+        final TryNode tryNode = returnNode.getTryChain();
+        final Node    expr    = returnNode.getExpression();
+
+        if (tryNode != null) {
+            //we are inside a try block - we don't necessarily have a result node yet. attr will do that.
+            if (expr != null) {
+                //we need to evaluate the result of the return in case it is complex while
+                //still in the try block, store it in a result value and return it afterwards
+                final long token        = returnNode.getToken();
+                final Node resultNode   = new IdentNode(getCurrentFunctionNode().getResultNode());
+                final Node assignResult = new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expr);
+
+                //add return_in_try = expr; to try block
+                new ExecuteNode(source, token, Token.descPosition(token), assignResult).accept(this);
+
+                //splice in the finally code, inlining it here
+                if (copyFinally(tryNode, null)) {
+                    return null;
+                }
+
+                //make sure that the return node now returns 'return_in_try'
+                returnNode.setExpression(resultNode);
+            } else if (copyFinally(tryNode, null)) {
+                return null;
+            }
+        } else if (expr != null) {
+            returnNode.setExpression(expr.accept(this));
+        }
+
+        addStatement(returnNode);
+
+        return null;
+    }
+
+    @Override
+    public Node leave(final ReturnNode returnNode) {
+        addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
+        return returnNode;
+    }
+
+    @Override
+    public Node enter(final SwitchNode switchNode) {
+        nest(switchNode);
+        return switchNode;
+    }
+
+    @Override
+    public Node leave(final SwitchNode switchNode) {
+        unnest(switchNode);
+
+        final List<CaseNode> cases       = switchNode.getCases();
+        final CaseNode       defaultCase = switchNode.getDefaultCase();
+
+        boolean allTerminal = !cases.isEmpty();
+        for (final CaseNode caseNode : switchNode.getCases()) {
+            allTerminal &= caseNode.isTerminal();
+        }
+
+        if (allTerminal && defaultCase != null && defaultCase.isTerminal()) {
+            setTerminal(switchNode, true);
+        }
+
+        addStatement(switchNode);
+
+        return switchNode;
+    }
+
+    @Override
+    public Node leave(final ThrowNode throwNode) {
+        addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
+        return throwNode;
+    }
+
+    @Override
+    public Node enter(final TryNode tryNode) {
+        final Block  finallyBody = tryNode.getFinallyBody();
+        final long   token       = tryNode.getToken();
+        final int    finish      = tryNode.getFinish();
+
+        nest(tryNode);
+
+        if (finallyBody == null) {
+            //do nothing if no finally exists
+            return tryNode;
+        }
+
+        /*
+         * We have a finally clause.
+         *
+         * Transform to do finally tail duplication as follows:
+         *
+         * <pre>
+         *  try {
+         *    try_body
+         *  } catch e1 {
+         *    catchbody_1
+         *  }
+         *  ...
+         *  } catch en {
+         *    catchbody_n
+         *  } finally {
+         *    finally_body
+         *  }
+         *
+         *  (where e1 ... en are optional)
+         *
+         *  turns into
+         *
+         *  try {
+         *    try {
+         *      try_body
+         *    } catch e1 {
+         *      catchbody1
+         *      //nothing inlined explicitly here, return, break other
+         *      //terminals may inline the finally body
+         *      ...
+         *    } catch en {
+         *      catchbody2
+         *      //nothing inlined explicitly here, return, break other
+         *      //terminals may inline the finally body
+         *    }
+         *  } catch all ex {
+         *      finally_body_inlined
+         *      rethrow ex
+         *  }
+         *  finally_body_inlined
+         * </pre>
+         *
+         * If tries are catches are terminal, visitors for return, break &
+         * continue will handle the tail duplications. Throw needs to be
+         * treated specially with the catchall as described in the above
+         * ASCII art.
+         *
+         * If the try isn't terminal we do the finally_body_inlined at the
+         * end. If the try is terminated with continue/break/return the
+         * existing visitor logic will inline the finally before that
+         * operation. if the try is terminated with a throw, the catches e1
+         * ... en will have a chance to process the exception. If the
+         * appropriate catch e1..en is non terminal we fall through to the
+         * last finally_body_inlined. if the catch e1...en IS terminal with
+         * continue/break/return existing visitor logic will fix it. If they
+         * are terminal with another throw it goes to the catchall and the
+         * finally_body_inlined marked (*) will fix it before rethrowing
+         * whatever problem there was for identical semantic.
+         */
+
+        // if try node does not contain a catch we can skip creation of a new
+        // try node and just append our synthetic catch to the existing try node.
+        if (!tryNode.getCatchBlocks().isEmpty()) {
+            // insert an intermediate try-catch* node, where we move the body and all catch blocks.
+            // the original try node become a try-finally container for the new try-catch* node.
+            // because we don't clone (to avoid deep copy), we have to fix the block chain in the end.
+            final TryNode innerTryNode;
+            innerTryNode = new TryNode(source, token, finish, tryNode.getNext());
+            innerTryNode.setBody(tryNode.getBody());
+            innerTryNode.setCatchBlocks(tryNode.getCatchBlocks());
+
+            // set outer tryNode's body to innerTryNode
+            final Block outerBody;
+            outerBody = new Block(source, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode());
+            outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
+            tryNode.setBody(outerBody);
+            tryNode.setCatchBlocks(null);
+
+            // now before we go on, we have to fix the block parents
+            // (we repair the block tree after the insertion so that all references are intact)
+            innerTryNode.getBody().setParent(tryNode.getBody());
+            for (final Block block : innerTryNode.getCatchBlocks()) {
+                block.setParent(tryNode.getBody());
+            }
+        }
+
+        // create a catch-all that inlines finally and rethrows
+
+        final Block catchBlock      = new Block(source, token, finish, getCurrentBlock(), getCurrentFunctionNode());
+        //this catch block should get define symbol
+
+        final Block catchBody       = new Block(source, token, finish, catchBlock, getCurrentFunctionNode());
+        final Node  catchAllFinally = finallyBody.clone();
+
+        catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
+        setTerminal(catchBody, true);
+
+        final CatchNode catchAllNode;
+        final IdentNode exception;
+
+        exception    = new IdentNode(source, token, finish, compiler.uniqueName("catch_all"));
+        catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
+        catchAllNode.setIsSyntheticRethrow();
+
+        catchBlock.addStatement(catchAllNode);
+
+        // replace all catches of outer tryNode with the catch-all
+        tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock)));
+
+        /*
+         * We leave the finally block for the original try in place for now
+         * so that children visitations will work. It is removed and placed
+         * afterwards in the else case below, after all children are visited
+         */
+
+        return tryNode;
+    }
+
+    @Override
+    public Node leave(final TryNode tryNode) {
+        final Block finallyBody   = tryNode.getFinallyBody();
+
+        boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
+
+        for (final Block catchBlock : tryNode.getCatchBlocks()) {
+            allTerminal &= catchBlock.isTerminal();
+        }
+
+        tryNode.setIsTerminal(allTerminal);
+
+        addStatement(tryNode);
+        unnest(tryNode);
+
+        // if finally body is present, place it after the tryNode
+        if (finallyBody != null) {
+            tryNode.setFinallyBody(null);
+            addStatement(finallyBody);
+        }
+
+        return tryNode;
+    }
+
+    @Override
+    public Node leave(final VarNode varNode) {
+        addStatement(varNode);
+        return varNode;
+    }
+
+    @Override
+    public Node enter(final WhileNode whileNode) {
+        return nest(whileNode);
+    }
+
+    @Override
+    public Node leave(final WhileNode whileNode) {
+        final Node test = whileNode.getTest();
+
+        if (test == null) {
+            setHasGoto(whileNode);
+        }
+
+        final Block   body    = whileNode.getBody();
+        final boolean escapes = controlFlowEscapes(body);
+        if (escapes) {
+            setTerminal(body, false);
+        }
+
+        Node node = whileNode;
+
+        if (body.isTerminal()) {
+            if (whileNode instanceof DoWhileNode) {
+                setTerminal(whileNode, true);
+            } else if (conservativeAlwaysTrue(test)) {
+                node = new ForNode(source, whileNode.getToken(), whileNode.getFinish());
+                ((ForNode)node).setBody(body);
+                ((ForNode)node).accept(this);
+                setTerminal(node, !escapes);
+            }
+        }
+
+        // pop the loop from the loop context
+        unnest(whileNode);
+        addStatement(node);
+
+        return node;
+    }
+
+    @Override
+    public Node leave(final WithNode withNode) {
+        if (withNode.getBody().isTerminal()) {
+            setTerminal(withNode,  true);
+        }
+        addStatement(withNode);
+
+        return withNode;
+    }
+
+    @Override
+    public Node leaveDELETE(final UnaryNode unaryNode) {
+        final Node rhs = unaryNode.rhs();
+        if (rhs instanceof IdentNode || rhs instanceof BaseNode) {
+            return unaryNode;
+        }
+        addStatement(new ExecuteNode(rhs));
+        return LiteralNode.newInstance(unaryNode, true);
+    }
+
+    /**
+     * Given a function node that is a callee in a CallNode, replace it with
+     * the appropriate marker function. This is used by {@link CodeGenerator}
+     * for fast scope calls
+     *
+     * @param function function called by a CallNode
+     * @return transformed node to marker function or identity if not ident/access/indexnode
+     */
+    private static Node markerFunction(final Node function) {
+        if (function instanceof IdentNode) {
+            return new IdentNode((IdentNode)function) {
+                @Override
+                public boolean isFunction() {
+                    return true;
+                }
+            };
+        } else if (function instanceof AccessNode) {
+            return new AccessNode((AccessNode)function) {
+                @Override
+                public boolean isFunction() {
+                    return true;
+                }
+            };
+        } else if (function instanceof IndexNode) {
+            return new IndexNode((IndexNode)function) {
+                @Override
+                public boolean isFunction() {
+                    return true;
+                }
+            };
+        }
+
+        return function;
+    }
+
+    /**
+     * Calculate a synthetic eval location for a node for the stacktrace, for example src#17<eval>
+     * @param node a node
+     * @return eval location
+     */
+    private static String evalLocation(final IdentNode node) {
+        //final StringBuilder sb = new StringBuilder(node.getSource().getName());
+        return new StringBuilder().
+            append(node.getSource().getName()).
+            append('#').
+            append(node.getSource().getLine(node.position())).
+            append("<eval>").
+            toString();
+    }
+
+    /**
+     * Check whether a call node may be a call to eval. In that case we
+     * clone the args in order to create the following construct in
+     * {@link CodeGenerator}
+     *
+     * <pre>
+     * if (calledFuntion == buildInEval) {
+     *    eval(cloned arg);
+     * } else {
+     *    cloned arg;
+     * }
+     * </pre>
+     *
+     * @param callNode call node to check if it's an eval
+     */
+    private void checkEval(final CallNode callNode) {
+        if (callNode.getFunction() instanceof IdentNode) {
+
+            final List<Node> args   = callNode.getArgs();
+            final IdentNode  callee = (IdentNode)callNode.getFunction();
+
+            // 'eval' call with at least one argument
+            if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
+                final CallNode.EvalArgs evalArgs =
+                    new CallNode.EvalArgs(
+                        args.get(0).clone().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
+                        getCurrentFunctionNode().getThisNode(),
+                        evalLocation(callee),
+                        getCurrentFunctionNode().isStrictMode());
+                callNode.setEvalArgs(evalArgs);
+            }
+        }
+    }
+
+    private static boolean conservativeAlwaysTrue(final Node node) {
+        return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue()));
+    }
+
+    /**
+     * Helper that given a loop body makes sure that it is not terminal if it
+     * has a continue that leads to the loop header or to outer loops' loop
+     * headers. This means that, even if the body ends with a terminal
+     * statement, we cannot tag it as terminal
+     *
+     * @param loopBody the loop body to check
+     * @return true if control flow may escape the loop
+     */
+    private boolean controlFlowEscapes(final Node loopBody) {
+        final List<Node> escapes = new ArrayList<>();
+
+        loopBody.accept(new NodeVisitor() {
+            @Override
+            public Node leave(final BreakNode node) {
+                escapes.add(node);
+                return node;
+            }
+
+            @Override
+            public Node leave(final ContinueNode node) {
+                // all inner loops have been popped.
+                if (nesting.contains(node.getTargetNode())) {
+                    escapes.add(node);
+                }
+                return node;
+            }
+        });
+
+        return !escapes.isEmpty();
+    }
+
+    private void guaranteeReturn(final FunctionNode functionNode) {
+        Node resultNode;
+
+        if (functionNode.isScript()) {
+            resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr
+        } else {
+            if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) {
+                return; //already in place or not needed, as it should be for a non-undefined returning function
+            }
+            resultNode = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
+        }
+
+        //create a return statement
+        final Node returnNode = new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
+        returnNode.accept(this);
+    }
+
+
+    private Node nest(final Node node) {
+        LOG.info("Nesting: " + node);
+        LOG.indent();
+        nesting.push(node);
+        return node;
+    }
+
+    private void unnest(final Node node) {
+        LOG.unindent();
+        assert nesting.getFirst() == node : "inconsistent nesting order : " + nesting.getFirst() + " != " + node;
+        LOG.info("Unnesting: " + nesting);
+        nesting.pop();
+    }
+
+    private static void setTerminal(final Node node, final boolean isTerminal) {
+        LOG.info("terminal = " + isTerminal + " for " + node);
+        node.setIsTerminal(isTerminal);
+    }
+
+    private static void setHasGoto(final Node node) { //, final boolean hasGoto) {
+        LOG.info("hasGoto = true for " + node);
+        node.setHasGoto();
+    }
+
+    private static void copyTerminal(final Node node, final Node sourceNode) {
+        LOG.info("copy terminal flags " + sourceNode + " -> " + node);
+        node.copyTerminalFlags(sourceNode);
+    }
+
+    private void addStatement(final Node statement, final boolean storeInLastStatement) {
+        LOG.info("add statement = " + statement + " (lastStatement = " + lastStatement + ")");
+        statements.add(statement);
+        if (storeInLastStatement) {
+            lastStatement = statement;
+        }
+    }
+
+    private void addStatement(final Node statement) {
+        addStatement(statement, true);
+    }
+
     /**
      * Determine if Try block is inside target block.
      *
@@ -316,271 +909,15 @@
         return false;
     }
 
-    @Override
-    public Node enter(final BreakNode breakNode) {
-        final TryNode tryNode = breakNode.getTryChain();
-
-        if (tryNode != null && copyFinally(tryNode, breakNode.getTargetNode())) {
+    private Node enterBreakOrContinue(final LabeledNode labeledNode) {
+        final TryNode tryNode = labeledNode.getTryChain();
+        if (tryNode != null && copyFinally(tryNode, labeledNode.getTargetNode())) {
             return null;
         }
-
-        statements.add(breakNode);
-
+        addStatement(labeledNode);
         return null;
     }
 
-    /**
-     * Blesses nodes with an expectant type, converting if necessary.
-     *
-     * @param node Node to be blest.
-     * @param type Type class expected.
-     *
-     * @return Converted node or original node if no conversion is needed.
-     */
-    private Node convert(final Node node, final Type type) {
-
-        final Symbol       symbol       = node.getSymbol();
-        final FunctionNode functionNode = getCurrentFunctionNode();
-
-        assert !type.isUnknown() : "unknown";
-
-        /*
-         * Conversions are now mandatory, have to be placed at code time and
-         * cannot be removed as there might be cases like:
-         *
-         * var x = 17; if (x + 1) { ... }
-         *
-         * x = Number(4711); if (x + 1) { ... }
-         *
-         * In the old world this behaved as follows:
-         *
-         * Here, x is originally inferred type to a number, then the if does a
-         * double add without a cast. However, the next statement turns x into
-         * an object, and this messes up the original. If we would now suddenly
-         * need an explicit cast for the first addition, given that we don't do
-         * fancy stuff like splitting live range.
-         *
-         * Even worse is the case where we have an eval that modifies local
-         * variables in the middle of a function and may widen a type suspected
-         * to be at most e.g. NUMBER to e.g. OBJECT.
-         *
-         * There are a few local optimizations we can do. If we want to do an
-         * OBJECT to OBJECT cast, for example, we can skip it as already is
-         * maximally wide, except if we are in a method with an eval where
-         * everything is possible...
-         *
-         * @see test/test262/test/suite/ch07/7.2/S7.2_A1.4_T2.js for an example
-         * of this.
-         */
-
-        assert symbol != null : "no symbol for " + node;
-
-        /* check object to object cast */
-        if (!functionNode.hasEval() && node.getType().isEquivalentTo(Type.OBJECT) && type.isEquivalentTo(Type.OBJECT)) {
-            return node;
-        }
-
-        Node resultNode = node;
-
-        // Literal nodes may be converted directly
-
-        if (node instanceof LiteralNode) {
-            final LiteralNode<?> convertedLiteral = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, type).eval();
-            if (convertedLiteral != null) {
-                resultNode = newLiteral(convertedLiteral);
-            }
-            // object literals still need the cast
-            if (type.isObject()) {
-                resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node);
-            }
-        } else {
-            if (resultNode.getSymbol().isParam()) {
-                resultNode.getSymbol().setType(type);
-            }
-             resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), resultNode);
-        }
-
-        functionNode.newTemporary(type, resultNode);
-        resultNode.copyTerminalFlags(node);
-
-        return resultNode;
-    }
-
-    /**
-     * Accept and convert all arguments to type Object. If we have a
-     * specialization profile for this function, we instead try to specialize
-     * the arguments before the casts based on their current types and values.
-     *
-     * @param callNode function call
-     * @return return type for call
-     */
-    private Type acceptArgs(final CallNode callNode) {
-        final List<Node> oldArgs = callNode.getArgs();
-        final List<Node> acceptedArgs = new ArrayList<>(oldArgs.size());
-
-        for (final Node arg : oldArgs) {
-            //acceptedArgs.add(convert(arg.accept(this), OBJECT));
-            acceptedArgs.add(arg.accept(this));
-        }
-        callNode.setArgs(acceptedArgs);
-
-        return Type.OBJECT;
-    }
-
-    private static String evalLocation(final IdentNode node) {
-        final StringBuilder sb = new StringBuilder(node.getSource().getName());
-
-        sb.append('#');
-        sb.append(node.getSource().getLine(node.position()));
-        sb.append("<eval>");
-
-        return sb.toString();
-    }
-
-    private void checkEval(final CallNode callNode) {
-        if (callNode.getFunction() instanceof IdentNode) {
-
-            final List<Node> args   = callNode.getArgs();
-            final IdentNode  callee = (IdentNode)callNode.getFunction();
-
-            // 'eval' call with atleast one argument
-            if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
-                final CallNode.EvalArgs evalArgs = new CallNode.EvalArgs();
-                // code that is evaluated
-                evalArgs.code = args.get(0).clone();
-                evalArgs.code.accept(this);
-                // 'this' to be passed to evaluated code
-                evalArgs.evalThis = new IdentNode(getCurrentFunctionNode().getThisNode());
-                // location string of the eval call
-                evalArgs.location = evalLocation(callee);
-                // strict mode context or not?
-                evalArgs.strictMode = getCurrentFunctionNode().isStrictMode();
-                callNode.setEvalArgs(evalArgs);
-            }
-        }
-    }
-
-    private static Node markerFunction(final Node function) {
-        if (function instanceof IdentNode) {
-            return new IdentNode((IdentNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
-        } else if (function instanceof AccessNode) {
-            return new AccessNode((AccessNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
-        } else if (function instanceof IndexNode) {
-            return new IndexNode((IndexNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
-        }
-
-        return function;
-    }
-
-    @Override
-    public Node enter(final CallNode callNode) {
-        final Node function       = callNode.getFunction();
-        final Node markedFunction = markerFunction(function);
-
-        callNode.setFunction(markedFunction.accept(this));
-
-        checkEval(callNode);
-
-        final Type returnType = acceptArgs(callNode);
-        getCurrentFunctionNode().newTemporary(returnType, callNode);
-        callNode.getFunction().getSymbol().setType(returnType);
-
-        return null;
-    }
-
-    @Override
-    public Node leave(final CaseNode caseNode) {
-        caseNode.copyTerminalFlags(caseNode.getBody());
-
-        return caseNode;
-    }
-
-    @Override
-    public Node enter(final CatchNode catchNode) {
-        final IdentNode ident = catchNode.getException();
-        final Block     block = getCurrentBlock();
-
-        // define block-local exception variable
-        block.defineSymbol(ident.getName(), IS_VAR | IS_LET, ident).setType(Type.OBJECT);
-        localDefs.add(ident.getName());
-
-        return catchNode;
-    }
-
-    @Override
-    public Node leave(final CatchNode catchNode) {
-        final Node exceptionCondition = catchNode.getExceptionCondition();
-        if (exceptionCondition != null) {
-            catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
-        }
-
-        catchNode.copyTerminalFlags(catchNode.getBody());
-
-        statements.add(catchNode);
-
-        return catchNode;
-    }
-
-    @Override
-    public Node enter(final ContinueNode continueNode) {
-        final TryNode tryNode = continueNode.getTryChain();
-        final Node target = continueNode.getTargetNode();
-
-        if (tryNode != null && copyFinally(tryNode, target)) {
-            return null;
-        }
-
-        statements.add(continueNode);
-
-        return null;
-    }
-
-    @Override
-    public Node enter(final DoWhileNode whileNode) {
-        return enter((WhileNode)whileNode);
-    }
-
-    @Override
-    public Node leave(final DoWhileNode whileNode) {
-        return leave((WhileNode)whileNode);
-    }
-
-    @Override
-    public Node enter(final EmptyNode emptyNode) {
-        return null;
-    }
-
-    /**
-     * Is this an assignment to the special variable that hosts scripting eval
-     * results?
-     *
-     * @param expression expression to check whether it is $evalresult = X
-     *
-     * @return true if an assignment to eval result, false otherwise
-     */
-    private boolean isEvalResultAssignment(final Node expression) {
-        Node e = expression;
-        if (e.tokenType() == TokenType.DISCARD) {
-            e = ((UnaryNode)expression).rhs();
-        }
-        final Node resultNode = getCurrentFunctionNode().getResultNode();
-        return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode);
-    }
 
     /**
      * An internal expression has a symbol that is tagged internal. Check if
@@ -595,2543 +932,41 @@
     }
 
     /**
-     * Discard the result of the expression.
+     * Is this an assignment to the special variable that hosts scripting eval
+     * results?
      *
-     * @param expression Expression to discard.
-     *
-     * @return Discard node.
+     * @param expression expression to check whether it is $evalresult = X
+     * @return true if an assignment to eval result, false otherwise
      */
-    private Node discard(final Node expression) {
-        expression.setDiscard(true);
-
-        if (expression.getSymbol() != null) {
-            final Node discard = new UnaryNode(source, Token.recast(expression.getToken(), TokenType.DISCARD), expression);
-            discard.copyTerminalFlags(expression);
-
-            return discard;
+    private boolean isEvalResultAssignment(final Node expression) {
+        Node e = expression;
+        if (e.tokenType() == TokenType.DISCARD) {
+            e = ((UnaryNode)expression).rhs();
         }
-
-        return expression;
+        final Node resultNode = getCurrentFunctionNode().getResultNode();
+        return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode);
     }
 
     /**
-     * ExecuteNodes are needed to actually generate code, with a few exceptions
-     * such as ReturnNodes and ThrowNodes and various control flow that can
-     * standalone. Every other kind of statement needs to be wrapped in an
-     * ExecuteNode in order to become code
-     *
-     * @param executeNode  the execute node to visit
+     * Prepare special function nodes.
+     * TODO : only create those that are needed.
+     * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant
      */
-    @Override
-    public Node leave(final ExecuteNode executeNode) {
-
-        Node expression = executeNode.getExpression();
-
-        /*
-         * Handle the eval result for scripts. Every statement has to write its
-         * return value to a special variable that is the result of the script.
-         */
-        if (getCurrentFunctionNode().isScript() && !(expression instanceof Block) && !isEvalResultAssignment(expression) && !isInternalExpression(expression)) {
-            final Node resultNode = getCurrentFunctionNode().getResultNode();
-            expression = new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN), resultNode, convert(expression, resultNode.getType()));
-
-            getCurrentFunctionNode().newTemporary(Type.OBJECT, expression);
-        }
-
-        expression = discard(expression);
-        executeNode.setExpression(expression);
-        executeNode.copyTerminalFlags(expression);
-
-        statements.add(executeNode);
-
-        return executeNode;
-    }
-
-    /**
-     * Helper that given a loop body makes sure that it is not terminal if it
-     * has a continue that leads to the loop header or to outer loops' loop
-     * headers. This means that, even if the body ends with a terminal
-     * statement, we cannot tag it as terminal
-     *
-     * @param loopBody the loop body to check
-     */
-    private boolean controlFlowEscapes(final Node loopBody) {
-        final List<Node> escapes = new ArrayList<>();
-
-        loopBody.accept(new NodeVisitor() {
-            @Override
-            public Node leave(final BreakNode node) {
-                escapes.add(node);
-                return node;
-            }
-
-            @Override
-            public Node leave(final ContinueNode node) {
-                // all inner loops have been popped.
-                if (nesting.contains(node.getTargetNode())) {
-                    escapes.add(node);
-                }
-                return node;
-            }
-        });
-
-        return !escapes.isEmpty();
-    }
-
-    @Override
-    public Node enter(final ForNode forNode) {
-        // push the loop to the loop context
-        nest(forNode);
-        return forNode;
-    }
-
-    private static boolean conservativeAlwaysTrue(final Node node) {
-        return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue()));
-    }
-
-    @Override
-    public Node leave(final ForNode forNode) {
-        final Node init   = forNode.getInit();
-        final Node test   = forNode.getTest();
-        final Node modify = forNode.getModify();
-
-        if (forNode.isForIn()) {
-            final String name = compiler.uniqueName(ITERATOR_PREFIX.tag());
-            // DEFINE SYMBOL: may be any type, not local var
-            final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
-            iter.setType(Type.OBJECT); // NASHORN-73
-            forNode.setIterator(iter);
-            forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
-
-            /*
-             * Iterators return objects, so we need to widen the scope of the
-             * init variable if it, for example, has been assigned double type
-             * see NASHORN-50
-             */
-            forNode.getInit().getSymbol().setType(Type.OBJECT);
-        } else {
-            /* Normal for node, not for in */
-
-            if (init != null) {
-                forNode.setInit(discard(init));
-            }
-
-            if (test != null) {
-                forNode.setTest(convert(test, Type.BOOLEAN));
-            } else {
-                forNode.setHasGoto();
-            }
-
-            if (modify != null) {
-                forNode.setModify(discard(modify));
-            }
-        }
-
-        final Block body = forNode.getBody();
-
-        final boolean escapes = controlFlowEscapes(body);
-        if (escapes) {
-            body.setIsTerminal(false);
-        }
-
-        // pop the loop from the loop context
-        unnest();
-
-        if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
-            forNode.setTest(null);
-            forNode.setIsTerminal(!escapes);
-        }
-
-        statements.add(forNode);
-
-        return forNode;
-    }
-
-    /**
-     * Initialize this symbol and variable for function node
-     *
-     * @param functionNode the function node
-     */
-    private void initThis(final FunctionNode functionNode) {
-        final long token = functionNode.getToken();
-        final int finish = functionNode.getFinish();
-
-        final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null);
-        final IdentNode thisNode = new IdentNode(source, token, finish, thisSymbol.getName());
-
-        thisSymbol.setType(Type.OBJECT);
-        thisSymbol.setNeedsSlot(true);
-
-        thisNode.setSymbol(thisSymbol);
-
-        functionNode.setThisNode(thisNode);
-    }
-
-    /**
-     * Initialize scope symbol and variable for function node
-     *
-     * @param functionNode the function node
-     */
-    private void initScope(final FunctionNode functionNode) {
-        final long token = functionNode.getToken();
-        final int finish = functionNode.getFinish();
-
-        final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
-        final IdentNode scopeNode = new IdentNode(source, token, finish, scopeSymbol.getName());
-
-        scopeSymbol.setType(ScriptObject.class);
-        scopeSymbol.setNeedsSlot(true);
-
-        scopeNode.setSymbol(scopeSymbol);
-
-        functionNode.setScopeNode(scopeNode);
-    }
-
-    /**
-     * Initialize return symbol and variable for function node
-     *
-     * @param functionNode the function node
-     */
-    private void initReturn(final FunctionNode functionNode) {
-        final long token = functionNode.getToken();
-        final int finish = functionNode.getFinish();
-
-        final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
-        final IdentNode returnNode = new IdentNode(source, token, finish, returnSymbol.getName());
-
-        returnSymbol.setType(Object.class);
-        returnSymbol.setNeedsSlot(true);
-
-        returnNode.setSymbol(returnSymbol);
-
-        functionNode.setResultNode(returnNode);
-    }
-
-    /**
-     * Initialize varargs for function node, if applicable
-     *
-     * @param functionNode
-     */
-    private void initVarArg(final FunctionNode functionNode) {
+    private void initFunctionNode(final FunctionNode functionNode) {
         final long token  = functionNode.getToken();
         final int  finish = functionNode.getFinish();
 
-        assert functionNode.getCalleeNode() != null;
+        LOG.info("Init this, scope, result, callee, varargs and argument for " + functionNode.getName());
 
-        final Symbol    varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
-        final IdentNode varArgsNode   = new IdentNode(source, token, finish, varArgsSymbol.getName());
-
-        varArgsSymbol.setType(Type.OBJECT_ARRAY);
-        varArgsSymbol.setNeedsSlot(true);
-
-        varArgsNode.setSymbol(varArgsSymbol);
-
-        functionNode.setVarArgsNode(varArgsNode);
-
-        final String    argumentsName    = ARGUMENTS.tag();
-        final String    name             = functionNode.hideArguments() ? functionNode.uniqueName("$" + argumentsName) : argumentsName;
-        final Symbol    argumentsSymbol  = functionNode.defineSymbol(name, IS_PARAM | IS_INTERNAL, null);
-        final IdentNode argumentsNode    = new IdentNode(source, token, finish, argumentsSymbol.getName());
-
-        argumentsSymbol.setType(Type.OBJECT);
-        argumentsSymbol.setNeedsSlot(true);
-
-        argumentsNode.setSymbol(argumentsSymbol);
-
-        functionNode.setArgumentsNode(argumentsNode);
+        functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
+        functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
+        functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
+        functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
+        functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
+        functionNode.setArgumentsNode(new IdentNode(source, token, finish, functionNode.hideArguments() ? compiler.uniqueName('$' + ARGUMENTS.tag()) : ARGUMENTS.tag()));
     }
 
-    private void initCallee(final FunctionNode functionNode) {
-        if (functionNode.getCalleeNode() != null) {
-            return;
-        }
+}
 
-        final long token = functionNode.getToken();
-        final int finish = functionNode.getFinish();
 
-        final Symbol    calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
-        final IdentNode calleeNode   = new IdentNode(source, token, finish, calleeSymbol.getName());
 
-        calleeSymbol.setType(ScriptFunction.class);
-        calleeSymbol.setNeedsSlot(true);
-
-        calleeNode.setSymbol(calleeSymbol);
-
-        functionNode.setCalleeNode(calleeNode);
-    }
-
-    /**
-     * Initialize parameters for function node. This may require specializing
-     * types if a specialization profile is known
-     *
-     * @param functionNode the function node
-     */
-    private void initParameters(final FunctionNode functionNode) {
-        /*
-         * If a function is specialized, we don't need to tag either it return
-         * type or its parameters with the widest (OBJECT) type for safety.
-         */
-        functionNode.setReturnType(Type.UNKNOWN);
-
-        for (final IdentNode ident : functionNode.getParameters()) {
-            localDefs.add(ident.getName());
-            final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident);
-            if (paramSymbol != null) {
-                paramSymbol.setType(Type.UNKNOWN);
-            }
-        }
-    }
-
-    /**
-     * This has to run before fix assignment types, store any type specializations for
-     * paramters, then turn then to objects for the generic version of this method
-     *
-     * @param functionNode functionNode
-     */
-    private static void fixParameters(final FunctionNode functionNode) {
-        boolean nonObjectParams = false;
-        List<Type> paramSpecializations = new ArrayList<>();
-
-        for (final IdentNode ident : functionNode.getParameters()) {
-            final Symbol paramSymbol = ident.getSymbol();
-            if (paramSymbol != null) {
-                Type type = paramSymbol.getSymbolType();
-                if (type.isUnknown()) {
-                    type = Type.OBJECT;
-                }
-                paramSpecializations.add(type);
-                if (!type.isObject()) {
-                    nonObjectParams = true;
-                }
-                paramSymbol.setType(Type.OBJECT);
-            }
-        }
-
-        if (!nonObjectParams) {
-            paramSpecializations = null;
-            /*
-             * Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters
-             * here. If the callee has parameter specializations, we can regenerate it with those particular types for speed.
-             */
-        } else {
-            LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations);
-        }
-
-        // parameters should not be slots for a vararg function, make sure this is the case
-        if (functionNode.isVarArg()) {
-            for (final IdentNode param : functionNode.getParameters()) {
-                param.getSymbol().setNeedsSlot(false);
-            }
-        }
-    }
-
-    private LiteralNode<Undefined> undefined() {
-        return LiteralNode.newInstance(source, 0, 0, UNDEFINED);
-    }
-
-    private void guaranteeReturn(final FunctionNode functionNode) {
-        Node resultNode;
-
-        if (functionNode.isScript()) {
-            resultNode = functionNode.getResultNode();
-        } else {
-            final Node lastStatement = Node.lastStatement(functionNode.getStatements());
-            if (lastStatement != null && (lastStatement.isTerminal() || lastStatement instanceof ReturnNode)) {
-                return; // return already in place, as it should be for a non-undefined returning function
-            }
-            resultNode = undefined().accept(this);
-        }
-
-        new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null).accept(this);
-
-        functionNode.setReturnType(Type.OBJECT);
-    }
-
-    /**
-     * Fix return types for a node. The return type is the widest of all known
-     * return expressions in the function, and has to be the same for all return
-     * nodes, thus we need to traverse all of them in case some of them must be
-     * upcast
-     *
-     * @param node function node to scan return types for
-     */
-    private void fixReturnTypes(final FunctionNode node) {
-
-        if (node.getReturnType().isUnknown()) {
-            node.setReturnType(Type.OBJECT); // for example, infinite loops
-        }
-
-        node.accept(new NodeVisitor() {
-            @Override
-            public Node enter(final FunctionNode subFunction) {
-                //leave subfunctions alone. their return values are already specialized and should not
-                //be touched.
-                return null;
-            }
-            @Override
-            public Node leave(final ReturnNode returnNode) {
-                if (returnNode.hasExpression()) {
-                    returnNode.setExpression(convert(returnNode.getExpression(), node.getReturnType()));
-                }
-                return returnNode;
-            }
-        });
-    }
-
-    /**
-     * Augment assignments with casts after traversing a function node.
-     * Assignment nodes are special, as they require entire scope for type
-     * inference.
-     *
-     * Consider e.g.
-     *
-     * var x = 18.5; //double
-     * for (...) {
-     *      var y = x; //initially y is inferred to be double y = func(x) //y changes to object, but above assignment is unaware that it needs to be var y = (object)x instaed
-     */
-
-    private void fixAssignmentTypes(final FunctionNode node, final List<Symbol> declaredSymbols) {
-
-        // Make sure all unknown symbols are OBJECT types now. No UNKNOWN may survive lower.
-        for (final Symbol symbol : declaredSymbols) {
-            if (symbol.getSymbolType().isUnknown()) {
-                LOG.finest("fixAssignmentTypes: widening " + symbol + " to object " + symbol + " in " + getCurrentFunctionNode());
-                symbol.setType(Type.OBJECT);
-                symbol.setCanBeUndefined();
-            }
-
-            if (symbol.canBeUndefined()) {
-                /*
-                 * Ideally we'd like to only widen to the narrowest
-                 * possible type that supports local variables, i.e. doubles for
-                 * numerics, but
-                 *
-                 * var x;
-                 *
-                 * if (x !== undefined) do something
-                 *
-                 * x++;
-                 *
-                 * Screws this up, as x is determined to be a double even though
-                 * the use is undefined This can be fixed with better use
-                 * analysis in the IdentNode visitor.
-                 *
-                 * This actually seems to be superseded with calls to ensureTypeNotUnknown
-                 */
-                if (!Type.areEquivalent(symbol.getSymbolType(), Type.OBJECT)) {
-                    debug(symbol + " in " + getCurrentFunctionNode() + " can be undefined. Widening to object");
-                    symbol.setType(Type.OBJECT);
-                }
-            }
-        }
-
-        node.accept(new NodeVisitor() {
-
-            private void fix(final Assignment<? extends Node> assignment) {
-                final Node src  = assignment.getAssignmentSource();
-                final Node dest = assignment.getAssignmentDest();
-
-                if (src == null) {
-                    return;
-                }
-
-                //we don't know about scope yet so this is too conservative. AccessSpecialized will remove casts that are not required
-
-                final Type srcType  = src.getType();
-                Type destType = dest.getType();
-
-                //we can only narrow an operation type if the variable doesn't have a slot
-                if (!dest.getSymbol().hasSlot() && !node.hasDeepWithOrEval()) {
-                    destType = Type.narrowest(((Node)assignment).getWidestOperationType(), destType);
-                }
-
-                if (!Type.areEquivalent(destType, srcType)) {
-                    LOG.finest("fixAssignment " + assignment + " type = " + src.getType() + "=>" + dest.getType());
-                    assignment.setAssignmentSource(convert(src, destType));
-                }
-            }
-
-            private Node checkAssignment(final Node assignNode) {
-                if (!assignNode.isAssignment()) {
-                    return assignNode;
-                }
-                fix((Assignment<?>)assignNode);
-                return assignNode;
-            }
-
-
-            @Override
-            public Node leave(final UnaryNode unaryNode) {
-                return checkAssignment(unaryNode);
-            }
-
-            @Override
-            public Node leave(final BinaryNode binaryNode) {
-                return checkAssignment(binaryNode);
-            }
-
-            @Override
-            public Node leave(final VarNode varNode) {
-                return checkAssignment(varNode);
-            }
-        });
-    }
-
-    /*
-     * For a script, add scope symbols as defined in the property map
-     */
-    private static void initFromPropertyMap(final Context context, final FunctionNode functionNode) {
-        assert functionNode.isScript();
-
-        final PropertyMap map = context.getGlobalMap();
-
-        for (final Property property : map.getProperties()) {
-            final String key = property.getKey();
-            functionNode.defineSymbol(key, IS_GLOBAL, null).setType(Type.OBJECT);
-        }
-    }
-
-    @Override
-    public Node enter(final FunctionNode functionNode) {
-        LOG.info("START FunctionNode: " + functionNode.getName());
-
-        Node initialEvalResult = undefined();
-
-        nest(functionNode);
-
-        localDefs = new HashSet<>();
-        localUses = new HashSet<>();
-
-        functionNode.setFrame(functionNode.pushFrame());
-
-        initThis(functionNode);
-        initCallee(functionNode);
-        if (functionNode.isVarArg()) {
-            initVarArg(functionNode);
-        }
-
-        initParameters(functionNode);
-        initScope(functionNode);
-        initReturn(functionNode);
-
-        /*
-         * Add all nested functions as symbols in this function
-         */
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-            final IdentNode ident = nestedFunction.getIdent();
-
-            // for initial eval result is the last declared function
-            if (ident != null && nestedFunction.isStatement()) {
-                final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction);
-
-                functionSymbol.setType(ScriptFunction.class);
-                initialEvalResult = new IdentNode(ident);
-            }
-        }
-
-        if (functionNode.isScript()) {
-            initFromPropertyMap(compiler.getContext(), functionNode);
-        }
-
-        // Add function name as local symbol
-        if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) {
-            final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode);
-            selfSymbol.setType(Type.OBJECT);
-            selfSymbol.setNode(functionNode);
-        }
-
-        /*
-         * As we are evaluating a nested structure, we need to store the
-         * statement list for the surrounding block and restore it when the
-         * function is done
-         */
-        final List<Node> savedStatements = statements;
-        statements = new ArrayList<>();
-
-        /*
-         * This pushes all declarations (except for non-statements, i.e. for
-         * node temporaries) to the top of the function scope. This way we can
-         * get around problems like
-         *
-         * while (true) {
-         *   break;
-         *   if (true) {
-         *     var s;
-         *   }
-         * }
-         *
-         * to an arbitrary nesting depth.
-         *
-         * @see NASHORN-73
-         */
-
-        final List<Symbol> declaredSymbols = new ArrayList<>();
-        for (final VarNode decl : functionNode.getDeclarations()) {
-            final IdentNode ident = decl.getName();
-            // any declared symbols that aren't visited need to be typed as
-            // well, hence the list
-            declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident)));
-        }
-
-        declaredSymbolsLocal = new ArrayList<>();
-
-        /*
-         * Main function code lowering
-         */
-        try {
-            /*
-             * Every nested function needs a definition in the outer function
-             * with its name. Add these.
-             */
-            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-                final VarNode varNode = nestedFunction.getFunctionVarNode();
-                if (varNode != null) {
-                    final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode();
-                    if (lineNumberNode != null) {
-                        lineNumberNode.accept(this);
-                    }
-                    varNode.accept(this);
-                    varNode.setIsFunctionVarNode();
-                }
-            }
-
-            if (functionNode.isScript()) {
-                new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
-            }
-
-            /*
-             * Now we do the statements. This fills the block with code
-             */
-            for (final Node statement : functionNode.getStatements()) {
-                statement.accept(this);
-
-                final Node lastStatement = Node.lastStatement(statements);
-                if (lastStatement != null && lastStatement.hasTerminalFlags()) {
-                    functionNode.copyTerminalFlags(lastStatement);
-                    break;
-                }
-            }
-
-            functionNode.setStatements(statements);
-
-            /*
-             * If there are unusedterminated enpoints in the function, we need
-             * to add a "return undefined" in those places for correct semantics
-             */
-            if (!functionNode.isTerminal()) {
-                guaranteeReturn(functionNode);
-            }
-
-            /*
-             * Emit all nested functions
-             */
-            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-                nestedFunction.accept(this);
-            }
-
-            fixReturnTypes(functionNode);
-
-            if (functionNode.needsSelfSymbol()) {
-                final IdentNode selfSlotNode = new IdentNode(functionNode.getIdent());
-                selfSlotNode.setSymbol(functionNode.findSymbol(functionNode.getIdent().getName()));
-                final Node functionRef = new IdentNode(functionNode.getCalleeNode());
-                statements.add(0, new VarNode(source, functionNode.getToken(), functionNode.getFinish(), selfSlotNode, functionRef, false).accept(this));
-            }
-        } finally {
-            /*
-             * Restore statement for outer block that we were working on
-             */
-            statements = savedStatements;
-        }
-
-        unnest();
-
-        fixParameters(functionNode);
-
-        /*
-         * Fix assignment issues. assignments are troublesome as they can be on
-         * the form "dest = dest op source" where the type is different for the
-         * two dest:s: right now this is a post pass that handles putting casts
-         * in the correct places, but it can be implemented as a bottom up AST
-         * traversal on the fly. TODO.
-         */
-        fixAssignmentTypes(functionNode, declaredSymbols);
-
-        /*
-         * Set state of function to lowered and pop the frame
-         */
-        functionNode.setIsLowered(true);
-        functionNode.popFrame();
-
-        LOG.info("END FunctionNode: " + functionNode.getName());
-
-        return null;
-    }
-
-    @Override
-    public Node enter(final IdentNode identNode) {
-        final String name = identNode.getName();
-
-        if (identNode.isPropertyName()) {
-            // assign a pseudo symbol to property name
-            identNode.setSymbol(new Symbol(name, 0, Type.OBJECT));
-            return null;
-        }
-
-        final Block block = getCurrentBlock();
-        final Symbol oldSymbol = identNode.getSymbol();
-        Symbol symbol = block.findSymbol(name);
-
-        /*
-         * If an existing symbol with the name is found, use that otherwise,
-         * declare a new one
-         */
-
-        if (symbol != null) {
-            if (isFunctionExpressionSelfReference(symbol)) {
-                ((FunctionNode) symbol.getNode()).setNeedsSelfSymbol();
-            }
-
-            if (!identNode.isInitializedHere()) { // NASHORN-448
-                // here is a use outside the local def scope
-                if (!localDefs.contains(name)) {
-                    symbol.setType(Type.OBJECT);
-                    symbol.setCanBeUndefined();
-                }
-            }
-
-            identNode.setSymbol(symbol);
-            if (!getCurrentFunctionNode().isLocal(symbol)) {
-                // non-local: we need to put symbol in scope (if it isn't already)
-                if (!symbol.isScope()) {
-                    final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction());
-                    for (final Block lookupBlock : lookupBlocks) {
-                        final Symbol refSymbol = lookupBlock.findSymbol(name);
-                        if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f()
-                            LOG.finest("Found a ref symbol that must be scope " + refSymbol);
-                            refSymbol.setIsScope();
-                        }
-                    }
-                }
-            }
-        } else {
-            symbol = block.useSymbol(name, identNode);
-            // we have never seen this before, it can be undefined
-            symbol.setType(Type.OBJECT); // TODO unknown -we have explicit casts anyway?
-            symbol.setCanBeUndefined();
-        }
-
-        if (symbol != oldSymbol && !identNode.isInitializedHere()) {
-            symbol.increaseUseCount();
-        }
-        localUses.add(identNode.getName());
-
-        return null;
-    }
-
-    private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) {
-        if (currentFunction.findParentFunction() == topFunction) {
-            final List<Block> blocks = new LinkedList<>();
-
-            blocks.add(currentFunction.getParent());
-            blocks.addAll(currentFunction.getReferencingParentBlocks());
-            return blocks;
-        }
-        /**
-         * assumption: all parent blocks of an inner function will always be in the same outer function;
-         * therefore we can simply skip through intermediate functions.
-         * @see FunctionNode#addReferencingParentBlock(Block)
-         */
-        return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction);
-    }
-
-    private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
-        if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
-            return ((FunctionNode) symbol.getNode()).getIdent().getName().equals(symbol.getName());
-        }
-        return false;
-    }
-
-    @Override
-    public Node enter(final IfNode ifNode) {
-        nest(ifNode);
-        return ifNode;
-    }
-
-    @Override
-    public Node leave(final IfNode ifNode) {
-
-        final Node test = convert(ifNode.getTest(), Type.BOOLEAN);
-
-        /*
-         * constant if checks should short circuit into just one of the
-         * pass/fail blocks
-         */
-        if (test.getSymbol().isConstant()) {
-            assert test instanceof LiteralNode<?>;
-            final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail();
-            if (shortCut != null) {
-                for (final Node statement : shortCut.getStatements()) {
-                    statements.add(statement);
-                }
-            }
-            return null;
-        }
-
-        final Node pass = ifNode.getPass();
-        final Node fail = ifNode.getFail();
-
-        if (pass.isTerminal() && fail != null && fail.isTerminal()) {
-            ifNode.setIsTerminal(true);
-        }
-
-        ifNode.setTest(test);
-        statements.add(ifNode);
-
-        unnest();
-
-        return ifNode;
-    }
-
-    @Override
-    public Node leave(final IndexNode indexNode) {
-        getCurrentFunctionNode().newTemporary(Type.OBJECT, indexNode);
-
-        return indexNode;
-    }
-
-    @Override
-    public Node enter(final LabelNode labeledNode) {
-        // Don't want a symbol for label.
-        final Block body = labeledNode.getBody();
-        body.accept(this);
-        labeledNode.copyTerminalFlags(body);
-        statements.add(labeledNode);
-
-        return null;
-    }
-
-    @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
-        statements.add(lineNumberNode);
-        return null;
-    }
-
-    /**
-     * Generate a new literal symbol.
-     *
-     * @param literalNode LiteralNode needing symbol.
-     * @return The literal node augmented with the symbol
-     */
-    private LiteralNode<?> newLiteral(final LiteralNode<?> literalNode) {
-        return newLiteral(getCurrentFunctionNode(), literalNode);
-    }
-
-    private static LiteralNode<?> newLiteral(final FunctionNode functionNode, final LiteralNode<?> literalNode) {
-        functionNode.newLiteral(literalNode);
-        return literalNode;
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Override
-    public Node enter(final LiteralNode literalNode) {
-        if (literalNode.isTokenType(TokenType.THIS)) {
-            literalNode.setSymbol(getCurrentFunctionNode().getThisNode().getSymbol());
-            return null;
-        }
-
-        if (literalNode instanceof ArrayLiteralNode) {
-            final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
-            final Node[] array = arrayLiteralNode.getValue();
-
-            for (int i = 0; i < array.length; i++) {
-                final Node element = array[i];
-                if (element != null) {
-                    array[i] = array[i].accept(this);
-                }
-            }
-
-            arrayLiteralNode.analyze();
-            final Type elementType = arrayLiteralNode.getElementType();
-
-            // we only have types after all elements are accepted
-            for (int i = 0; i < array.length; i++) {
-                final Node element = array[i];
-                if (element != null) {
-                    array[i] = convert(element, elementType);
-                }
-            }
-        } else {
-            final Object value = literalNode.getValue();
-
-            if (value instanceof Node) {
-                final Node node = ((Node)value).accept(this);
-                if (node != null) {
-                    return newLiteral(LiteralNode.newInstance(source, node.getToken(), node.getFinish(), node));
-                }
-            }
-        }
-
-        newLiteral(literalNode);
-
-        return null;
-    }
-
-    @Override
-    public Node leave(final ObjectNode objectNode) {
-        getCurrentFunctionNode().newTemporary(Type.OBJECT, objectNode);
-        return objectNode;
-    }
-
-    @Override
-    public Node enter(final PropertyNode propertyNode) {
-        // assign a pseudo symbol to property name, see NASHORN-710
-        propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
-        return propertyNode;
-    }
-
-    @Override
-    public Node enter(final ReferenceNode referenceNode) {
-        final FunctionNode functionNode = referenceNode.getReference();
-        if (functionNode != null) {
-            functionNode.addReferencingParentBlock(getCurrentBlock());
-        }
-        return referenceNode;
-    }
-
-    @Override
-    public Node leave(final ReferenceNode referenceNode) {
-        getCurrentFunctionNode().newTemporary(Type.OBJECT, referenceNode);
-        return referenceNode;
-    }
-
-    /**
-     * For each return node we need to set a return expression of the correct
-     * type. In the non-specializing world, we can always upcast to object and
-     * ignore the rest, but in the specializing world we have to keep track of
-     * the widest return type so far, which is the common one for this method.
-     *
-     * @param returnNode  the return node we are generating
-     * @param expression  the expression to return from this code
-     */
-    private void setReturnExpression(final ReturnNode returnNode, final Node expression) {
-        final FunctionNode functionNode = getCurrentFunctionNode();
-
-        returnNode.setExpression(expression);
-
-        final Symbol symbol = expression.getSymbol();
-        if (expression.getType().isUnknown() && symbol.isParam()) {
-            symbol.setType(Type.OBJECT);
-        }
-        functionNode.setReturnType(Type.widest(expression.getType(), functionNode.getReturnType()));
-    }
-
-    @Override
-    public Node enter(final ReturnNode returnNode) {
-        final TryNode tryNode = returnNode.getTryChain();
-        final Node expression = returnNode.getExpression();
-
-        if (tryNode != null) {
-            if (expression != null) {
-                final long token = returnNode.getToken();
-                final Node resultNode = getCurrentFunctionNode().getResultNode();
-                new ExecuteNode(source, token, Token.descPosition(token), new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expression)).accept(this);
-
-                if (copyFinally(tryNode, null)) {
-                    return null;
-                }
-
-                setReturnExpression(returnNode, resultNode);
-            } else if (copyFinally(tryNode, null)) {
-                return null;
-            }
-        } else if (expression != null) {
-            setReturnExpression(returnNode, expression.accept(this));
-        }
-
-        statements.add(returnNode);
-
-        return null;
-    }
-
-    @Override
-    public Node leave(final RuntimeNode runtimeNode) {
-        for (final Node arg : runtimeNode.getArgs()) {
-            ensureTypeNotUnknown(arg);
-        }
-
-        getCurrentFunctionNode().newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
-
-        return runtimeNode;
-    }
-
-    @Override
-    public Node enter(final SwitchNode switchNode) {
-        nest(switchNode);
-        return switchNode;
-    }
-
-    @Override
-    public Node leave(final SwitchNode switchNode) {
-        unnest();
-
-        final Node           expression  = switchNode.getExpression();
-        final List<CaseNode> cases       = switchNode.getCases();
-        final CaseNode       defaultCase = switchNode.getDefaultCase();
-        final boolean        hasDefault  = defaultCase != null;
-        final int            n           =  cases.size() + (hasDefault ? -1 : 0);
-
-        boolean allTerminal = !cases.isEmpty();
-        boolean allInteger  = n > 1;
-
-        for (final CaseNode caseNode : cases) {
-            allTerminal &= caseNode.isTerminal();
-            final Node test = caseNode.getTest();
-
-            // Skip default.
-            if (test == null) {
-                continue;
-            }
-
-            // If not a number.
-            if (!(test instanceof LiteralNode) || !test.getType().isNumeric()) {
-                allInteger = false;
-                continue;
-            }
-
-            final LiteralNode<?> testLiteral = (LiteralNode<?>)test;
-
-            // If a potential integer.
-            if (!(testLiteral.getValue() instanceof Integer)) {
-                // If not an integer value.
-                if (!JSType.isRepresentableAsInt(testLiteral.getNumber())) {
-                    allInteger = false;
-                    continue;
-                }
-
-                // Guarantee all case literals are Integers (simplifies sorting in code gen.)
-                caseNode.setTest(newLiteral(LiteralNode.newInstance(source, testLiteral.getToken(), testLiteral.getFinish(), testLiteral.getInt32())));
-            }
-        }
-
-        if (allTerminal && defaultCase != null && defaultCase.isTerminal()) {
-            switchNode.setIsTerminal(true);
-        }
-
-        if (!allInteger) {
-            switchNode.setExpression(convert(expression, Type.OBJECT));
-
-            for (final CaseNode caseNode : cases) {
-                final Node test = caseNode.getTest();
-
-                if (test != null) {
-                    caseNode.setTest(convert(test, Type.OBJECT));
-                }
-            }
-        }
-
-        final String name = compiler.uniqueName(SWITCH_TAG_PREFIX.tag());
-        final Symbol tag  = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
-
-        tag.setType(allInteger ? Type.INT : Type.OBJECT);
-        switchNode.setTag(tag);
-
-        statements.add(switchNode);
-
-        return switchNode;
-    }
-
-    @Override
-    public Node leave(final ThrowNode throwNode) {
-        throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
-        statements.add(throwNode);
-
-        return throwNode;
-    }
-
-    @Override
-    public Node enter(final TryNode tryNode) {
-        final Block  finallyBody   = tryNode.getFinallyBody();
-        final Source tryNodeSource = tryNode.getSource();
-        final long   token         = tryNode.getToken();
-        final int    finish        = tryNode.getFinish();
-
-        nest(tryNode);
-
-        if (finallyBody == null) {
-            return tryNode;
-        }
-
-        /**
-         * We have a finally clause.
-         *
-         * Transform to do finally tail duplication as follows:
-         *
-         * <pre>
-         *  try {
-         *    try_body
-         *  } catch e1 {
-         *    catchbody_1
-         *  }
-         *  ...
-         *  } catch en {
-         *    catchbody_n
-         *  } finally {
-         *    finally_body
-         *  }
-         *
-         *  (where e1 ... en are optional)
-         *
-         *  turns into
-         *
-         *  try {
-         *    try {
-         *      try_body
-         *    } catch e1 {
-         *      catchbody1
-         *      //nothing inlined explicitly here, return, break other
-         *      //terminals may inline the finally body
-         *      ...
-         *    } catch en {
-         *      catchbody2
-         *      //nothing inlined explicitly here, return, break other
-         *      //terminals may inline the finally body
-         *    }
-         *  } catch all ex {
-         *      finally_body_inlined
-         *      rethrow ex
-         *  }
-         *  finally_body_inlined
-         * </pre>
-         *
-         * If tries are catches are terminal, visitors for return, break &
-         * continue will handle the tail duplications. Throw needs to be
-         * treated specially with the catchall as described in the above
-         * ASCII art.
-         *
-         * If the try isn't terminal we do the finally_body_inlined at the
-         * end. If the try is terminated with continue/break/return the
-         * existing visitor logic will inline the finally before that
-         * operation. if the try is terminated with a throw, the catches e1
-         * ... en will have a chance to process the exception. If the
-         * appropriate catch e1..en is non terminal we fall through to the
-         * last finally_body_inlined. if the catch e1...en IS terminal with
-         * continue/break/return existing visitor logic will fix it. If they
-         * are terminal with another throw it goes to the catchall and the
-         * finally_body_inlined marked (*) will fix it before rethrowing
-         * whatever problem there was for identical semantic.
-         */
-
-        // if try node does not contain a catch we can skip creation of a new
-        // try node and just append our synthetic catch to the existing try node.
-        if (!tryNode.getCatchBlocks().isEmpty()) {
-            // insert an intermediate try-catch* node, where we move the body and all catch blocks.
-            // the original try node become a try-finally container for the new try-catch* node.
-            // because we don't clone (to avoid deep copy), we have to fix the block chain in the end.
-            final TryNode innerTryNode = new TryNode(tryNodeSource, token, finish, tryNode.getNext());
-            innerTryNode.setBody(tryNode.getBody());
-            innerTryNode.setCatchBlocks(tryNode.getCatchBlocks());
-
-            // set outer tryNode's body to innerTryNode
-            final Block outerBody = new Block(tryNodeSource, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode());
-            outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
-            tryNode.setBody(outerBody);
-            tryNode.setCatchBlocks(null);
-
-            // now before we go on, we have to fix the block parents
-            // (we repair the block tree after the insertion so that all references are intact)
-            innerTryNode.getBody().setParent(tryNode.getBody());
-            for (final Block block : innerTryNode.getCatchBlocks()) {
-                block.setParent(tryNode.getBody());
-            }
-        }
-
-        // create a catch-all that inlines finally and rethrows
-        final String name = compiler.uniqueName(EXCEPTION_PREFIX.tag());
-
-        final IdentNode exception = new IdentNode(tryNodeSource, token, finish, name);
-        exception.accept(this);
-
-        final Block catchBlock      = new Block(tryNodeSource, token, finish, getCurrentBlock(), getCurrentFunctionNode());
-        final Block catchBody       = new Block(tryNodeSource, token, finish, catchBlock, getCurrentFunctionNode());
-        final Node  catchAllFinally = finallyBody.clone();
-
-        catchBody.addStatement(new ExecuteNode(tryNodeSource, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
-        catchBody.setIsTerminal(true);
-
-        final CatchNode catchNode = new CatchNode(tryNodeSource, token, finish, new IdentNode(exception), null, catchBody);
-        catchNode.setIsSyntheticRethrow();
-        catchBlock.addStatement(catchNode);
-
-        // replace all catches of outer tryNode with the catch-all
-        tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock)));
-
-        /*
-         * We leave the finally block for the original try in place for now
-         * so that children visitations will work. It is removed and placed
-         * afterwards in the else case below, after all children are visited
-         */
-
-        return tryNode;
-    }
-
-    @Override
-    public Node leave(final TryNode tryNode) {
-        final Block finallyBody   = tryNode.getFinallyBody();
-
-        boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
-
-        for (final Block catchBlock : tryNode.getCatchBlocks()) {
-            allTerminal &= catchBlock.isTerminal();
-        }
-
-        tryNode.setIsTerminal(allTerminal);
-
-        final String name      = compiler.uniqueName(EXCEPTION_PREFIX.tag());
-        final Symbol exception = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
-
-        exception.setType(ECMAException.class);
-        tryNode.setException(exception);
-
-        statements.add(tryNode);
-
-        unnest();
-
-        // if finally body is present, place it after the tryNode
-        if (finallyBody != null) {
-            tryNode.setFinallyBody(null);
-            statements.add(finallyBody);
-        }
-
-        return tryNode;
-    }
-
-    @Override
-    public Node enter(final VarNode varNode) {
-        final IdentNode ident = varNode.getName();
-        final String    name  = ident.getName();
-
-        final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident);
-        assert symbol != null;
-
-        varNode.setSymbol(symbol);
-        declaredSymbolsLocal.add(symbol);
-
-        // NASHORN-467 - use before definition of vars - conservative
-        if (localUses.contains(ident.getName())) {
-            symbol.setType(Type.OBJECT);
-            symbol.setCanBeUndefined();
-        }
-
-        return varNode;
-    }
-
-    private static boolean isScopeOrGlobal(final Symbol symbol) {
-        return symbol.getBlock().getFunction().isScript(); //we can't do explicit isScope checks as early as lower, which is why AccessSpecializer is afterwards as well.
-    }
-
-    @Override
-    public Node leave(final VarNode varNode) {
-        final Node      init  = varNode.getInit();
-        final IdentNode ident = varNode.getName();
-        final String    name  = ident.getName();
-
-        if (init != null) {
-            localDefs.add(name);
-        }
-
-        if (varNode.shouldAppend()) {
-            statements.add(varNode);
-        }
-
-        if (init == null) {
-            // var x; with no init will be treated like a use of x by
-            // visit(IdentNode) unless we remove the name
-            // from the localdef list.
-            localDefs.remove(name);
-            return varNode;
-        }
-
-        final Symbol symbol = varNode.getSymbol();
-        if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScopeOrGlobal(symbol)) { //see NASHORN-56
-            // Forbid integers as local vars for now as we have no way to treat them as undefined
-            final Type type = Compiler.shouldUseIntegers() ? init.getType() : Type.NUMBER;
-            varNode.setInit(convert(init, type));
-            symbol.setType(type);
-        } else {
-            symbol.setType(Type.OBJECT); // var x = obj;, x is an "object" //TODO too conservative now?
-        }
-
-        return varNode;
-    }
-
-    @Override
-    public Node enter(final WhileNode whileNode) {
-        // push the loop to the loop context
-        nest(whileNode);
-
-        return whileNode;
-    }
-
-    @Override
-    public Node leave(final WhileNode whileNode) {
-        final Node test = whileNode.getTest();
-
-        if (test != null) {
-            whileNode.setTest(convert(test, Type.BOOLEAN));
-        } else {
-            whileNode.setHasGoto();
-        }
-
-        Node returnNode = whileNode;
-
-        final Block   body    = whileNode.getBody();
-        final boolean escapes = controlFlowEscapes(body);
-        if (escapes) {
-            body.setIsTerminal(false);
-        }
-
-        if (body.isTerminal()) {
-            if (whileNode instanceof DoWhileNode) {
-                whileNode.setIsTerminal(true);
-            } else if (conservativeAlwaysTrue(test)) {
-                returnNode = new ForNode(source, whileNode.getToken(), whileNode.getFinish());
-                ((ForNode)returnNode).setBody(body);
-                returnNode.setIsTerminal(!escapes);
-            }
-        }
-
-        // pop the loop from the loop context
-        unnest();
-        statements.add(returnNode);
-
-        return returnNode;
-    }
-
-    @Override
-    public Node leave(final WithNode withNode) {
-        withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
-
-        if (withNode.getBody().isTerminal()) {
-            withNode.setIsTerminal(true);
-        }
-
-        statements.add(withNode);
-
-        return withNode;
-    }
-
-    /*
-     * Unary visits.
-     */
-
-    /**
-     * Visit unary node and set symbols and inferred type
-     *
-     * @param unaryNode  unary node to visit
-     * @param type       The narrowest allowed type for this node
-     *
-     * @return the final node, or replacement thereof
-     */
-    public Node leaveUnary(final UnaryNode unaryNode, final Type type) {
-        /* Try to eliminate the unary node if the expression is constant */
-        final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
-        if (literalNode != null) {
-            return newLiteral(literalNode);
-        }
-
-        /* Add explicit conversion */
-        unaryNode.setRHS(convert(unaryNode.rhs(), type));
-        getCurrentFunctionNode().newTemporary(type, unaryNode);
-
-        return unaryNode;
-    }
-
-    @Override
-    public Node leaveADD(final UnaryNode unaryNode) {
-        return leaveUnary(unaryNode, Type.NUMBER);
-    }
-
-    @Override
-    public Node leaveBIT_NOT(final UnaryNode unaryNode) {
-        return leaveUnary(unaryNode, Type.INT);
-    }
-
-    @Override
-    public Node leaveCONVERT(final UnaryNode unaryNode) {
-        final Node rhs = unaryNode.rhs();
-
-        if (rhs.isTokenType(TokenType.CONVERT)) {
-            rhs.setSymbol(unaryNode.getSymbol());
-            return rhs;
-        }
-
-        return unaryNode;
-    }
-
-    /**
-     * In an assignment, recursively make sure that there are slots for
-     * everything that has to be laid out as temporary storage, which is the
-     * case if we are assign-op:ing a BaseNode subclass. This has to be
-     * recursive to handle things like multi dimensional arrays as lhs
-     *
-     * see NASHORN-258
-     *
-     * @param functionNode   the current function node (has to be passed as it changes in the visitor below)
-     * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes
-     */
-    private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) {
-
-        assignmentDest.accept(new NodeVisitor() {
-
-            @Override
-            public Node leave(final AccessNode accessNode) {
-                final Node base = accessNode.getBase();
-                if (base.getSymbol().isThis()) {
-                    functionNode.addThisProperty(accessNode.getProperty().getName(), accessNode);
-                }
-
-                return accessNode;
-            }
-
-            @Override
-            public Node leave(final IndexNode indexNode) {
-                final Node index = indexNode.getIndex();
-                index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant());
-
-                return indexNode;
-            }
-
-        });
-    }
-
-    @Override
-    public Node leaveDECINC(final UnaryNode unaryNode) {
-        // @see assignOffset
-        ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs());
-        final Type type = Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER;
-
-        unaryNode.rhs().getSymbol().setType(type);
-        getCurrentFunctionNode().newTemporary(type, unaryNode);
-
-        return unaryNode;
-    }
-
-    /**
-     * Helper class for wrapping a result
-     */
-    private abstract static class ResultNodeVisitor extends NodeVisitor {
-
-        private Node resultNode;
-
-        ResultNodeVisitor(final Node resultNode) {
-            this.resultNode = resultNode;
-        }
-
-        Node getResultNode() {
-            return resultNode;
-        }
-
-        void setResultNode(final Node resultNode) {
-            this.resultNode = resultNode;
-        }
-    }
-
-    @Override
-    public Node leaveDELETE(final UnaryNode unaryNode) {
-        final boolean              strictMode     = getCurrentFunctionNode().isStrictMode();
-        final Node                 rhs            = unaryNode.rhs();
-        final long                 token          = unaryNode.getToken();
-        final int                  finish         = unaryNode.getFinish();
-        final LiteralNode<Boolean> trueNode       = LiteralNode.newInstance(source, token, finish, true);
-        final LiteralNode<Boolean> strictFlagNode = LiteralNode.newInstance(source, token, finish, strictMode);
-
-        newLiteral(trueNode);
-        newLiteral(strictFlagNode);
-
-        final NodeVisitor  lower = this;
-        final FunctionNode currentFunctionNode = getCurrentFunctionNode();
-
-        final ResultNodeVisitor visitor = new ResultNodeVisitor(trueNode) {
-
-            private void initRuntimeNode(final RuntimeNode node) {
-                node.accept(lower);
-                currentFunctionNode.newTemporary(Type.BOOLEAN, unaryNode);
-                node.setSymbol(unaryNode.getSymbol());
-
-                setResultNode(node);
-            }
-
-            @Override
-            public Node enter(final IdentNode node) {
-                // If this is a declared variable or a function parameter, delete always fails (except for globals).
-                final boolean failDelete =
-                        strictMode ||
-                        node.getSymbol().isParam() ||
-                        (node.getSymbol().isVar() && !node.getSymbol().isTopLevel());
-
-                final Node literalNode =
-                    newLiteral(
-                        currentFunctionNode,
-                        LiteralNode.newInstance(
-                            source,
-                            node.getToken(),
-                            node.getFinish(),
-                            node.getName()));
-
-                if (failDelete) {
-                    if (THIS.tag().equals(node.getName())) {
-                        statements.add(new ExecuteNode(source, token, finish, discard(node)));
-                        currentFunctionNode.newTemporary(Type.BOOLEAN, trueNode);
-                        return null; //trueNode it is
-                    }
-                }
-
-                final List<Node> args = new ArrayList<>();
-                args.add(convert(currentFunctionNode.getScopeNode(), Type.OBJECT));
-                args.add(convert(literalNode, Type.OBJECT));
-                args.add(strictFlagNode);
-
-                initRuntimeNode(
-                    new RuntimeNode(
-                        source,
-                        token,
-                        finish,
-                        failDelete ? FAIL_DELETE : DELETE,
-                        args));
-
-                return null;
-            }
-
-            @Override
-            public Node enter(final AccessNode node) {
-                final IdentNode property = node.getProperty();
-
-                initRuntimeNode(
-                    new RuntimeNode(
-                        source,
-                        token,
-                        finish,
-                        DELETE,
-                        convert(node.getBase(), Type.OBJECT),
-                        convert(
-                            newLiteral(
-                                currentFunctionNode,
-                                LiteralNode.newInstance(
-                                    source,
-                                    property.getToken(),
-                                    property.getFinish(),
-                                    property.getName())),
-                            Type.OBJECT),
-                        strictFlagNode));
-
-                return null;
-            }
-
-            @Override
-            public Node enter(final IndexNode node) {
-                initRuntimeNode(
-                    new RuntimeNode(
-                        source,
-                        token,
-                        finish,
-                        DELETE,
-                        convert(node.getBase(), Type.OBJECT),
-                        convert(node.getIndex(), Type.OBJECT),
-                        strictFlagNode));
-
-                return null;
-            }
-
-            @Override
-            public Node enterDefault(final Node node) {
-                statements.add(new ExecuteNode(source, token, finish, discard(node)));
-                currentFunctionNode.newTemporary(Type.BOOLEAN, trueNode);
-                //just return trueNode which is the default
-                return null;
-            }
-        };
-
-        rhs.accept(visitor);
-
-        return visitor.getResultNode();
-    }
-
-    @Override
-    public Node enterNEW(final UnaryNode unaryNode) {
-        final CallNode callNode = (CallNode)unaryNode.rhs();
-
-        callNode.setIsNew();
-
-        final Node function = callNode.getFunction();
-        final Node markedFunction = markerFunction(function);
-
-        callNode.setFunction(markedFunction.accept(this));
-
-        acceptArgs(callNode);
-
-        getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode);
-
-        return null;
-    }
-
-    @Override
-    public Node leaveNOT(final UnaryNode unaryNode) {
-        return leaveUnary(unaryNode, Type.BOOLEAN);
-    }
-
-    /**
-     * Create a null literal
-     *
-     * @param parent node to inherit token from.
-     * @return the new null literal
-     */
-    private LiteralNode<?> newNullLiteral(final Node parent) {
-        return newLiteral(LiteralNode.newInstance(parent.getSource(), parent.getToken(), parent.getFinish()));
-    }
-
-    @Override
-    public Node leaveTYPEOF(final UnaryNode unaryNode) {
-        final Node rhs    = unaryNode.rhs();
-        final long token  = unaryNode.getToken();
-        final int  finish = unaryNode.getFinish();
-
-        RuntimeNode runtimeNode;
-
-        if (rhs instanceof IdentNode) {
-            final IdentNode ident = (IdentNode)rhs;
-
-            if (ident.getSymbol().isParam() || ident.getSymbol().isVar()) {
-                runtimeNode = new RuntimeNode(
-                    source,
-                    token,
-                    finish,
-                    TYPEOF,
-                    convert(
-                        rhs,
-                        Type.OBJECT),
-                    newNullLiteral(unaryNode));
-            } else {
-                runtimeNode = new RuntimeNode(
-                    source,
-                    token,
-                    finish,
-                    TYPEOF,
-                    convert(
-                        getCurrentFunctionNode().getScopeNode(),
-                        Type.OBJECT),
-                    convert(
-                        newLiteral(
-                            LiteralNode.newInstance(
-                                source,
-                                ident.getToken(),
-                                ident.getFinish(),
-                                ident.getName())),
-                    Type.OBJECT));
-            }
-        }  else {
-            runtimeNode = new RuntimeNode(
-                source,
-                token,
-                finish,
-                TYPEOF,
-                convert(
-                    rhs,
-                    Type.OBJECT),
-                newNullLiteral(unaryNode));
-        }
-
-        runtimeNode.accept(this);
-
-        getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode);
-        runtimeNode.setSymbol(unaryNode.getSymbol());
-
-        return runtimeNode;
-    }
-
-    @Override
-    public Node leaveSUB(final UnaryNode unaryNode) {
-        return leaveUnary(unaryNode, Type.NUMBER);
-    }
-
-    @Override
-    public Node leaveVOID(final UnaryNode unaryNode) {
-        final Node rhs = unaryNode.rhs();
-        getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode);
-
-        // Set up request.
-        final RuntimeNode runtimeNode = new RuntimeNode(source, unaryNode.getToken(), unaryNode.getFinish(), VOID, convert(rhs, Type.OBJECT));
-
-        // Use same symbol as unary node.
-        runtimeNode.setSymbol(unaryNode.getSymbol());
-
-        return runtimeNode;
-    }
-
-    /**
-     * Compute the narrowest possible type for a binaryNode, based on its
-     * contents.
-     *
-     * @param binaryNode the binary node
-     * @return the narrowest possible type
-     */
-    private static Type binaryType(final BinaryNode binaryNode) {
-        final Node lhs = binaryNode.lhs();
-        assert lhs.hasType();
-
-        final Node rhs = binaryNode.rhs();
-        assert rhs.hasType();
-
-        // actually bitwise assignments are ok for ints. TODO
-        switch (binaryNode.tokenType()) {
-        case ASSIGN_BIT_AND:
-        case ASSIGN_BIT_OR:
-        case ASSIGN_BIT_XOR:
-        case ASSIGN_SHL:
-        case ASSIGN_SAR:
-            return Compiler.shouldUseIntegers() ? Type.widest(lhs.getType(), rhs.getType(), Type.INT) : Type.INT;
-        case ASSIGN_SHR:
-            return Type.LONG;
-        case ASSIGN:
-            return Compiler.shouldUseIntegers() ? Type.widest(lhs.getType(), rhs.getType(), Type.NUMBER) : Type.NUMBER;
-        default:
-            return binaryArithType(lhs.getType(), rhs.getType());
-        }
-    }
-
-    private static Type binaryArithType(final Type lhsType, final Type rhsType) {
-         if (!Compiler.shouldUseIntegerArithmetic()) {
-             return Type.NUMBER;
-         }
-         return Type.widest(lhsType, rhsType, Type.NUMBER);
-     }
-
-    /**
-     * Visit binary node and set symbols and inferred type
-     *
-     * @param binaryNode unary node to visit
-     * @param type       The narrowest allowed type for this node
-     *
-     * @return the final node, or replacement thereof
-     */
-    private Node leaveBinary(final BinaryNode binaryNode, final Type type) {
-        return leaveBinary(binaryNode, type, type, type);
-    }
-
-    /**
-     * Visit a binary node, specifying types for rhs, rhs and destType.
-     *
-     * @param binaryNode the binary node
-     * @param destType destination type
-     * @param lhsType type for left hand side
-     * @param rhsType type for right hand side
-     *
-     * @return resulting binary node
-     */
-    private Node leaveBinary(final BinaryNode binaryNode, final Type destType, final Type lhsType, final Type rhsType) {
-        /* Attempt to turn this binary expression into a constant */
-        final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
-        if (literalNode != null) {
-            return newLiteral(literalNode);
-        }
-
-        if (lhsType != null) {
-            binaryNode.setLHS(convert(binaryNode.lhs(), lhsType));
-        }
-
-        if (rhsType != null) {
-            binaryNode.setRHS(convert(binaryNode.rhs(), rhsType));
-        }
-
-        getCurrentFunctionNode().newTemporary(destType, binaryNode);
-
-        return binaryNode;
-    }
-
-    /**
-     * Determine if the outcome of + operator is a string.
-     *
-     * @param node
-     *            Node to test.
-     * @return true if a string result.
-     */
-    private boolean isAddString(final Node node) {
-        if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) {
-            final BinaryNode binaryNode = (BinaryNode)node;
-            final Node lhs = binaryNode.lhs();
-            final Node rhs = binaryNode.rhs();
-
-            return isAddString(lhs) || isAddString(rhs);
-        }
-
-        return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).getObject() instanceof String;
-    }
-
-    /**
-     * Helper for creating a new runtime node from a parent node, inheriting its
-     * types and tokens
-     *
-     * @param parent   Parent node.
-     * @param args     Runtime request arguments.
-     * @param request  Runtime request type.
-     * @return New {@link RuntimeNode}.
-     */
-
-    private RuntimeNode newRuntime(final Node parent, final List<Node> args, final Request request) {
-        final RuntimeNode runtimeNode = new RuntimeNode(source, parent.getToken(), parent.getFinish(), request, args);
-        runtimeNode.setStart(parent.getStart());
-        runtimeNode.setFinish(parent.getFinish());
-
-        // Use same symbol as parent node.
-        runtimeNode.accept(this);
-        runtimeNode.setSymbol(parent.getSymbol());
-
-        return runtimeNode;
-    }
-
-    /**
-     * Helper for creating a new runtime node from a binary node
-     *
-     * @param binaryNode {@link RuntimeNode} expression.
-     * @param request    Runtime request type.
-     * @return New {@link RuntimeNode}.
-     */
-    private RuntimeNode newRuntime(final BinaryNode binaryNode, final Request request) {
-        return newRuntime(binaryNode, Arrays.asList(new Node[] { binaryNode.lhs(), binaryNode.rhs() }), request);
-    }
-
-    /**
-     * Add is a special binary, as it works not only on arithmetic, but for
-     * strings etc as well.
-     */
-    @Override
-    public Node leaveADD(final BinaryNode binaryNode) {
-        final Node lhs = binaryNode.lhs();
-        final Node rhs = binaryNode.rhs();
-
-        //parameters must be blown up to objects
-        ensureTypeNotUnknown(lhs);
-        ensureTypeNotUnknown(rhs);
-
-        if ((lhs.getType().isNumeric() || lhs.getType().isBoolean()) && (rhs.getType().isNumeric() || rhs.getType().isBoolean())) {
-            return leaveBinary(binaryNode, binaryType(binaryNode));
-        } else if (isAddString(binaryNode)) {
-            binaryNode.setLHS(convert(lhs, Type.OBJECT));
-            binaryNode.setRHS(convert(rhs, Type.OBJECT));
-            getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode);
-        } else {
-            getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode);
-            return newRuntime(binaryNode, ADD);
-        }
-
-        return binaryNode;
-    }
-
-    @Override
-    public Node leaveAND(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, Type.OBJECT, null, null);
-    }
-
-    /**
-     * This is a helper called before an assignment.
-     * @param binaryNode assignment node
-     */
-    private Node enterAssign(final BinaryNode binaryNode) {
-        final Node lhs = binaryNode.lhs();
-
-        if (!(lhs instanceof IdentNode)) {
-            return binaryNode;
-        }
-
-        final Block     block = getCurrentBlock();
-        final IdentNode ident = (IdentNode)lhs;
-        final String    name  = ident.getName();
-
-        Symbol symbol = getCurrentBlock().findSymbol(name);
-
-        if (symbol == null) {
-            symbol = block.defineSymbol(name, IS_GLOBAL, ident);
-            binaryNode.setSymbol(symbol);
-        } else if (!getCurrentFunctionNode().isLocal(symbol)) {
-            symbol.setIsScope();
-        }
-
-        localDefs.add(name);
-
-        return binaryNode;
-    }
-
-    /**
-     * This assign helper is called after an assignment, when all children of
-     * the assign has been processed. It fixes the types and recursively makes
-     * sure that everyhing has slots that should have them in the chain.
-     *
-     * @param binaryNode assignment node
-     * @param destType   destination type of assignment
-     */
-    private Node leaveAssign(final BinaryNode binaryNode, final Type destType) {
-        binaryNode.lhs().getSymbol().setType(destType); // lhs inherits dest type
-        getCurrentFunctionNode().newTemporary(destType, binaryNode); // as does destination
-
-        ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode.lhs());
-        return binaryNode;
-    }
-
-    @Override
-    public Node enterASSIGN(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN(final BinaryNode binaryNode) {
-        final Node lhs = binaryNode.lhs();
-        final Node rhs = binaryNode.rhs();
-
-        if (rhs.getType().isNumeric()) {
-            final Symbol lhsSymbol = lhs.getSymbol();
-            final Type lhsType = Type.widest(lhs.getType(), binaryType(binaryNode));
-
-            // for index nodes, we can set anything
-            lhsSymbol.setType(lhsType);
-            getCurrentFunctionNode().newTemporary(lhs.getType(), binaryNode);
-            if (!(lhs instanceof IndexNode)) {
-                binaryNode.setRHS(convert(rhs, lhsType));
-            }
-        } else {
-            // Force symbol to be object if not numeric assignment.
-            binaryNode.setRHS(convert(rhs, Type.OBJECT));
-            getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode);
-
-            if (lhs instanceof IdentNode) {
-                lhs.getSymbol().setType(Type.OBJECT);
-            }
-        }
-
-        if (lhs instanceof AccessNode) {
-            final Node baseNode = ((AccessNode)lhs).getBase();
-
-            if (baseNode.getSymbol().isThis()) {
-                final IdentNode property = ((AccessNode)lhs).getProperty();
-                getCurrentFunctionNode().addThisProperty(property.getName(), property);
-            }
-        }
-
-        return binaryNode;
-    }
-
-    @Override
-    public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
-        final Node lhs = binaryNode.lhs();
-        final Node rhs = binaryNode.rhs();
-        final boolean bothNumeric = lhs.getType().isNumeric() && rhs.getType().isNumeric();
-
-        /*
-         * In the case of bothNumeric,
-         * compute type for lhs += rhs. Assign type to lhs. Assign type to
-         * temporary for this node. Convert rhs from whatever it was to this
-         * type. Legacy wise dest has always been narrowed. It should
-         * actually only be the lhs that is the double, or other narrower
-         * type than OBJECT
-         */
-        return leaveAssign(binaryNode, bothNumeric ? binaryType(binaryNode) : Type.OBJECT);
-    }
-
-    @Override
-    public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
-        return enterAssign(binaryNode);
-    }
-
-    @Override
-    public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
-        return leaveAssign(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node leaveBIT_AND(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, Type.INT);
-    }
-
-    @Override
-    public Node leaveBIT_OR(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, Type.INT);
-    }
-
-    @Override
-    public Node leaveBIT_XOR(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, Type.INT);
-    }
-
-    @Override
-    public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
-        binaryNode.setLHS(discard(binaryNode.lhs()));
-        getCurrentFunctionNode().newTemporary(binaryNode.rhs().getType(), binaryNode);
-
-        return binaryNode;
-    }
-
-    @Override
-    public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
-        binaryNode.setRHS(discard(binaryNode.rhs()));
-        getCurrentFunctionNode().newTemporary(binaryNode.lhs().getType(), binaryNode);
-
-        return binaryNode;
-    }
-
-    @Override
-    public Node leaveDIV(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node leaveEQ(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, EQ);
-    }
-
-    @Override
-    public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, EQ_STRICT);
-    }
-
-    private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) {
-        final Node lhs = binaryNode.lhs();
-        final Node rhs = binaryNode.rhs();
-
-        /* Attempt to turn this comparison into a constant and collapse it */
-        final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
-        if (literalNode != null) {
-            return newLiteral(literalNode);
-        }
-
-        // another case where dest != source operand types always a boolean
-        getCurrentFunctionNode().newTemporary(request.getReturnType(), binaryNode);
-
-        ensureTypeNotUnknown(lhs);
-        ensureTypeNotUnknown(rhs);
-        final Type type = Type.widest(lhs.getType(), rhs.getType());
-
-        if (type.isObject()) {
-            return newRuntime(binaryNode, request);
-        }
-
-        if ((request.equals(EQ_STRICT) || request.equals(NE_STRICT)) && lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
-            // special case: number compared against boolean => never equal. must not convert!
-            final boolean              result     = request.equals(NE_STRICT);
-            final LiteralNode<Boolean> resultNode = LiteralNode.newInstance(source, 0, 0, result);
-            final boolean              canSkipLHS = (lhs instanceof LiteralNode);
-            final boolean              canSkipRHS = (rhs instanceof LiteralNode);
-
-            if (canSkipLHS && canSkipRHS) {
-                return resultNode.accept(this);
-            }
-
-            final Node argNode;
-
-            if (!canSkipLHS && !canSkipRHS) {
-                argNode = new BinaryNode(source, Token.recast(binaryNode.getToken(), TokenType.COMMARIGHT), lhs, rhs);
-            } else {
-                argNode = !canSkipLHS ? lhs : rhs;
-            }
-
-            return new BinaryNode(source, Token.recast(binaryNode.getToken(), TokenType.COMMARIGHT), argNode, resultNode).accept(this);
-        }
-
-        binaryNode.setLHS(convert(lhs, type));
-        binaryNode.setRHS(convert(rhs, type));
-
-        return binaryNode;
-    }
-
-    @Override
-    public Node leaveGE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, GE);
-    }
-
-    @Override
-    public Node leaveGT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, GT);
-    }
-
-    private Node exitIN_INSTANCEOF(final BinaryNode binaryNode, final Request request) {
-        getCurrentFunctionNode().newTemporary(request.getReturnType(), binaryNode);
-        return newRuntime(binaryNode, request);
-    }
-
-    @Override
-    public Node leaveIN(final BinaryNode binaryNode) {
-        return exitIN_INSTANCEOF(binaryNode, IN);
-    }
-
-    @Override
-    public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
-        return exitIN_INSTANCEOF(binaryNode, INSTANCEOF);
-    }
-
-    @Override
-    public Node leaveLE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, LE);
-    }
-
-    @Override
-    public Node leaveLT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, LT);
-    }
-
-    @Override
-    public Node leaveMOD(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node leaveMUL(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode,  binaryType(binaryNode));
-    }
-
-    @Override
-    public Node leaveNE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, NE);
-    }
-
-    @Override
-    public Node leaveNE_STRICT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, NE_STRICT);
-    }
-
-    @Override
-    public Node leaveOR(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, Type.OBJECT, null, null);
-    }
-
-    @Override
-    public Node leaveSAR(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, Type.INT);
-    }
-
-    @Override
-    public Node leaveSHL(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, Type.INT);
-    }
-
-    @Override
-    public Node leaveSHR(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, Type.LONG, Type.INT, Type.INT);
-    }
-
-    @Override
-    public Node leaveSUB(final BinaryNode binaryNode) {
-        return leaveBinary(binaryNode, binaryType(binaryNode));
-    }
-
-    @Override
-    public Node leave(final TernaryNode ternaryNode) {
-        final Node test = ternaryNode.lhs();
-        final Node lhs  = ternaryNode.rhs();
-        final Node rhs  = ternaryNode.third();
-
-        ensureTypeNotUnknown(lhs);
-        ensureTypeNotUnknown(rhs);
-        final Type type = Type.widest(lhs.getType(), rhs.getType());
-
-        ternaryNode.setRHS(convert(lhs, type));
-        ternaryNode.setThird(convert(rhs, type));
-
-        // optimize away the ternary if the test is constant.
-        if (test.getSymbol().isConstant()) {
-            return ((LiteralNode<?>)test).isTrue() ? lhs : rhs;
-        }
-
-        ternaryNode.setLHS(convert(test, Type.BOOLEAN));
-
-        getCurrentFunctionNode().newTemporary(type, ternaryNode);
-
-        return ternaryNode;
-    }
-
-    private static void ensureTypeNotUnknown(final Node node) {
-        final Symbol symbol = node.getSymbol();
-
-        /*
-         * Note that not just unknowns, but params need to be blown
-         * up to objects, because we can have something like
-         *
-         * function f(a) {
-         *    var b = ~a; //b and a are inferred to be int
-         *    return b;
-         * }
-         *
-         * In this case, it would be correct to say that "if you have
-         * an int at the callsite, just pass it".
-         *
-         * However
-         *
-         * function f(a) {
-         *    var b = ~a;      //b and a are inferred to be int
-         *    return b == 17;  //b is still inferred to be int.
-         * }
-         *
-         * can be called with f("17") and if we assume that b is an
-         * int and don't blow it up to an object in the comparison, we
-         * are screwed. I hate JavaScript.
-         *
-         * This check has to be done for any operation that might take
-         * objects as parameters, for example +, but not *, which is known
-         * to coerce types into doubles
-         */
-        if (node.getType().isUnknown() || symbol.isParam()) {
-            symbol.setType(Type.OBJECT);
-            symbol.setCanBeUndefined();
-         }
-    }
-
-    /**
-     * A simple node visitor that ensure that scope and slot information is correct.
-     * This is run as a post pass after we know all scope changing information about
-     * the Lowering. This is also called after potential mutations like splitting
-     * have taken place, as splitting forces scope.
-     *
-     * This was previously done on a per functionNode basis in {@link CodeGenerator},
-     * but this is too late for type information to be used in {@link AccessSpecializer}
-     */
-    static class FinalizeSymbols extends NodeVisitor {
-        @Override
-        public Node leave(final Block block) {
-            return updateSymbols(block);
-        }
-
-        @Override
-        public Node leave(final FunctionNode function) {
-            return updateSymbols(function);
-        }
-
-        private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
-            if (!symbol.isScope()) {
-                LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
-            }
-            if (loseSlot && symbol.hasSlot()) {
-                LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
-            }
-        }
-
-        // called after a block or function node (subclass of block) is finished
-        // to correct scope and slot assignment for variables
-        private static Block updateSymbols(final Block block) {
-
-            if (!block.needsScope()) {
-                return block; // nothing to do
-            }
-
-            assert !(block instanceof FunctionNode) || block.getFunction() == block;
-
-            final FunctionNode functionNode   = block.getFunction();
-            final List<Symbol> symbols        = block.getFrame().getSymbols();
-            final boolean      allVarsInScope = functionNode.varsInScope();
-            final boolean      isVarArg       = functionNode.isVarArg();
-
-            for (final Symbol symbol : symbols) {
-                if (symbol.isInternal() || symbol.isThis()) {
-                    continue;
-                }
-
-                if (symbol.isVar()) {
-                    if (allVarsInScope || symbol.isScope()) {
-                        updateSymbolsLog(functionNode, symbol, true);
-                        symbol.setIsScope();
-                        symbol.setNeedsSlot(false);
-                    } else {
-                        assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
-                    }
-                } else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
-                    updateSymbolsLog(functionNode, symbol, isVarArg);
-                    symbol.setIsScope();
-                    symbol.setNeedsSlot(!isVarArg);
-                }
-            }
-
-            return block;
-        }
-    }
-
-    /**
-     * Helper class to evaluate constant expressions at compile time This is
-     * also a simplifier used by BinaryNode visits, UnaryNode visits and
-     * conversions.
-     */
-    private abstract static class ConstantEvaluator<T extends Node> {
-        protected T            parent;
-        protected final Source source;
-        protected final long   token;
-        protected final int    finish;
-
-        protected ConstantEvaluator(final T parent) {
-            this.parent  = parent;
-            this.source  = parent.getSource();
-            this.token   = parent.getToken();
-            this.finish  = parent.getFinish();
-        }
-
-        /**
-         * Returns a literal node that replaces the given parent node, or null if replacement
-         * is impossible
-         * @return the literal node
-         */
-        protected abstract LiteralNode<?> eval();
-    }
-
-    static class LiteralNodeConstantEvaluator extends ConstantEvaluator<LiteralNode<?>> {
-        private final Type type;
-
-        LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) {
-            super(parent);
-            this.type = type;
-        }
-
-        @Override
-        protected LiteralNode<?> eval() {
-            final Object value = ((LiteralNode<?>)parent).getValue();
-
-            LiteralNode<?> literalNode = null;
-
-            if (type.isString()) {
-                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toString(value));
-            } else if (type.isBoolean()) {
-                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toBoolean(value));
-            } else if (type.isInteger()) {
-                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toInt32(value));
-            } else if (type.isLong()) {
-                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toLong(value));
-            } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) {
-                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toNumber(value));
-            }
-
-            return literalNode;
-        }
-    }
-
-    private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> {
-        UnaryNodeConstantEvaluator(final UnaryNode parent) {
-            super(parent);
-        }
-
-        @Override
-        protected LiteralNode<?> eval() {
-            final Node rhsNode = parent.rhs();
-
-            if (!rhsNode.getSymbol().isConstant()) {
-                return null;
-            }
-
-            final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode;
-            final boolean rhsInteger = rhs.getType().isInteger();
-
-            LiteralNode<?> literalNode;
-
-            switch (parent.tokenType()) {
-            case ADD:
-                if (rhsInteger) {
-                    literalNode = LiteralNode.newInstance(source, token, finish, rhs.getInt32());
-                } else {
-                    literalNode = LiteralNode.newInstance(source, token, finish, rhs.getNumber());
-                }
-                break;
-            case SUB:
-                if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js
-                    literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getInt32());
-                } else {
-                    literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getNumber());
-                }
-                break;
-            case NOT:
-                literalNode = LiteralNode.newInstance(source, token, finish, !rhs.getBoolean());
-                break;
-            case BIT_NOT:
-                literalNode = LiteralNode.newInstance(source, token, finish, ~rhs.getInt32());
-                break;
-            default:
-                return null;
-            }
-
-            return literalNode;
-        }
-    }
-
-    private static class BinaryNodeConstantEvaluator extends ConstantEvaluator<BinaryNode> {
-        BinaryNodeConstantEvaluator(final BinaryNode parent) {
-            super(parent);
-        }
-
-        @Override
-        protected LiteralNode<?> eval() {
-
-            if (!parent.lhs().getSymbol().isConstant() || !parent.rhs().getSymbol().isConstant()) {
-                return null;
-            }
-
-            final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs();
-            final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs();
-
-            final Type widest = Type.widest(lhs.getType(), rhs.getType());
-
-            boolean isInteger = widest.isInteger();
-            boolean isLong    = widest.isLong();
-
-            double value;
-
-            switch (parent.tokenType()) {
-            case AND:
-                return JSType.toBoolean(lhs.getObject()) ? rhs : lhs;
-            case OR:
-                return JSType.toBoolean(lhs.getObject()) ? lhs : rhs;
-            case DIV:
-                value = lhs.getNumber() / rhs.getNumber();
-                break;
-            case ADD:
-                value = lhs.getNumber() + rhs.getNumber();
-                break;
-            case MUL:
-                value = lhs.getNumber() * rhs.getNumber();
-                break;
-            case MOD:
-                value = lhs.getNumber() % rhs.getNumber();
-                break;
-            case SUB:
-                value = lhs.getNumber() - rhs.getNumber();
-                break;
-            case SHR:
-                return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL);
-            case SAR:
-                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32());
-            case SHL:
-                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() << rhs.getInt32());
-            case BIT_XOR:
-                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() ^ rhs.getInt32());
-            case BIT_AND:
-                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() & rhs.getInt32());
-            case BIT_OR:
-                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() | rhs.getInt32());
-            case GE:
-                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject()));
-            case LE:
-                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject()));
-            case GT:
-                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject()));
-            case LT:
-                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject()));
-            case NE:
-                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject()));
-            case NE_STRICT:
-                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject()));
-            case EQ:
-                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject()));
-            case EQ_STRICT:
-                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject()));
-            default:
-                return null;
-            }
-
-            isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value);
-            isLong    &= value != 0.0 && JSType.isRepresentableAsLong(value);
-
-            if (isInteger) {
-                return LiteralNode.newInstance(source, token, finish, JSType.toInt32(value));
-            } else if (isLong) {
-                return LiteralNode.newInstance(source, token, finish, JSType.toLong(value));
-            }
-
-            return LiteralNode.newInstance(source, token, finish, value);
-        }
-    }
-}
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Wed Jan 30 12:26:45 2013 +0100
@@ -929,7 +929,7 @@
      */
     public MethodEmitter loadCallee() {
         debug("load callee " + functionNode.getCalleeNode().getSymbol());
-        assert functionNode.getCalleeNode().getSymbol().getSlot() != 0;
+        assert functionNode.getCalleeNode().getSymbol().getSlot() != 0 : "callee has wrong slot " + functionNode.getCalleeNode().getSymbol().getSlot() + " in " + functionNode.getName();
 
         return load(functionNode.getCalleeNode().getSymbol());
     }
@@ -992,7 +992,7 @@
      * @param symbol symbol to store stack to
      */
     public void store(final Symbol symbol) {
-        assert symbol != null;
+        assert symbol != null : "No symbol to store";
         if (symbol.hasSlot()) {
             debug("store", symbol);
             popType(symbol.getSymbolType()).store(method, symbol.getSlot());
@@ -1558,7 +1558,7 @@
             label.setStack(stack.clone());
             return;
         }
-        assert stacksEquivalent(stack, labelStack);
+        assert stacksEquivalent(stack, labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point";
     }
 
     /**
--- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java	Wed Jan 30 12:26:45 2013 +0100
@@ -159,7 +159,9 @@
             int slot = 2;
             for (final Type type : paramTypes) {
                 method.load(type, slot++);
-                if (type == Type.NUMBER || type == Type.LONG) slot++;
+                if (type == Type.NUMBER || type == Type.LONG) {
+                    slot++;
+                }
             }
             method.dynamicCall(returnType, paramTypes.length, flags);
         }
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java	Wed Jan 30 12:26:45 2013 +0100
@@ -27,6 +27,8 @@
 
 import java.util.List;
 import java.util.Map;
+
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Block;
@@ -53,17 +55,18 @@
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.ThrowNode;
 import jdk.nashorn.internal.ir.TryNode;
+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;
+import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
+
 
 /**
  * Computes the "byte code" weight of an AST segment. This is used
  * for Splitting too large class files
  */
-public class WeighNodes extends NodeVisitor {
+public class WeighNodes extends NodeOperatorVisitor {
     /*
      * Weight constants.
      */
@@ -77,6 +80,7 @@
     private static final long IF_WEIGHT        = 2;
     private static final long LITERAL_WEIGHT   = 10;
     private static final long LOOP_WEIGHT      = 4;
+    private static final long NEW_WEIGHT       = 6;
     private static final long REFERENCE_WEIGHT = 20;
     private static final long RETURN_WEIGHT    = 2;
     private static final long SPLIT_WEIGHT     = 40;
@@ -120,21 +124,7 @@
     }
 
     @Override
-    public Node leave(final BinaryNode binaryNode) {
-        final TokenType tokenType = binaryNode.tokenType();
-
-        if (tokenType == TokenType.ADD || tokenType == TokenType.ASSIGN_ADD) {
-            weight += ADD_WEIGHT;
-        } else {
-            weight += 1;
-        }
-
-        return binaryNode;
-    }
-
-    @Override
     public Node enter(final Block block) {
-
         if (weightCache != null && weightCache.containsKey(block)) {
             weight += weightCache.get(block);
             return null;
@@ -305,4 +295,274 @@
         weight += WITH_WEIGHT;
         return withNode;
     }
+
+    @Override
+    public Node leaveADD(final UnaryNode unaryNode) {
+        return unaryNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveBIT_NOT(final UnaryNode unaryNode) {
+        return unaryNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveCONVERT(final UnaryNode unaryNode) {
+        return unaryNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveDECINC(final UnaryNode unaryNode) {
+         return unaryNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveDELETE(final UnaryNode unaryNode) {
+        return runtimeNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveDISCARD(final UnaryNode unaryNode) {
+        return unaryNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveNEW(final UnaryNode unaryNode) {
+        weight += NEW_WEIGHT;
+        return unaryNode;
+    }
+
+    @Override
+    public Node leaveNOT(final UnaryNode unaryNode) {
+        return unaryNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveSUB(final UnaryNode unaryNode) {
+        return unaryNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveTYPEOF(final UnaryNode unaryNode) {
+        return runtimeNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveVOID(final UnaryNode unaryNode) {
+        return unaryNodeWeight(unaryNode);
+    }
+
+    @Override
+    public Node leaveADD(final BinaryNode binaryNode) {
+        weight += ADD_WEIGHT;
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveAND(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
+        weight += ADD_WEIGHT;
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveBIND(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveBIT_AND(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveBIT_OR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveBIT_XOR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveDIV(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveEQ(final BinaryNode binaryNode) {
+        return runtimeNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
+        return runtimeNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveGE(final BinaryNode binaryNode) {
+        return runtimeNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveGT(final BinaryNode binaryNode) {
+        return runtimeNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveIN(final BinaryNode binaryNode) {
+        weight += CALL_WEIGHT;
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
+        weight += CALL_WEIGHT;
+        return binaryNode;
+    }
+
+    @Override
+    public Node leaveLE(final BinaryNode binaryNode) {
+        return runtimeNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveLT(final BinaryNode binaryNode) {
+        return runtimeNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveMOD(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveMUL(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveNE(final BinaryNode binaryNode) {
+        return runtimeNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveNE_STRICT(final BinaryNode binaryNode) {
+        return runtimeNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveOR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveSAR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveSHL(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveSHR(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    @Override
+    public Node leaveSUB(final BinaryNode binaryNode) {
+        return binaryNodeWeight(binaryNode);
+    }
+
+    private Node unaryNodeWeight(final UnaryNode unaryNode) {
+        weight += 1;
+        return unaryNode;
+    }
+
+    private Node binaryNodeWeight(final BinaryNode binaryNode) {
+        weight += 1;
+        return binaryNode;
+    }
+
+    private Node runtimeNodeWeight(final UnaryNode unaryNode) {
+        weight += CALL_WEIGHT;
+        return unaryNode;
+    }
+
+    private Node runtimeNodeWeight(final BinaryNode binaryNode) {
+        weight += Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()).isObject() ? CALL_WEIGHT : 1;
+        return binaryNode;
+    }
 }
--- a/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Wed Jan 30 12:26:45 2013 +0100
@@ -33,8 +33,9 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
 
 import java.lang.invoke.MethodHandle;
+import java.util.ArrayList;
 import java.util.EnumSet;
-import java.util.List;
+
 import jdk.nashorn.internal.codegen.CodeGenerator;
 import jdk.nashorn.internal.codegen.FunctionSignature;
 import jdk.nashorn.internal.codegen.MethodEmitter;
@@ -61,11 +62,9 @@
      *
      * @param codegen      the code generator
      * @param functionNode the function node to turn into a ScriptFunction implementation
-     * @param keys         initial keys for the object map
-     * @param symbols      corresponding initial symbols for object map
      */
-    public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode, final List<String> keys, final List<Symbol> symbols) {
-        super(codegen, keys, symbols, false, false);
+    public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode) {
+        super(codegen, new ArrayList<String>(), new ArrayList<Symbol>(), false, false);
         this.functionNode = functionNode;
     }
 
--- a/src/jdk/nashorn/internal/ir/Block.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/Block.java	Wed Jan 30 12:26:45 2013 +0100
@@ -35,11 +35,11 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
+
 import jdk.nashorn.internal.codegen.Frame;
 import jdk.nashorn.internal.codegen.MethodEmitter.Label;
 import jdk.nashorn.internal.ir.annotations.Ignore;
@@ -287,7 +287,7 @@
 
         for (Block block = this; block != null; block = block.getParent()) {
             // Find name.
-            final Symbol symbol = block.getSymbols().get(name);
+            final Symbol symbol = block.symbols.get(name);
             // If found then we are good.
             if (symbol != null) {
                 return symbol;
@@ -307,7 +307,7 @@
         // Search up block chain to locate symbol.
         for (Block block = this; block != null; block = block.getParent()) {
             // Find name.
-            final Symbol symbol = block.getSymbols().get(name);
+            final Symbol symbol = block.symbols.get(name);
             // If found then we are good.
             if (symbol != null) {
                 return symbol;
@@ -458,14 +458,22 @@
     }
 
     /**
-     * Print symbols in block (debugging.)
+     * Print symbols in block in alphabetical order, sorted on name
+     * Used for debugging, see the --print-symbols flag
      *
      * @param stream print writer to output symbols to
      *
      * @return true if symbols were found
      */
     public boolean printSymbols(final PrintWriter stream) {
-        final Collection<Symbol> values = symbols.values();
+        final List<Symbol> values = new ArrayList<>(symbols.values());
+
+        Collections.sort(values, new Comparator<Symbol>() {
+            @Override
+            public int compare(final Symbol s0, final Symbol s1) {
+                return s0.getName().compareTo(s1.getName());
+            }
+        });
 
         for (final Symbol symbol : values) {
             symbol.print(stream);
@@ -564,15 +572,6 @@
     }
 
     /**
-     * Get the symbol table for this block
-     *
-     * @return a symbol table, which is a map from symbol name to symbol.
-     */
-    private Map<String, Symbol> getSymbols() {
-        return symbols;
-    }
-
-    /**
      * Check whether scope is necessary for this Block
      *
      * @return true if this function needs a scope
--- a/src/jdk/nashorn/internal/ir/CallNode.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/CallNode.java	Wed Jan 30 12:26:45 2013 +0100
@@ -28,7 +28,9 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+
 import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.Source;
 
@@ -57,16 +59,83 @@
      */
     public static class EvalArgs {
         /** evaluated code */
-        public Node    code;
+        private Node code;
+
         /** 'this' passed to evaluated code */
-        public Node    evalThis;
+        private IdentNode evalThis;
+
         /** location string for the eval call */
-        public String  location;
+        final private String location;
+
         /** is this call from a strict context? */
-        public boolean strictMode;
+        final private boolean strictMode;
+
+        /**
+         * Constructor
+         *
+         * @param code       code to evaluate
+         * @param evalThis   this node
+         * @param location   location for the eval call
+         * @param strictMode is this a call from a strict context?
+         */
+        public EvalArgs(final Node code, final IdentNode evalThis, final String location, final boolean strictMode) {
+            this.code = code;
+            this.evalThis = evalThis;
+            this.location = location;
+            this.strictMode = strictMode;
+        }
+
+        /**
+         * Return the code that is to be eval:ed by this eval function
+         * @return code as an AST node
+         */
+        public Node getCode() {
+            return code;
+        }
+
+        /**
+         * Set the code that is to be eval.ed by this eval function
+         * @param code the code as an AST node
+         */
+        public void setCode(final Node code) {
+            this.code = code;
+        }
+
+        /**
+         * Get the {@code this} symbol used to invoke this eval call
+         * @return the {@code this} symbol
+         */
+        public IdentNode getThis() {
+            return this.evalThis;
+        }
+
+        /**
+         * Set the {@code this} symbol used to invoke this eval call
+         * @param evalThis the {@code this} symbol
+         */
+        public void setThis(final IdentNode evalThis) {
+            this.evalThis = evalThis;
+        }
+
+        /**
+         * Get the human readable location for this eval call
+         * @return the location
+         */
+        public String getLocation() {
+            return this.location;
+        }
+
+        /**
+         * Check whether this eval call is executed in strict mode
+         * @return true if executed in strict mode, false otherwise
+         */
+        public boolean getStrictMode() {
+            return this.strictMode;
+        }
     }
 
     /** arguments for 'eval' call. Non-null only if this call node is 'eval' */
+    @Ignore
     private EvalArgs evalArgs;
 
     /**
--- a/src/jdk/nashorn/internal/ir/CatchNode.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/CatchNode.java	Wed Jan 30 12:26:45 2013 +0100
@@ -57,7 +57,7 @@
      * @param body               catch body
      */
     public CatchNode(final Source source, final long token, final int finish, final IdentNode exception, final Node exceptionCondition, final Block body) {
-        super (source, token, finish);
+        super(source, token, finish);
 
         this.exception          = exception;
         this.exceptionCondition = exceptionCondition;
--- a/src/jdk/nashorn/internal/ir/ExecuteNode.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/ExecuteNode.java	Wed Jan 30 12:26:45 2013 +0100
@@ -47,14 +47,22 @@
      */
     public ExecuteNode(final Source source, final long token, final int finish, final Node expression) {
         super(source, token, finish);
+        this.expression = expression;
+    }
 
+    /**
+     * Constructor
+     *
+     * @param expression an expression to wrap, from which source, tokens and finish are also inherited
+     */
+    public ExecuteNode(final Node expression) {
+        super(expression.getSource(), expression.getToken(), expression.getFinish());
         this.expression = expression;
     }
 
     private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) {
         super(executeNode);
-
-        expression = cs.existingOrCopy(executeNode.expression);
+        this.expression = cs.existingOrCopy(executeNode.expression);
     }
 
     @Override
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Wed Jan 30 12:26:45 2013 +0100
@@ -32,10 +32,8 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Stack;
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
@@ -126,9 +124,6 @@
     @Ignore
     private IdentNode varArgsNode;
 
-    /** this access properties. */
-    private final LinkedHashMap<String, Node> thisProperties;
-
     /** Pending label list. */
     private final Stack<LabelNode> labelStack;
 
@@ -147,6 +142,10 @@
     @Ignore
     private LineNumberNode funcVarLineNumberNode;
 
+    /** Initializer var func = __callee__, where applicable */
+    @Ignore
+    private Node selfSymbolInit;
+
     /** Function flags. */
     private int flags;
 
@@ -183,7 +182,7 @@
     private static final int NEEDS_SCOPE           = HAS_ALL_VARS_IN_SCOPE | IS_VAR_ARG;
 
     /** What is the return type of this function? */
-    private Type returnType = Type.OBJECT;
+    private Type returnType = Type.UNKNOWN;
 
     /**
      * Used to keep track of a function's parent blocks.
@@ -216,7 +215,6 @@
         this.firstToken        = token;
         this.lastToken         = token;
         this.namespace         = new Namespace(compiler.getNamespace().getParent());
-        this.thisProperties    = new LinkedHashMap<>();
         this.labelStack        = new Stack<>();
         this.controlStack      = new Stack<>();
         this.declarations      = new ArrayList<>();
@@ -249,7 +247,6 @@
         this.argumentsNode     = (IdentNode)cs.existingOrCopy(functionNode.argumentsNode);
         this.varArgsNode       = (IdentNode)cs.existingOrCopy(functionNode.varArgsNode);
         this.calleeNode        = (IdentNode)cs.existingOrCopy(functionNode.calleeNode);
-        this.thisProperties    = new LinkedHashMap<>();
         this.labelStack        = new Stack<>();
         this.controlStack      = new Stack<>();
         this.declarations      = new ArrayList<>();
@@ -372,30 +369,44 @@
     /**
      * Create a temporary variable to the current frame.
      *
+     * @param currentFrame Frame to add to - defaults to current function frame
      * @param type  Strong type of symbol.
      * @param node  Primary node to use symbol.
      *
      * @return Symbol used.
      */
-    public Symbol newTemporary(final Type type, final Node node) {
-        Symbol sym = node.getSymbol();
+    public Symbol newTemporary(final Frame currentFrame, final Type type, final Node node) {
+        assert currentFrame != null;
+        Symbol symbol = node.getSymbol();
 
         // If no symbol already present.
-        if (sym == null) {
+        if (symbol == null) {
             final String uname = uniqueName(TEMP_PREFIX.tag());
-            sym = new Symbol(uname, IS_TEMP, type);
-            sym.setNode(node);
+            symbol = new Symbol(uname, IS_TEMP, type);
+            symbol.setNode(node);
         }
 
         // Assign a slot if it doesn't have one.
-        if (!sym.hasSlot()) {
-            frames.addSymbol(sym);
+        if (!symbol.hasSlot()) {
+            currentFrame.addSymbol(symbol);
         }
 
         // Set symbol to node.
-        node.setSymbol(sym);
+        node.setSymbol(symbol);
 
-        return sym;
+        return symbol;
+    }
+
+    /**
+     * Add a new temporary variable to the current frame
+     *
+     * @param type Strong type of symbol
+     * @param node Primary node to use symbol
+     *
+     * @return symbol used
+     */
+    public Symbol newTemporary(final Type type, final Node node) {
+        return newTemporary(frames, type, node);
     }
 
     /**
@@ -414,22 +425,13 @@
         return sym;
     }
 
-    /**
-     * Add a property to the constructor (function) based on this.x usage.
-     *
-     * @param key  Name of property.
-     * @param node Value node (has type.)
-     */
-    public void addThisProperty(final String key, final Node node) {
-        if (node == null) {
-            return;
-        }
-
-        thisProperties.put(key, node);
-    }
-
     @Override
     public void toString(final StringBuilder sb) {
+        sb.append('[');
+        sb.append(returnType);
+        sb.append(']');
+        sb.append(' ');
+
         sb.append("function");
 
         if (ident != null) {
@@ -872,11 +874,22 @@
     }
 
     /**
+     * Get the initializer statement for the __callee__ variable, where applicable
+     * for self references
+     * @return initialization
+     */
+    public Node getSelfSymbolInit() {
+        return this.selfSymbolInit;
+    }
+
+    /**
      * Flag the function as needing a self symbol. This is needed only for
      * self referring functions
+     * @param selfSymbolInit initialization expression for self symbol
      */
-    public void setNeedsSelfSymbol() {
+    public void setNeedsSelfSymbol(final Node selfSymbolInit) {
         this.flags |= NEEDS_SELF_SYMBOL;
+        this.selfSymbolInit = selfSymbolInit;
     }
 
     /**
@@ -942,16 +955,6 @@
     }
 
     /**
-     * Get a all properties accessed with {@code this} used as a base in this
-     * function - the map is ordered upon assignment order in the control flow
-     *
-     * @return map a map of property name to node mapping for this accesses
-     */
-    public Map<String, Node> getThisProperties() {
-        return Collections.unmodifiableMap(thisProperties);
-    }
-
-    /**
      * Get the namespace this function uses for its symbols
      * @return the namespace
      */
@@ -984,11 +987,7 @@
         //we never bother with object types narrower than objects, that will lead to byte code verification errors
         //as for instance even if we know we are returning a string from a method, the code generator will always
         //treat it as an object, at least for now
-        this.returnType = returnType.isObject() ? Type.OBJECT : returnType;
-        // Adjust type of result node symbol
-        if (returnType != Type.UNKNOWN) {
-            resultNode.getSymbol().setTypeOverride(this.returnType);
-        }
+        this.returnType = Type.widest(this.returnType,  returnType.isObject() ? Type.OBJECT : returnType);
     }
 
     /**
@@ -1010,15 +1009,13 @@
 
     /**
      * Set the lowered state
-     *
-     * @param isLowered lowered state
      */
-    public void setIsLowered(final boolean isLowered) {
-        flags = isLowered ? flags | IS_LOWERED : flags & ~IS_LOWERED;
+    public void setIsLowered() {
+        flags |= IS_LOWERED;
     }
 
     /**
-     * Get the lowered
+     * Get the lowered state
      *
      * @return true if function is lowered
      */
@@ -1077,22 +1074,6 @@
     }
 
     /**
-     * @return the unit index
-     */
-//    public int getUnit() {
- //       return unit;
- //   }
-
-    /**
-     * Set the index of this function's compile unit. Used by splitter.
-     * @see Splitter
-     * @param unit the unit index
-     */
-//    public void setUnit(final int unit) {
-//        this.unit = unit;
-//    }
-
-    /**
      * Get the compile unit used to compile this function
      * @see Compiler
      * @see Splitter
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/LiteralNode.java	Wed Jan 30 12:26:45 2013 +0100
@@ -32,8 +32,10 @@
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Lexer.LexerToken;
+import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.parser.TokenType;
 import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.Undefined;
 
@@ -200,6 +202,15 @@
     }
 
     /**
+     * Test if tha value is a number
+     *
+     * @return True if value is a number
+     */
+    public boolean isNumeric() {
+        return value instanceof Number;
+    }
+
+    /**
      * Assist in IR navigation.
      *
      * @param visitor IR navigating visitor.
@@ -240,16 +251,49 @@
      * @return the new literal node
      */
     public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish) {
-        return new LiteralNode<Node>(source, token, finish, null) {
-            @Override
-            protected Node copy(final CopyState cs) {
-                return LiteralNode.newInstance(getSource(), getToken(), getFinish());
-            }
-            @Override
-            public Type getType() {
-                return Type.OBJECT;
-            }
-        };
+        return new NodeLiteralNode(source, token, finish);
+    }
+
+    /**
+     * Create a new null literal based on a parent node (source, token, finish)
+     *
+     * @param parent parent node
+     *
+     * @return the new literal node
+     */
+    public static LiteralNode<?> newInstance(final Node parent) {
+        return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
+    }
+
+    private static class BooleanLiteralNode extends LiteralNode<Boolean> {
+
+        private BooleanLiteralNode(final Source source, final long token, final int finish, final boolean value) {
+            super(source, Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
+        }
+
+        private BooleanLiteralNode(final BooleanLiteralNode literalNode) {
+            super(literalNode);
+        }
+
+        @Override
+        protected Node copy(final CopyState cs) {
+            return new BooleanLiteralNode(this);
+        }
+
+        @Override
+        public boolean isTrue() {
+            return value;
+        }
+
+        @Override
+        public Type getType() {
+            return Type.BOOLEAN;
+        }
+
+        @Override
+        public Type getWidestOperationType() {
+            return Type.BOOLEAN;
+        }
     }
 
     /**
@@ -263,29 +307,63 @@
      * @return the new literal node
      */
     public static LiteralNode<Boolean> newInstance(final Source source, final long token, final int finish, final boolean value) {
-        return new LiteralNode<Boolean>(source, token, finish, value) {
-            @Override
-            protected Node copy(final CopyState cs) {
-                return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
+        return new BooleanLiteralNode(source, token,  finish, value);
+    }
+
+    /**
+     * Create a new boolean literal based on a parent node (source, token, finish)
+     *
+     * @param parent parent node
+     * @param value  true or false
+     *
+     * @return the new literal node
+     */
+    public static LiteralNode<?> newInstance(final Node parent, final boolean value) {
+        return new BooleanLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
+    }
+
+    private static class NumberLiteralNode extends LiteralNode<Number> {
+
+        private final Type type = numberGetType(value);
+
+        private NumberLiteralNode(final Source source, final long token, final int finish, final Number value) {
+            super(source, Token.recast(token, TokenType.DECIMAL), finish, value);
+        }
+
+        private NumberLiteralNode(final NumberLiteralNode literalNode) {
+            super(literalNode);
+        }
+
+        private static Type numberGetType(final Number number) {
+            if (number instanceof Integer) {
+                return Type.INT;
+            } else if (number instanceof Long) {
+                return Type.LONG;
+            } else if (number instanceof Double) {
+                return Type.NUMBER;
+            } else {
+                assert false;
             }
 
-            @Override
-            public boolean isTrue() {
-                return value;
-            }
+            return null;
+        }
 
-            @Override
-            public Type getType() {
-                return Type.BOOLEAN;
-            }
+        @Override
+        protected Node copy(final CopyState cs) {
+            return new NumberLiteralNode(this);
+        }
 
-            @Override
-            public Type getWidestOperationType() {
-                return Type.BOOLEAN;
-            }
-        };
+        @Override
+        public Type getType() {
+            return type;
+        }
+
+        @Override
+        public Type getWidestOperationType() {
+            return getType();
+        }
+
     }
-
     /**
      * Create a new number literal
      *
@@ -297,40 +375,34 @@
      * @return the new literal node
      */
     public static LiteralNode<Number> newInstance(final Source source, final long token, final int finish, final Number value) {
-        return new LiteralNode<Number>(source, token, finish, value) {
+        return new NumberLiteralNode(source, token, finish, value);
+    }
 
-            private Type numberGetType(final Number number) {
-                if (number instanceof Integer) {
-                    return Type.INT;
-                } else if (number instanceof Long) {
-                    return Type.LONG;
-                } else if (number instanceof Double) {
-                    return Type.NUMBER;
-                } else {
-                    assert false;
-                }
+    /**
+     * Create a new number literal based on a parent node (source, token, finish)
+     *
+     * @param parent parent node
+     * @param value  literal value
+     *
+     * @return the new literal node
+     */
+    public static LiteralNode<?> newInstance(final Node parent, final Number value) {
+        return new NumberLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
+    }
 
-                return null;
-            }
+    private static class UndefinedLiteralNode extends LiteralNode<Undefined> {
+        private UndefinedLiteralNode(final Source source, final long token, final int finish) {
+            super(source, Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
+        }
 
-            private final Type type = numberGetType(value);
+        private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
+            super(literalNode);
+        }
 
-            @Override
-            protected Node copy(final CopyState cs) {
-                return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
-            }
-
-            @Override
-            public Type getType() {
-                return type;
-            }
-
-            @Override
-            public Type getWidestOperationType() {
-                return getType();
-            }
-
-        };
+        @Override
+        protected Node copy(final CopyState cs) {
+            return new UndefinedLiteralNode(this);
+        }
     }
 
     /**
@@ -344,12 +416,41 @@
      * @return the new literal node
      */
     public static LiteralNode<Undefined> newInstance(final Source source, final long token, final int finish, final Undefined value) {
-        return new LiteralNode<Undefined>(source, token, finish, value) {
-            @Override
-            protected Node copy(final CopyState cs) {
-                return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
-            }
-        };
+        return new UndefinedLiteralNode(source, token, finish);
+    }
+
+    /**
+     * Create a new null literal based on a parent node (source, token, finish)
+     *
+     * @param parent parent node
+     * @param value  undefined value
+     *
+     * @return the new literal node
+     */
+    public static LiteralNode<?> newInstance(final Node parent, final Undefined value) {
+        return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
+    }
+
+    private static class StringLiteralNode extends LiteralNode<String> {
+        private StringLiteralNode(final Source source, final long token, final int finish, final String value) {
+            super(source, Token.recast(token, TokenType.STRING), finish, value);
+        }
+
+        private StringLiteralNode(final StringLiteralNode literalNode) {
+            super(literalNode);
+        }
+
+        @Override
+        protected Node copy(final CopyState cs) {
+            return new StringLiteralNode(this);
+        }
+
+        @Override
+        public void toString(final StringBuilder sb) {
+            sb.append('\"');
+            sb.append(value);
+            sb.append('\"');
+        }
     }
 
     /**
@@ -363,19 +464,39 @@
      * @return the new literal node
      */
     public static LiteralNode<String> newInstance(final Source source, final long token, final int finish, final String value) {
-        return new LiteralNode<String>(source, token, finish, value) {
-            @Override
-            protected Node copy(final CopyState cs) {
-                return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
-            }
+        return new StringLiteralNode(source, token, finish, value);
+    }
 
-            @Override
-            public void toString(final StringBuilder sb) {
-                sb.append('\"');
-                sb.append(value);
-                sb.append('\"');
-            }
-        };
+    /**
+     * Create a new String literal based on a parent node (source, token, finish)
+     *
+     * @param parent parent node
+     * @param value  string value
+     *
+     * @return the new literal node
+     */
+    public static LiteralNode<?> newInstance(final Node parent, final String value) {
+        return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
+    }
+
+    private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
+        private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) {
+            super(source, Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
+        }
+
+        private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
+            super(literalNode);
+        }
+
+        @Override
+        protected Node copy(final CopyState cs) {
+            return new LexerTokenLiteralNode(this);
+        }
+
+        @Override
+        public void toString(final StringBuilder sb) {
+            sb.append(value.toString());
+        }
     }
 
     /**
@@ -389,22 +510,65 @@
      * @return the new literal node
      */
     public static LiteralNode<LexerToken> newInstance(final Source source, final long token, final int finish, final LexerToken value) {
-        return new LiteralNode<LexerToken>(source, token, finish, value) {
-            @Override
-            protected Node copy(final CopyState cs) {
-                return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
-            }
-
-
-            @Override
-            public void toString(final StringBuilder sb) {
-                sb.append(value.toString());
-            }
-        };
+        return new LexerTokenLiteralNode(source, token, finish, value);
     }
 
     /**
-     * Create a new array for an arbitrary node
+     * Create a new lexer token literal based on a parent node (source, token, finish)
+     *
+     * @param parent parent node
+     * @param value  lexer token
+     *
+     * @return the new literal node
+     */
+    public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) {
+        return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
+    }
+
+    private static class NodeLiteralNode extends LiteralNode<Node> {
+
+        private NodeLiteralNode(final Source source, final long token, final int finish) {
+            this(source, token, finish, null);
+        }
+
+        private NodeLiteralNode(final Source source, final long token, final int finish, final Node value) {
+            super(source, Token.recast(token, TokenType.OBJECT), finish, value);
+        }
+
+        private NodeLiteralNode(final LiteralNode<Node> literalNode) {
+            super(literalNode);
+        }
+
+        @Override
+        protected Node copy(final CopyState cs) {
+            return new NodeLiteralNode(this);
+        }
+
+        @Override
+        public Node accept(final NodeVisitor visitor) {
+            if (visitor.enter(this) != null) {
+                if (value != null) {
+                    value = value.accept(visitor);
+                }
+                return visitor.leave(this);
+            }
+
+            return this;
+        }
+
+        @Override
+        public Type getType() {
+            return value == null ? Type.OBJECT : super.getType();
+        }
+
+        @Override
+        public Type getWidestOperationType() {
+            return value == null ? Type.OBJECT : value.getWidestOperationType();
+        }
+
+    }
+    /**
+     * Create a new node literal for an arbitrary node
      *
      * @param source  the source
      * @param token   token
@@ -414,33 +578,19 @@
      * @return the new literal node
      */
     public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish, final Node value) {
-        return new LiteralNode<Node>(source, token, finish, value) {
-            @Override
-            protected Node copy(final CopyState cs) {
-                return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
-            }
+        return new NodeLiteralNode(source, token, finish, value);
+    }
 
-            @Override
-            public Node accept(final NodeVisitor visitor) {
-                if (visitor.enter(this) != null) {
-                    value = value.accept(visitor);
-                    return visitor.leave(this);
-                }
-
-                return this;
-            }
-
-            @Override
-            public void toString(final StringBuilder sb) {
-                value.toString(sb);
-            }
-
-            @Override
-            public Type getWidestOperationType() {
-                return value.getWidestOperationType();
-            }
-
-        };
+    /**
+     * Create a new node literal based on a parent node (source, token, finish)
+     *
+     * @param parent parent node
+     * @param value  node value
+     *
+     * @return the new literal node
+     */
+    public static LiteralNode<?> newInstance(final Node parent, final Node value) {
+        return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
     }
 
     /**
@@ -521,8 +671,7 @@
          * @param value   array literal value, a Node array
          */
         protected ArrayLiteralNode(final Source source, final long token, final int finish, final Node[] value) {
-            super(source, token, finish, value);
-
+            super(source, Token.recast(token, TokenType.ARRAY), finish, value);
             this.elementType = Type.UNKNOWN;
         }
 
@@ -530,13 +679,14 @@
          * Copy constructor
          * @param node source array literal node
          */
-        protected ArrayLiteralNode(final LiteralNode<Node[]> node) {
+        protected ArrayLiteralNode(final ArrayLiteralNode node) {
             super(node);
+            this.elementType = node.elementType;
         }
 
         @Override
         protected Node copy(final CopyState cs) {
-            return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
+            return new ArrayLiteralNode(this);
         }
 
         /**
@@ -769,6 +919,19 @@
         return new ArrayLiteralNode(source, token, finish, value.toArray(new Node[value.size()]));
     }
 
+
+    /**
+     * Create a new array literal based on a parent node (source, token, finish)
+     *
+     * @param parent parent node
+     * @param value  literal value list
+     *
+     * @return the new literal node
+     */
+    public static LiteralNode<?> newInstance(final Node parent, final List<Node> value) {
+        return new ArrayLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value.toArray(new Node[value.size()]));
+    }
+
     /**
      * Create a new array literal of Nodes
      *
--- a/src/jdk/nashorn/internal/ir/Node.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/Node.java	Wed Jan 30 12:26:45 2013 +0100
@@ -26,7 +26,7 @@
 package jdk.nashorn.internal.ir;
 
 import java.util.IdentityHashMap;
-import java.util.List;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Token;
@@ -38,7 +38,7 @@
  */
 public abstract class Node extends Location {
     /** Node symbol. */
-    private Symbol nodeSymbol;
+    private Symbol symbol;
 
     /** Start of source range. */
     protected int start;
@@ -68,7 +68,7 @@
     public Node(final Source source, final long token, final int finish) {
         super(source, token);
 
-        start  = Token.descPosition(token);
+        this.start  = Token.descPosition(token);
         this.finish = finish;
     }
 
@@ -80,7 +80,7 @@
     protected Node(final Node node) {
         super(node);
 
-        this.nodeSymbol    = node.nodeSymbol;
+        this.symbol        = node.symbol;
         this.isResolved    = node.isResolved;
         this.isTerminal    = node.isTerminal;
         this.hasGoto       = node.hasGoto;
@@ -107,8 +107,8 @@
      * @return the type of the node.
      */
     public Type getType() {
-        assert hasType();
-        return nodeSymbol.getSymbolType();
+        assert hasType() : this + " has no type";
+        return symbol.getSymbolType();
     }
 
     /**
@@ -379,7 +379,7 @@
      * @return the symbol
      */
     public Symbol getSymbol() {
-        return nodeSymbol;
+        return symbol;
     }
 
     /**
@@ -389,7 +389,7 @@
      * @param symbol the symbol
      */
     public void setSymbol(final Symbol symbol) {
-        nodeSymbol = symbol;
+        this.symbol = symbol;
     }
 
     /**
@@ -412,21 +412,4 @@
         this.isTerminal = isTerminal;
     }
 
-    /**
-     * Return last node in a statement list.
-     *
-     * @param statements Statement list.
-     *
-     * @return Last (non-debug) statement or null if empty block.
-     */
-    public static Node lastStatement(final List<Node> statements) {
-        for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) {
-            final Node node = statements.get(lastIndex);
-            if (!node.isDebug()) {
-                return node;
-            }
-        }
-
-        return null;
-    }
 }
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java	Wed Jan 30 12:26:45 2013 +0100
@@ -257,6 +257,9 @@
     /** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */
     private Type callSiteType;
 
+    /** is final - i.e. may not be removed again, lower in the code pipeline */
+    private boolean isFinal;
+
     /**
      * Constructor
      *
@@ -286,6 +289,51 @@
         this(source, token, finish, request, Arrays.asList(args));
     }
 
+    /**
+     * Constructor
+     *
+     * @param parent  parent node from which to inherit source, token, finish
+     * @param request the request
+     * @param args    arguments to request
+     */
+    public RuntimeNode(final Node parent, final Request request, final Node... args) {
+        this(parent, request, Arrays.asList(args));
+    }
+
+    /**
+     * Constructor
+     *
+     * @param parent  parent node from which to inherit source, token, finish
+     * @param request the request
+     * @param args    arguments to request
+     */
+    public RuntimeNode(final Node parent, final Request request, final List<Node> args) {
+        super(parent);
+
+        this.request = request;
+        this.args    = args;
+    }
+
+    /**
+     * Constructor
+     *
+     * @param parent  parent node from which to inherit source, token, finish and arguments
+     * @param request the request
+     */
+    public RuntimeNode(final UnaryNode parent, final Request request) {
+        this(parent, request, parent.rhs());
+    }
+
+    /**
+     * Constructor
+     *
+     * @param parent  parent node from which to inherit source, token, finish and arguments
+     * @param request the request
+     */
+    public RuntimeNode(final BinaryNode parent, final Request request) {
+        this(parent, request, parent.lhs(), parent.rhs());
+    }
+
     private RuntimeNode(final RuntimeNode runtimeNode, final CopyState cs) {
         super(runtimeNode);
 
@@ -300,6 +348,21 @@
         this.callSiteType = runtimeNode.callSiteType;
     }
 
+    /**
+     * Is this node final - i.e. it can never be replaced with other nodes again
+     * @return true if final
+     */
+    public boolean isFinal() {
+        return isFinal;
+    }
+
+    /**
+     * Flag this node as final - i.e it may never be replaced with other nodes again
+     */
+    public void setIsFinal() {
+        this.isFinal = true;
+    }
+
     @Override
     protected Node copy(final CopyState cs) {
         return new RuntimeNode(this, cs);
--- a/src/jdk/nashorn/internal/ir/Symbol.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/Symbol.java	Wed Jan 30 12:26:45 2013 +0100
@@ -26,6 +26,10 @@
 package jdk.nashorn.internal.ir;
 
 import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.options.Options;
@@ -46,7 +50,7 @@
     /** Is this a constant */
     public static final int IS_CONSTANT = 0b0000_0101;
 
-    static final int KINDMASK    = 0b0000_1111;
+    static final int KINDMASK = 0b0000_1111;
 
     /** Is this scope */
     public static final int IS_SCOPE         = 0b0000_0001_0000;
@@ -85,8 +89,33 @@
     /** Number of times this symbol is used in code */
     private int useCount;
 
-    /** Debugging option - dump info and stack trace when a symbol with a given name is manipulated */
-    private static final String TRACE_SYMBOL = Options.getStringProperty("nashorn.compiler.symbol.trace", null);
+    /** Debugging option - dump info and stack trace when symbols with given names are manipulated */
+    private static final Set<String> TRACE_SYMBOLS;
+    private static final Set<String> TRACE_SYMBOLS_STACKTRACE;
+
+    static {
+        final String stacktrace = Options.getStringProperty("nashorn.compiler.symbol.stacktrace", null);
+        final String trace;
+        if (stacktrace != null) {
+            trace = stacktrace; //stacktrace always implies trace as well
+            TRACE_SYMBOLS_STACKTRACE = new HashSet<>();
+            for (StringTokenizer st = new StringTokenizer(stacktrace, ","); st.hasMoreTokens(); ) {
+                TRACE_SYMBOLS_STACKTRACE.add(st.nextToken());
+            }
+        } else {
+            trace = Options.getStringProperty("nashorn.compiler.symbol.trace", null);
+            TRACE_SYMBOLS_STACKTRACE = null;
+        }
+
+        if (trace != null) {
+            TRACE_SYMBOLS = new HashSet<>();
+            for (StringTokenizer st = new StringTokenizer(trace, ","); st.hasMoreTokens(); ) {
+                TRACE_SYMBOLS.add(st.nextToken());
+            }
+        } else {
+            TRACE_SYMBOLS = null;
+        }
+    }
 
     /**
      * Constructor
@@ -106,6 +135,7 @@
         this.type       = type;
         this.slot       = slot;
         this.fieldIndex = -1;
+        trace("CREATE SYMBOL");
     }
 
     /**
@@ -135,7 +165,7 @@
         final StringBuilder sb = new StringBuilder();
         sb.append(string.substring(0, Math.min(string.length(), max)));
 
-        while (sb.length () < max) {
+        while (sb.length() < max) {
             sb.append(' ');
         }
         return sb.toString();
@@ -263,6 +293,23 @@
         return name.hashCode() ^ block.hashCode();
     }
 
+    private static String type(final String desc) {
+        switch (desc.charAt(desc.length() - 1)) {
+        case ';':
+            return desc;//"obj";
+        case 'D':
+            return "double";
+        case 'I':
+            return "int";
+        case 'J':
+            return "long";
+        case 'Z':
+            return "boolean";
+        default:
+            return "UNKNOWN";
+        }
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb   = new StringBuilder();
@@ -270,8 +317,8 @@
 
         sb.append(name).
             append(' ').
-            append("(type=").
-            append(desc.charAt(desc.length() - 1) == ';' ? 'O' : desc).
+            append('(').
+            append(type(desc)).
             append(')');
 
         if (hasSlot()) {
@@ -602,10 +649,13 @@
         return block instanceof FunctionNode && ((FunctionNode) block).isScript();
     }
 
+
     private void trace(final String desc) {
-        if (TRACE_SYMBOL != null && TRACE_SYMBOL.equals(name)) {
+        if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) {
             Context.err("SYMBOL: '" + name + "' " + desc);
-            new Throwable().printStackTrace(Context.getCurrentErr());
+            if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
+                new Throwable().printStackTrace(Context.getContext().getErr());
+            }
         }
     }
 }
--- a/src/jdk/nashorn/internal/ir/TryNode.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/TryNode.java	Wed Jan 30 12:26:45 2013 +0100
@@ -56,6 +56,9 @@
     /** Exception symbol. */
     private Symbol exception;
 
+    /** Catchall exception for finally expansion, where applicable */
+    private Symbol finallyCatchAll;
+
     /**
      * Constructor
      *
@@ -184,6 +187,23 @@
     }
 
     /**
+     * Get the catch all symbol for this try block
+     * @return catch all symbol
+     */
+    public Symbol getFinallyCatchAll() {
+        return this.finallyCatchAll;
+    }
+
+    /**
+     * If a finally block exists, the synthetic catchall needs another symbol to
+     * store its throwable
+     * @param finallyCatchAll a symbol for the finally catch all exception
+     */
+    public void setFinallyCatchAll(final Symbol finallyCatchAll) {
+        this.finallyCatchAll = finallyCatchAll;
+    }
+
+    /**
      * Get the exit label for this try block
      * @return exit label
      */
--- a/src/jdk/nashorn/internal/ir/VarNode.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/VarNode.java	Wed Jan 30 12:26:45 2013 +0100
@@ -41,9 +41,6 @@
     /** Is this a function var node */
     private boolean isFunctionVarNode;
 
-    /** Should append VarNode to statement list? */
-    private final boolean shouldAppend;
-
     /**
      * Constructor
      *
@@ -54,25 +51,10 @@
      * @param init   init node or null if just a declaration
      */
     public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) {
-        this(source, token, finish, name, init, true);
-    }
-
-    /**
-     * Constructor
-     *
-     * @param source the source
-     * @param token  token
-     * @param finish finish
-     * @param name   name of variable
-     * @param init   init node or null if just a declaration
-     * @param shouldAppend should this turn into explicit code, like if it were an ExecuteNode
-     */
-    public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, final boolean shouldAppend) {
         super(source, token, finish);
 
         this.name  = name;
         this.init  = init;
-        this.shouldAppend = shouldAppend;
         if (init != null) {
             this.name.setIsInitializedHere();
         }
@@ -83,7 +65,6 @@
 
         this.name = (IdentNode)cs.existingOrCopy(varNode.name);
         this.init = cs.existingOrCopy(varNode.init);
-        this.shouldAppend = varNode.shouldAppend;
     }
 
     @Override
@@ -116,7 +97,6 @@
         setInit(source);
     }
 
-
     /**
      * Does this variable declaration have an init value
      * @return true if an init exists, false otherwise
@@ -235,15 +215,4 @@
         this.isFunctionVarNode = true;
     }
 
-    /**
-     * Is this the var for a for-in node or other construct that means
-     * manual or no appends of this varNode to the statement list in
-     * Lower? The default is yes as most VarNodes are auto-append to
-     * the end of the statement list when lowered
-     *
-     * @return should compiler append var node to statement list
-     */
-    public boolean shouldAppend() {
-        return shouldAppend;
-    }
 }
--- a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Wed Jan 30 12:26:45 2013 +0100
@@ -113,8 +113,8 @@
             status += " Goto ";
         }
 
-        if (node.getSymbol() != null && node.getSymbol().hasSlot()) {
-            status += " Slot " + node.getSymbol();
+        if (node.getSymbol() != null) {
+            status += node.getSymbol();
         }
 
         status = status.trim();
--- a/src/jdk/nashorn/internal/objects/NativeJSON.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/objects/NativeJSON.java	Wed Jan 30 12:26:45 2013 +0100
@@ -265,7 +265,7 @@
 
        if (node instanceof LiteralNode) {
             // check for array literal
-            if (node.tokenType() == TokenType.LBRACKET) {
+            if (node.tokenType() == TokenType.ARRAY) {
                 assert node instanceof ArrayLiteralNode;
                 final Node[] elements = ((ArrayLiteralNode)node).getValue();
 
--- a/src/jdk/nashorn/internal/objects/NativeString.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/objects/NativeString.java	Wed Jan 30 12:26:45 2013 +0100
@@ -789,6 +789,7 @@
      *
      * @param self  self reference
      * @param start start position for slice
+     * @param end   end position for slice
      * @return sliced out substring
      */
     @SpecializedFunction
@@ -808,6 +809,7 @@
      *
      * @param self  self reference
      * @param start start position for slice
+     * @param end   end position for slice
      * @return sliced out substring
      */
     @SpecializedFunction
@@ -843,9 +845,8 @@
     }
 
     private static Object splitString(String str, String separator, long limit) {
-
-        if (separator.equals("")) {
-            Object[] array = new Object[str.length()];
+        if (separator.isEmpty()) {
+            final Object[] array = new Object[str.length()];
             for (int i = 0; i < array.length; i++) {
                 array[i] = String.valueOf(str.charAt(i));
             }
@@ -856,18 +857,18 @@
         final int strLength = str.length();
         final int sepLength = separator.length();
         int pos = 0;
-        int count = 0;
+        int n = 0;
 
-        while (pos < strLength && count < limit) {
+        while (pos < strLength && n < limit) {
             int found = str.indexOf(separator, pos);
             if (found == -1) {
                 break;
             }
             elements.add(str.substring(pos, found));
-            count++;
+            n++;
             pos = found + sepLength;
         }
-        if (pos <= strLength && count < limit) {
+        if (pos <= strLength && n < limit) {
             elements.add(str.substring(pos));
         }
 
@@ -963,9 +964,8 @@
 
         if (validStart < validEnd) {
             return str.substring(validStart, validEnd);
-        } else {
-            return str.substring(validEnd, validStart);
         }
+        return str.substring(validEnd, validStart);
     }
 
     /**
--- a/src/jdk/nashorn/internal/parser/AbstractParser.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/parser/AbstractParser.java	Wed Jan 30 12:26:45 2013 +0100
@@ -96,11 +96,6 @@
         this.token        = Token.toDesc(EOL, 0, 1);
         this.type         = EOL;
         this.last         = EOL;
-        this.start        = 0;
-        this.finish       = 0;
-        this.line         = 0;
-        this.linePosition = 0;
-        this.lexer        = null;
         this.isStrictMode = strict;
     }
 
--- a/src/jdk/nashorn/internal/parser/Parser.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/parser/Parser.java	Wed Jan 30 12:26:45 2013 +0100
@@ -587,8 +587,6 @@
         script.setLastToken(token);
         script.setFinish(source.getLength() - 1);
 
-        block.addStatement(lineNumber());
-
         return script;
     }
 
@@ -617,6 +615,24 @@
     }
 
     /**
+     * Return last node in a statement list.
+     *
+     * @param statements Statement list.
+     *
+     * @return Last (non-debug) statement or null if empty block.
+     */
+    private static Node lastStatement(final List<Node> statements) {
+        for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) {
+            final Node node = statements.get(lastIndex);
+            if (!node.isDebug()) {
+                return node;
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * SourceElements :
      *      SourceElement
      *      SourceElements SourceElement
@@ -645,7 +661,7 @@
                     // check for directive prologues
                     if (checkDirective) {
                         // skip any debug statement like line number to get actual first line
-                        final Node lastStatement = Node.lastStatement(block.getStatements());
+                        final Node lastStatement = lastStatement(block.getStatements());
 
                         // get directive prologue, if any
                         final String directive = getDirective(lastStatement);
@@ -677,7 +693,7 @@
                                     }
 
                                     // verify that function name as well as parameter names
-                                    // satisfystrict mode restrictions.
+                                    // satisfy strict mode restrictions.
                                     verifyStrictIdent(function.getIdent(), "function name");
                                     for (final IdentNode param : function.getParameters()) {
                                         verifyStrictIdent(param, "function parameter");
@@ -2628,12 +2644,12 @@
                 // just expression as function body
                 final Node expr = expression();
 
-                // create a return statement
-                final ReturnNode  returnNode  = new ReturnNode(source, expr.getToken(), finish, expr, null);
-                final ExecuteNode executeNode = new ExecuteNode(source, returnNode.getToken(), finish, returnNode);
+                // create a return statement - this creates code in itself and does not need to be
+                // wrapped into an ExecuteNode
+                final ReturnNode  returnNode = new ReturnNode(source, expr.getToken(), finish, expr, null);
 
                 // add the return statement
-                functionNode.addStatement(executeNode);
+                functionNode.addStatement(returnNode);
                 functionNode.setLastToken(token);
                 functionNode.setFinish(Token.descPosition(token) + Token.descLength(token));
 
@@ -2648,8 +2664,6 @@
                 functionNode.setFinish(finish);
 
             }
-
-            block.addStatement(lineNumber());
         } finally {
             restoreBlock();
         }
--- a/src/jdk/nashorn/internal/parser/TokenType.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/parser/TokenType.java	Wed Jan 30 12:26:45 2013 +0100
@@ -171,6 +171,8 @@
     IDENT          (LITERAL,  null),
     REGEX          (LITERAL,  null),
     XML            (LITERAL,  null),
+    OBJECT         (LITERAL,  null),
+    ARRAY          (LITERAL,  null),
 
     COMMALEFT      (IR,       null),
     CONVERT        (IR,       null),
--- a/src/jdk/nashorn/internal/runtime/Context.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Wed Jan 30 12:26:45 2013 +0100
@@ -478,11 +478,11 @@
         return _timezone;
     }
 
-    /*
+    /**
      * Get the PropertyMap of the current global scope
      * @return the property map of the current global scope
      */
-    public PropertyMap getGlobalMap() {
+    public static PropertyMap getGlobalMap() {
         return Context.getGlobalTrusted().getMap();
     }
 
--- a/src/jdk/nashorn/internal/runtime/DebugLogger.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/DebugLogger.java	Wed Jan 30 12:26:45 2013 +0100
@@ -41,6 +41,8 @@
 
     private int indent;
 
+    private static final int INDENT_SPACE = 4;
+
     /**
      * Constructor
      *
@@ -93,7 +95,24 @@
      */
     public void indent(final int pos) {
         if (isEnabled) {
-           indent += pos * 4;
+           indent += pos * INDENT_SPACE;
+        }
+    }
+
+    /**
+     * Add an indent position
+     */
+    public void indent() {
+        indent += INDENT_SPACE;
+    }
+
+    /**
+     * Unindent a position
+     */
+    public void unindent() {
+        indent -= INDENT_SPACE;
+        if (indent < 0) {
+            indent = 0;
         }
     }
 
--- a/src/jdk/nashorn/internal/runtime/OptionsObject.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/OptionsObject.java	Wed Jan 30 12:26:45 2013 +0100
@@ -106,6 +106,11 @@
     /** time zone for this context */
     public final TimeZone _timezone;
 
+    /**
+     * Constructor
+     *
+     * @param context a context
+     */
     public OptionsObject(final Context context) {
         this._anon_functions = context._anon_functions;
         this._callsite_flags = context._callsite_flags;
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java	Wed Jan 30 12:26:45 2013 +0100
@@ -635,7 +635,13 @@
 
             sb.append(ScriptRuntime.safeToString(property.getKey()));
             final Class<?> ctype = property.getCurrentType();
-            sb.append(" <" + property.getClass().getSimpleName() + ":" + (ctype == null ? "undefined" : ctype.getSimpleName()) + ">");
+            sb.append(" <").
+                append(property.getClass().getSimpleName()).
+                append(':').
+                append(ctype == null ?
+                    "undefined" :
+                    ctype.getSimpleName()).
+                append('>');
         }
 
         sb.append(']');
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Wed Jan 30 12:26:45 2013 +0100
@@ -1044,6 +1044,10 @@
         set(key, value, getContext()._strict);
     }
 
+    /**
+     * Return true if the script object context is strict
+     * @return true if strict context
+     */
     public final boolean isStrictContext() {
         return getContext()._strict;
     }
@@ -1419,7 +1423,10 @@
         return (flags & IS_SCOPE) != 0;
     }
 
-    // java.util.Map-like methods to help ScriptObjectMirror implementation
+    /**
+     * Clears the properties from a ScriptObject
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     */
     public void clear() {
         final boolean strict = getContext()._strict;
         final Iterator<String> iter = propertyIterator();
@@ -1428,10 +1435,24 @@
         }
     }
 
+    /**
+     * Checks if a property with a given key is present in a ScriptObject
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @param key the key to check for
+     * @return true if a property with the given key exists, false otherwise
+     */
     public boolean containsKey(final Object key) {
         return has(key);
     }
 
+    /**
+     * Checks if a property with a given value is present in a ScriptObject
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @param value value to check for
+     * @return true if a property with the given value exists, false otherwise
+     */
     public boolean containsValue(final Object value) {
         final Iterator<Object> iter = valueIterator();
         while (iter.hasNext()) {
@@ -1442,6 +1463,13 @@
         return false;
     }
 
+    /**
+     * Returns the set of <property, value> entries that make up this
+     * ScriptObject's properties
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @return an entry set of all the properties in this object
+     */
     public Set<Map.Entry<Object, Object>> entrySet() {
         final Iterator<String> iter = propertyIterator();
         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
@@ -1452,10 +1480,23 @@
         return Collections.unmodifiableSet(entries);
     }
 
+    /**
+     * Check whether a ScriptObject contains no properties
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @return true if object has no properties
+     */
     public boolean isEmpty() {
         return !propertyIterator().hasNext();
     }
 
+    /**
+     * Return the set of keys (property names) for all properties
+     * in this ScriptObject
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @return keySet of this ScriptObject
+     */
     public Set<Object> keySet() {
         final Iterator<String> iter = propertyIterator();
         final Set<Object> keySet = new HashSet<>();
@@ -1465,12 +1506,27 @@
         return Collections.unmodifiableSet(keySet);
     }
 
+    /**
+     * Put a property in the ScriptObject
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @param key property key
+     * @param value property value
+     * @return oldValue if property with same key existed already
+     */
     public Object put(final Object key, final Object value) {
         final Object oldValue = get(key);
         set(key, value, getContext()._strict);
         return oldValue;
     }
 
+    /**
+     * Put several properties in the ScriptObject given a mapping
+     * of their keys to their values
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @param otherMap a <key,value> map of properties to add
+     */
     public void putAll(final Map<?, ?> otherMap) {
         final boolean strict = getContext()._strict;
         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
@@ -1478,12 +1534,26 @@
         }
     }
 
+    /**
+     * Remove a property from the ScriptObject.
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @param key the key of the property
+     * @return the oldValue of the removed property
+     */
     public Object remove(final Object key) {
         final Object oldValue = get(key);
         delete(key, getContext()._strict);
         return oldValue;
     }
 
+    /**
+     * Return the size of the ScriptObject - i.e. the number of properties
+     * it contains
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @return number of properties in ScriptObject
+     */
     public int size() {
         int n = 0;
         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
@@ -1492,6 +1562,12 @@
         return n;
     }
 
+    /**
+     * Return the values of the properties in the ScriptObject
+     * (java.util.Map-like method to help ScriptObjectMirror implementation)
+     *
+     * @return collection of values for the properties in this ScriptObject
+     */
     public Collection<Object> values() {
         final List<Object>     values = new ArrayList<>(size());
         final Iterator<Object> iter   = valueIterator();
--- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Wed Jan 30 12:26:45 2013 +0100
@@ -41,13 +41,13 @@
  */
 public class ScriptingFunctions {
 
-    /** Handle to implementation of {@link ScriptingFunctions#read} - Nashorn extension */
+    /** Handle to implementation of {@link ScriptingFunctions#readLine} - Nashorn extension */
     public static final MethodHandle READLINE = findOwnMH("readLine", Object.class, Object.class);
 
     /** Handle to implementation of {@link ScriptingFunctions#readFully} - Nashorn extension */
     public static final MethodHandle READFULLY = findOwnMH("readFully",     Object.class, Object.class, Object.class);
 
-    /** Handle to implementation of {@link ScriptingFunctions#read} - Nashorn extension */
+    /** Handle to implementation of {@link ScriptingFunctions#quit} - Nashorn extension */
     public static final MethodHandle QUIT = findOwnMH("quit",     Object.class, Object.class, Object.class);
 
     private ScriptingFunctions() {
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayIterator.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayIterator.java	Wed Jan 30 12:26:45 2013 +0100
@@ -25,7 +25,6 @@
 
 package jdk.nashorn.internal.runtime.arrays;
 
-import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ScriptObject;
 
 /**
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayLikeIterator.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayLikeIterator.java	Wed Jan 30 12:26:45 2013 +0100
@@ -26,7 +26,6 @@
 package jdk.nashorn.internal.runtime.arrays;
 
 import java.util.Iterator;
-import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptObject;
 
--- a/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java	Wed Jan 30 12:26:45 2013 +0100
@@ -27,7 +27,6 @@
 
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 
-import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.GlobalObject;
 import jdk.nashorn.internal.runtime.PropertyDescriptor;
 
--- a/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java	Wed Jan 30 12:26:45 2013 +0100
@@ -27,7 +27,6 @@
 
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 
-import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.GlobalObject;
 import jdk.nashorn.internal.runtime.PropertyDescriptor;
 
--- a/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java	Wed Jan 30 12:26:45 2013 +0100
@@ -446,7 +446,7 @@
          *
          * @throws Throwable if invocation fails or throws exception/error
          */
-        @SuppressWarnings("unused")
+        @SuppressWarnings({"unused", "resource"})
         public Object traceObject(final MethodHandle mh, final Object... args) throws Throwable {
             final PrintWriter out = Context.getCurrentErr();
             tracePrint(out, "ENTER ", args, null);
@@ -464,7 +464,7 @@
          *
          * @throws Throwable if invocation fails or throws exception/error
          */
-        @SuppressWarnings("unused")
+        @SuppressWarnings({"unused", "resource"})
         public void traceVoid(final MethodHandle mh, final Object... args) throws Throwable {
             final PrintWriter out = Context.getCurrentErr();
             tracePrint(out, "ENTER ", args, null);
--- a/src/jdk/nashorn/internal/runtime/options/Options.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/internal/runtime/options/Options.java	Wed Jan 30 12:26:45 2013 +0100
@@ -416,7 +416,7 @@
                     System.setProperty(value.substring(0, eq), value.substring(eq + 1));
                 } else {
                     // -Dfoo is fine. Set System property "foo" with "" as it's value
-                    if (!value.equals("")) {
+                    if (!value.isEmpty()) {
                         System.setProperty(value, "");
                     } else {
                         // do not allow empty property name
--- a/src/jdk/nashorn/tools/Shell.java	Tue Jan 29 14:25:39 2013 -0400
+++ b/src/jdk/nashorn/tools/Shell.java	Wed Jan 30 12:26:45 2013 +0100
@@ -178,7 +178,7 @@
      * @return null if there are problems with option parsing.
      */
     @SuppressWarnings("resource")
-    private Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) {
+    private static Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) {
         final PrintStream pout = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
         final PrintStream perr = err instanceof PrintStream ? (PrintStream) err : new PrintStream(err);
         final PrintWriter wout = new PrintWriter(pout, true);
@@ -230,7 +230,7 @@
      * @return error code
      * @throws IOException when any script file read results in I/O error
      */
-    private int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
+    private static int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
         final ScriptObject oldGlobal = Context.getGlobal();
         final boolean globalChanged = (oldGlobal != global);
         try {
@@ -330,7 +330,7 @@
      * @return return code
      */
     @SuppressWarnings("resource")
-    private int readEvalPrint(final Context context, final ScriptObject global) {
+    private static int readEvalPrint(final Context context, final ScriptObject global) {
         final String prompt = bundle.getString("shell.prompt");
         final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
         final PrintWriter err = context.getErr();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/access-specializer.js	Wed Jan 30 12:26:45 2013 +0100
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, 2013, 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.
+ */
+
+/**
+ * This is a simple test that checks that access specialization in FinalizeTypes is consistent.
+ * Here, a2 = 0 will be turned int {I}a2 = 0, and all would be fine and well, only we can't change
+ * the symbol type for a2 from double, and we can't as it's not a temporary. Either we have to put 
+ * a temporary in at the late finalize stage and add another assignment, or we genericize the check
+ * in CodeGenerator#Store so we detect whether a target is of the wrong type before storing. It 
+ * is hopefully very rare, and will only be a problem when assignment results that have been
+ * specialized live on the stack
+ *
+ * @test
+ * @run
+ */
+
+function f() {
+    var a0 = a1 = a2 = 0;
+    a0 = 16.1;
+    a1 = 17.1;
+    a2 = 18.1;
+}
+f();
--- a/test/script/basic/compile-octane.js.EXPECTED	Tue Jan 29 14:25:39 2013 -0400
+++ b/test/script/basic/compile-octane.js.EXPECTED	Wed Jan 30 12:26:45 2013 +0100
@@ -1,12 +1,36 @@
+Compiling... box2d.js
 Compiled OK: box2d.js
+
+Compiling... code-load.js
 Compiled OK: code-load.js
+
+Compiling... crypto.js
 Compiled OK: crypto.js
+
+Compiling... deltablue.js
 Compiled OK: deltablue.js
+
+Compiling... earley-boyer.js
 Compiled OK: earley-boyer.js
+
+Compiling... gbemu.js
 Compiled OK: gbemu.js
+
+Compiling... navier-stokes.js
 Compiled OK: navier-stokes.js
+
+Compiling... pdfjs.js
 Compiled OK: pdfjs.js
+
+Compiling... raytrace.js
 Compiled OK: raytrace.js
+
+Compiling... regexp.js
 Compiled OK: regexp.js
+
+Compiling... richards.js
 Compiled OK: richards.js
+
+Compiling... splay.js
 Compiled OK: splay.js
+
--- a/test/script/basic/run-octane.js	Tue Jan 29 14:25:39 2013 -0400
+++ b/test/script/basic/run-octane.js	Wed Jan 30 12:26:45 2013 +0100
@@ -65,11 +65,7 @@
 
 function run_one_benchmark(arg, iters) {
 
-    load(path + 'base.js');
-    load(arg);
-
     var file_name;
-
     var file = arg.split('/');
     if (file.length == 1) {
         file = arg.split('\\');
@@ -80,9 +76,17 @@
 	file.pop();
     }
     file_name = file[file.length - 1];
+
+    if (typeof compile_only !== 'undefined') {
+	print("Compiling... " + file_name);
+    }
+
+    load(path + 'base.js');
+    load(arg);
     
     if (typeof compile_only !== 'undefined') {
 	print("Compiled OK: " + file_name);
+	print("");
 	return;
     }
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/typecoerce.js	Wed Jan 30 12:26:45 2013 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010, 2013, 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.
+ */
+
+/**
+ * There was a bug in the old Lower that didn't fully propagate type information
+ * by way of assignment. 'q' in the example below would have finished a double
+ * even though it can get an object value through the assignment 'q = l' 
+ * 
+ * Furthermore, this caused type coercion to be done at q = l, and not a q = q * 2, 
+ * which is a bug. This test ensures it happens in the correct order
+ *
+ * @test
+ * @run
+ */
+
+function createObject() {
+    var obj = { valueOf: function() { print("toNumber coercion"); return 17; }}
+    return obj;
+}
+
+function f() {
+    var l = 1.2; //number
+    var q = 2.3; //number
+    for (var i = 0; i < 2; i++) {
+	q = l; // q = toNumber(l), no coercion here
+	print("assignment done");
+	q = q * 2; // q = q * 2, coercion here
+	print("multiplication done");
+	l = createObject();
+    }
+}
+
+f();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/typecoerce.js.EXPECTED	Wed Jan 30 12:26:45 2013 +0100
@@ -0,0 +1,5 @@
+assignment done
+multiplication done
+assignment done
+toNumber coercion
+multiplication done