8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
authorlagergren
Mon, 19 May 2014 15:29:42 +0200
changeset 24759 31aed7d9c02a
parent 24758 eb9658fa0120
child 24760 183f11df79ee
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation. Reviewed-by: hannesw, attila
nashorn/bin/runopt.sh
nashorn/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java
nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java
nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java
nashorn/src/jdk/nashorn/internal/codegen/Compiler.java
nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java
nashorn/src/jdk/nashorn/internal/codegen/FindScopeDepths.java
nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java
nashorn/src/jdk/nashorn/internal/codegen/Label.java
nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
nashorn/src/jdk/nashorn/internal/codegen/Lower.java
nashorn/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java
nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java
nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java
nashorn/src/jdk/nashorn/internal/ir/AccessNode.java
nashorn/src/jdk/nashorn/internal/ir/BaseNode.java
nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java
nashorn/src/jdk/nashorn/internal/ir/Block.java
nashorn/src/jdk/nashorn/internal/ir/BlockStatement.java
nashorn/src/jdk/nashorn/internal/ir/BreakableNode.java
nashorn/src/jdk/nashorn/internal/ir/BreakableStatement.java
nashorn/src/jdk/nashorn/internal/ir/CallNode.java
nashorn/src/jdk/nashorn/internal/ir/CaseNode.java
nashorn/src/jdk/nashorn/internal/ir/CatchNode.java
nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java
nashorn/src/jdk/nashorn/internal/ir/Expression.java
nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java
nashorn/src/jdk/nashorn/internal/ir/ForNode.java
nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java
nashorn/src/jdk/nashorn/internal/ir/IdentNode.java
nashorn/src/jdk/nashorn/internal/ir/IfNode.java
nashorn/src/jdk/nashorn/internal/ir/IndexNode.java
nashorn/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java
nashorn/src/jdk/nashorn/internal/ir/JumpStatement.java
nashorn/src/jdk/nashorn/internal/ir/LabelNode.java
nashorn/src/jdk/nashorn/internal/ir/Labels.java
nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java
nashorn/src/jdk/nashorn/internal/ir/LoopNode.java
nashorn/src/jdk/nashorn/internal/ir/Node.java
nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java
nashorn/src/jdk/nashorn/internal/ir/Optimistic.java
nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java
nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java
nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java
nashorn/src/jdk/nashorn/internal/ir/SplitNode.java
nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java
nashorn/src/jdk/nashorn/internal/ir/Symbol.java
nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java
nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java
nashorn/src/jdk/nashorn/internal/ir/TryNode.java
nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java
nashorn/src/jdk/nashorn/internal/ir/VarNode.java
nashorn/src/jdk/nashorn/internal/ir/WhileNode.java
nashorn/src/jdk/nashorn/internal/ir/WithNode.java
nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java
nashorn/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java
nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java
nashorn/src/jdk/nashorn/internal/objects/NativeArray.java
nashorn/src/jdk/nashorn/internal/parser/Parser.java
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java
nashorn/src/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptLoader.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
nashorn/src/jdk/nashorn/tools/Shell.java
nashorn/test/script/trusted/JDK-8006529.js
--- a/nashorn/bin/runopt.sh	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/bin/runopt.sh	Mon May 19 15:29:42 2014 +0200
@@ -1,13 +1,43 @@
 #!/bin/sh
 
+###########################################################################################
+# This is a helper script to evaluate nashorn with optimistic types
+# it produces a flight recording for every run, and uses the best 
+# known flags for performance for the current configration
+###########################################################################################
+
+# Flags to instrument lambdaform computation, caching, interpretation and compilation
+# Default compile threshold for lambdaforms is 30
 #FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
+
+
+# Flags to run trusted tests from the Nashorn test suite
 #FLAGS="-Djava.security.manager -Djava.security.policy=../build/nashorn.policy -Dnashorn.debug"
 
-FILENAME="./optimistic_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
 
+# Unique timestamped file name for JFR recordings. For JFR, we also have to
+# crank up the stack cutoff depth to 1024, because of ridiculously long lambda form
+# stack traces.
+#
+# It is also recommended that you go into $JAVA_HOME/jre/lib/jfr/default.jfc and
+# set the "method-sampling-interval" Normal and Maximum sample time as low as you
+# can go (10 ms on most platforms). The default is normally higher. The increased
+# sampling overhead is usually negligible for Nashorn runs, but the data is better
+JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+
+
+# Directory where to look for nashorn.jar in a dist folder. The default is "..", assuming
+# that we run the script from the make dir
 DIR=..
 NASHORN_JAR=$DIR/dist/nashorn.jar
 
+
+# The built Nashorn jar is placed first in the bootclasspath to override the JDK
+# nashorn.jar in $JAVA_HOME/jre/lib/ext. Thus, we also need -esa, as assertions in
+# nashorn count as system assertions in this configuration
+
+# Type profiling default level is 111, 222 adds some compile time, but is faster
+
 $JAVA_HOME/bin/java \
 $FLAGS \
 -ea \
@@ -16,17 +46,33 @@
 -Xms2G -Xmx2G \
 -XX:+UnlockCommercialFeatures \
 -XX:+FlightRecorder \
--XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=1024 \
+-XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$JFR_FILENAME,stackdepth=1024 \
 -XX:TypeProfileLevel=222 \
--XX:+UnlockExperimentalVMOptions \
--XX:+UseTypeSpeculation \
--XX:+UseMathExactIntrinsics \
--XX:+UnlockDiagnosticVMOptions \
 -cp $CLASSPATH:../build/test/classes/ \
 jdk.nashorn.tools.Shell ${@}
 
-#-Djava.security.manager= -Djava.security.policy=$DIR/build/nashorn.policy \
+# Below are flags that may come in handy, but aren't used for default runs
+
+
+# Type specialization and math intrinsic replacement should be enabled by default in 8u20 and nine,
+# keeping this flag around for experimental reasons. Replace + with - to switch it off
+#-XX:+UseTypeSpeculation \
+
+
+# Same with math intrinsics. They should be enabled by default in 8u20 and 9
+#-XX:+UseMathExactIntrinsics \
+
+
+# Add -Dnashorn.time to time the compilation phases.
+#-Dnashorn.time \
+
+
+# Add ShowHiddenFrames to get lambda form internals on the stack traces
 #-XX:+ShowHiddenFrames \
-#-XX:+PrintOptoAssembly \
-#-XX:-TieredCompilation \
-#-XX:CICompilerCount=1 \
+
+
+# Add print optoassembly to get an asm dump. This requires 1) a debug build, not product,
+# That tired compilation is switched off, for C2 only output and that the number of
+# compiler threads is set to 1 for determinsm.
+#-XX:+PrintOptoAssembly -XX:-TieredCompilation -XX:CICompilerCount=1 \
+
--- a/nashorn/src/jdk/nashorn/internal/codegen/ApplySpecialization.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ApplySpecialization.java	Mon May 19 15:29:42 2014 +0200
@@ -35,6 +35,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.CallNode;
 import jdk.nashorn.internal.ir.Expression;
@@ -45,8 +46,6 @@
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.objects.Global;
 import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.Debug;
-import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.logging.DebugLogger;
 import jdk.nashorn.internal.runtime.logging.Loggable;
 import jdk.nashorn.internal.runtime.logging.Logger;
@@ -82,39 +81,31 @@
  */
 
 @Logger(name="apply2call")
-public final class ApplySpecialization implements Loggable {
+public final class ApplySpecialization extends NodeVisitor<LexicalContext> implements Loggable {
 
     private static final boolean USE_APPLY2CALL = Options.getBooleanProperty("nashorn.apply2call", true);
 
-    private final RecompilableScriptFunctionData data;
-
-    private FunctionNode functionNode;
-
-    private final MethodType actualCallSiteType;
-
     private final DebugLogger log;
 
-    private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
+    private final Compiler compiler;
+
+    private final Set<Integer> changed = new HashSet<>();
 
-    private boolean changed;
+    private final Deque<List<IdentNode>> explodedArguments = new ArrayDeque<>();
 
-    private boolean finished;
+    private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
 
     /**
      * Apply specialization optimization. Try to explode arguments and call
      * applies as calls if they just pass on the "arguments" array and
      * "arguments" doesn't escape.
      *
-     * @param context             context
-     * @param data                recompilable script function data, which contains e.g. needs callee information
-     * @param functionNode        functionNode
-     * @param actualCallSiteType  actual call site type that we use (not Object[] varargs)
+     * @param compiler compiler
      */
-    public ApplySpecialization(final Context context, final RecompilableScriptFunctionData data, final FunctionNode functionNode, final MethodType actualCallSiteType) {
-        this.data               = data;
-        this.functionNode       = functionNode;
-        this.actualCallSiteType = actualCallSiteType;
-        this.log                = initLogger(context);
+    public ApplySpecialization(final Compiler compiler) {
+        super(new LexicalContext());
+        this.compiler = compiler;
+        this.log = initLogger(compiler.getContext());
     }
 
     @Override
@@ -128,14 +119,6 @@
     }
 
     /**
-     * Return the function node, possibly after transformation
-     * @return function node
-     */
-    public FunctionNode getFunctionNode() {
-        return functionNode;
-    }
-
-    /**
      * Arguments may only be used as args to the apply. Everything else is disqualified
      * We cannot control arguments if they escape from the method and go into an unknown
      * scope, thus we are conservative and treat any access to arguments outside the
@@ -143,12 +126,12 @@
      *
      * @return true if arguments escape
      */
-    private boolean argumentsEscape() {
+    private boolean argumentsEscape(final FunctionNode functionNode) {
 
         final Deque<Set<Expression>> stack = new ArrayDeque<>();
         //ensure that arguments is only passed as arg to apply
         try {
-            functionNode = (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+            functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
                 private boolean isCurrentArg(final Expression expr) {
                     return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
                 }
@@ -202,53 +185,60 @@
         return false;
     }
 
-    private boolean finish() {
-        finished = true;
-        return changed;
+    @Override
+    public boolean enterCallNode(final CallNode callNode) {
+        return !explodedArguments.isEmpty();
     }
 
-    /**
-     * Try to do the apply to call transformation
-     * @return true if successful, false otherwise
-     */
-    public boolean transform() {
-        if (!USE_APPLY2CALL) {
+    @Override
+    public Node leaveCallNode(final CallNode callNode) {
+        //apply needs to be a global symbol or we don't allow it
+
+        final List<IdentNode> newParams = explodedArguments.peek();
+        if (isApply(callNode)) {
+            final List<Expression> newArgs = new ArrayList<>();
+            for (final Expression arg : callNode.getArgs()) {
+                if (arg instanceof IdentNode && ARGUMENTS.equals(((IdentNode)arg).getName())) {
+                    newArgs.addAll(newParams);
+                } else {
+                    newArgs.add(arg);
+                }
+            }
+
+            changed.add(lc.getCurrentFunction().getId());
+
+            final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
+
+            log.fine("Transformed ",
+                    callNode,
+                    " from apply to call => ",
+                    newCallNode,
+                    " in ",
+                    DebugLogger.quote(lc.getCurrentFunction().getName()));
+
+            return newCallNode;
+        }
+
+        return callNode;
+    }
+
+    private boolean pushExplodedArgs(final FunctionNode functionNode) {
+        int start = 0;
+
+        final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
+        if (actualCallSiteType == null) {
             return false;
         }
-
-        if (finished) {
-            throw new AssertionError("Can't apply transform twice");
-        }
-
-
         assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
 
-        changed = false;
-
-        if (!Global.instance().isSpecialNameValid("apply")) {
-            log.fine("Apply transform disabled: apply/call overridden");
-            assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
-            return finish();
-        }
-
-        //eval can do anything to escape arguments so that is not ok
-        if (functionNode.hasEval()) {
-            return finish();
-        }
-
-        if (argumentsEscape()) {
-            return finish();
-        }
-
-        int start = 0;
-
-        if (data.needsCallee()) {
+        final TypeMap ptm = compiler.getTypeMap();
+        if (ptm.needsCallee()) {
             start++;
         }
 
         start++; //we always uses this
 
-        final List<IdentNode> params = functionNode.getParameters();
+        final List<IdentNode> params    = functionNode.getParameters();
         final List<IdentNode> newParams = new ArrayList<>();
         final long to = Math.max(params.size(), actualCallSiteType.parameterCount() - start);
         for (int i = 0; i < to; i++) {
@@ -259,45 +249,66 @@
             }
         }
 
-        // expand arguments
-        // this function has to be guarded with call and apply being builtins
-        functionNode = (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
-            @Override
-            public Node leaveCallNode(final CallNode callNode) {
-                //apply needs to be a global symbol or we don't allow it
+        explodedArguments.push(newParams);
+        return true;
+    }
+
+    @Override
+    public boolean enterFunctionNode(final FunctionNode functionNode) {
+        if (!USE_APPLY2CALL) {
+            return false;
+        }
 
-                if (isApply(callNode)) {
-                    final List<Expression> newArgs = new ArrayList<>();
-                    for (final Expression arg : callNode.getArgs()) {
-                        if (arg instanceof IdentNode && ARGUMENTS.equals(((IdentNode)arg).getName())) {
-                            newArgs.addAll(newParams);
-                        } else {
-                            newArgs.add(arg);
-                        }
-                    }
-
-                    changed = true;
+        if (!Global.instance().isSpecialNameValid("apply")) {
+            log.fine("Apply transform disabled: apply/call overridden");
+            assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
+            return false;
+        }
 
-                    final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
-                    log.fine("Transformed " + callNode + " from apply to call => " + newCallNode + " in '" + functionNode.getName() + "'");
-                    return newCallNode;
-                }
+        if (!compiler.isOnDemandCompilation()) {
+            return false;
+        }
 
-                return callNode;
-            }
-        });
+        if (functionNode.hasEval()) {
+            return false;
+        }
 
-        if (changed) {
-            functionNode = functionNode.clearFlag(null, FunctionNode.USES_ARGUMENTS).
-                    setFlag(null, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
-                    setParameters(null, newParams);
+        if (argumentsEscape(functionNode)) {
+            return false;
         }
 
-        if (log.isEnabled()) {
-            log.info(Debug.id(data) + " Successfully specialized apply to call in '" + functionNode.getName() + "' id=" + functionNode.getId() + " signature=" + actualCallSiteType + " needsCallee=" + data.needsCallee() + " " + functionNode.getSource().getURL());
+        return pushExplodedArgs(functionNode);
+    }
+
+    /**
+     * Try to do the apply to call transformation
+     * @return true if successful, false otherwise
+     */
+    @Override
+    public Node leaveFunctionNode(final FunctionNode functionNode0) {
+        FunctionNode newFunctionNode = functionNode0;
+        final String functionName = newFunctionNode.getName();
+
+        if (changed.contains(newFunctionNode.getId())) {
+            newFunctionNode = newFunctionNode.clearFlag(lc, FunctionNode.USES_ARGUMENTS).
+                    setFlag(lc, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
+                    setParameters(lc, explodedArguments.peek());
+
+            if (log.isEnabled()) {
+                log.info("Successfully specialized apply to call in '",
+                        functionName,
+                        " params=",
+                        explodedArguments.peek(),
+                        "' id=",
+                        newFunctionNode.getId(),
+                        " source=",
+                        newFunctionNode.getSource().getURL());
+            }
         }
 
-        return finish();
+        explodedArguments.pop();
+
+        return newFunctionNode;
     }
 
     private static boolean isApply(final CallNode callNode) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java	Mon May 19 15:29:42 2014 +0200
@@ -152,12 +152,12 @@
 
     private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
     private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
-    private final CompilationEnvironment env;
+    private final Compiler compiler;
 
-    public AssignSymbols(final CompilationEnvironment env) {
+    public AssignSymbols(final Compiler compiler) {
         super(new LexicalContext());
-        this.env   = env;
-        this.log   = initLogger(env.getContext());
+        this.compiler = compiler;
+        this.log   = initLogger(compiler.getContext());
         this.debug = log.isEnabled();
     }
 
@@ -216,7 +216,7 @@
         });
     }
 
-    private IdentNode compilerConstantIdentifier(CompilerConstants cc) {
+    private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
         return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
     }
 
@@ -382,8 +382,8 @@
             symbol.setFlags(flags);
         }
 
-        if((isVar || isParam) && env.useOptimisticTypes() && env.isOnDemandCompilation()) {
-            env.declareLocalSymbol(name);
+        if((isVar || isParam) && compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
+            compiler.declareLocalSymbol(name);
         }
 
         return symbol;
@@ -751,7 +751,7 @@
     }
 
     @Override
-    public Node leaveFunctionNode(FunctionNode functionNode) {
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
 
         return markProgramBlock(
                removeUnusedSlots(
@@ -842,8 +842,8 @@
         return runtimeNode;
     }
 
-    private FunctionNode markProgramBlock(FunctionNode functionNode) {
-        if (env.isOnDemandCompilation() || !functionNode.isProgram()) {
+    private FunctionNode markProgramBlock(final FunctionNode functionNode) {
+        if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) {
             return functionNode;
         }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Mon May 19 15:29:42 2014 +0200
@@ -253,16 +253,20 @@
     // Number of live locals on entry to (and thus also break from) labeled blocks.
     private final IntDeque labeledBlockBreakLiveLocals = new IntDeque();
 
+    //is this a rest of compilation
+    private final int[] continuationEntryPoints;
+
     /**
      * Constructor.
      *
      * @param compiler
      */
-    CodeGenerator(final Compiler compiler) {
+    CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
         super(new CodeGeneratorLexicalContext());
-        this.compiler      = compiler;
-        this.callSiteFlags = compiler.getEnv()._callsite_flags;
-        this.log           = initLogger(compiler.getCompilationEnvironment().getContext());
+        this.compiler                = compiler;
+        this.continuationEntryPoints = continuationEntryPoints;
+        this.callSiteFlags           = compiler.getEnv()._callsite_flags;
+        this.log                     = initLogger(compiler.getContext());
     }
 
     @Override
@@ -324,8 +328,36 @@
         return method;
     }
 
+    private boolean isRestOf() {
+        return continuationEntryPoints != null;
+    }
+
     private boolean isOptimisticOrRestOf() {
-        return useOptimisticTypes() || compiler.getCompilationEnvironment().isCompileRestOf();
+        return useOptimisticTypes() || isRestOf();
+    }
+
+    private boolean isCurrentContinuationEntryPoint(final int programPoint) {
+        return isRestOf() && getCurrentContinuationEntryPoint() == programPoint;
+    }
+
+    private int[] getContinuationEntryPoints() {
+        return isRestOf() ? continuationEntryPoints : null;
+    }
+
+    private int getCurrentContinuationEntryPoint() {
+        return isRestOf() ? continuationEntryPoints[0] : INVALID_PROGRAM_POINT;
+    }
+
+    private boolean isContinuationEntryPoint(final int programPoint) {
+        if (isRestOf()) {
+            assert continuationEntryPoints != null;
+            for (final int cep : continuationEntryPoints) {
+                if (cep == programPoint) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     /**
@@ -406,6 +438,7 @@
         }
 
         void getProto() {
+            //empty
         }
 
         @Override
@@ -443,7 +476,7 @@
         //information.
         final FunctionNode fn   = lc.getCurrentFunction();
         final int          fnId = fn.getId();
-        final int externalDepth = compiler.getCompilationEnvironment().getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName());
+        final int externalDepth = compiler.getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName());
 
         //count the number of scopes from this place to the start of the function
 
@@ -589,11 +622,11 @@
             return maybeNew(objectToNumber(narrowest), objectToNumber(widest));
         }
 
-        private static Type booleanToInt(Type t) {
+        private static Type booleanToInt(final Type t) {
             return t == Type.BOOLEAN ? Type.INT : t;
         }
 
-        private static Type objectToNumber(Type t) {
+        private static Type objectToNumber(final Type t) {
             return t.isObject() ? Type.NUMBER : t;
         }
 
@@ -805,7 +838,7 @@
             }
 
             @Override
-            public boolean enterSUB(UnaryNode unaryNode) {
+            public boolean enterSUB(final UnaryNode unaryNode) {
                 loadSUB(unaryNode, resultBounds);
                 return false;
             }
@@ -877,19 +910,19 @@
             }
 
             @Override
-            public boolean enterNOT(UnaryNode unaryNode) {
+            public boolean enterNOT(final UnaryNode unaryNode) {
                 loadNOT(unaryNode);
                 return false;
             }
 
             @Override
-            public boolean enterADD(UnaryNode unaryNode) {
+            public boolean enterADD(final UnaryNode unaryNode) {
                 loadADD(unaryNode, resultBounds);
                 return false;
             }
 
             @Override
-            public boolean enterBIT_NOT(UnaryNode unaryNode) {
+            public boolean enterBIT_NOT(final UnaryNode unaryNode) {
                 loadBIT_NOT(unaryNode);
                 return false;
             }
@@ -913,7 +946,7 @@
             }
 
             @Override
-            public boolean enterVOID(UnaryNode unaryNode) {
+            public boolean enterVOID(final UnaryNode unaryNode) {
                 loadVOID(unaryNode, resultBounds);
                 return false;
             }
@@ -1041,7 +1074,7 @@
     }
 
     private boolean useOptimisticTypes() {
-        return !lc.inSplitNode() && compiler.getCompilationEnvironment().useOptimisticTypes();
+        return !lc.inSplitNode() && compiler.useOptimisticTypes();
     }
 
     @Override
@@ -1438,7 +1471,7 @@
      * @param flags the flags that need optimism stripped from them.
      * @return flags without optimism
      */
-    static int nonOptimisticFlags(int flags) {
+    static int nonOptimisticFlags(final int flags) {
         return flags & ~(CALLSITE_OPTIMISTIC | -1 << CALLSITE_PROGRAM_POINT_SHIFT);
     }
 
@@ -1626,12 +1659,14 @@
                 } else if (symbol.isParam() && (varsInScope || hasArguments || symbol.isScope())) {
                     assert symbol.isScope()   : "scope for " + symbol + " should have been set in AssignSymbols already " + function.getName() + " varsInScope="+varsInScope+" hasArguments="+hasArguments+" symbol.isScope()=" + symbol.isScope();
                     assert !(hasArguments && symbol.hasSlot())  : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
-                    final Type paramType;
+
+                    final Type   paramType;
                     final Symbol paramSymbol;
-                    if(hasArguments) {
+
+                    if (hasArguments) {
                         assert !symbol.hasSlot()  : "slot for " + symbol + " should have been removed in Lower already ";
                         paramSymbol = null;
-                        paramType = null;
+                        paramType   = null;
                     } else {
                         paramSymbol = symbol;
                         // NOTE: We're relying on the fact here that Block.symbols is a LinkedHashMap, hence it will
@@ -1647,11 +1682,15 @@
                             }
                         }
                     }
+
                     tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, paramType, paramSymbol) {
                         //this symbol will be put fielded, we can't initialize it as undefined with a known type
                         @Override
                         public Class<?> getValueType() {
-                            return OBJECT_FIELDS_ONLY || value == null || paramType.isBoolean() ? Object.class : paramType.getTypeClass();
+                            if (OBJECT_FIELDS_ONLY || value == null || paramType == null) {
+                                return Object.class;
+                            }
+                            return paramType.isBoolean() ? Object.class : paramType.getTypeClass();
                         }
                     });
                 }
@@ -1718,13 +1757,13 @@
         }
     }
 
-    private void initializeInternalFunctionParameter(CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
+    private void initializeInternalFunctionParameter(final CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
         final Symbol symbol = initializeInternalFunctionOrSplitParameter(cc, fn, functionStart, slot);
         // Internal function params (:callee, this, and :varargs) are never expanded to multiple slots
         assert symbol.getFirstSlot() == slot;
     }
 
-    private Symbol initializeInternalFunctionOrSplitParameter(CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
+    private Symbol initializeInternalFunctionOrSplitParameter(final CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
         final Symbol symbol = fn.getBody().getExistingSymbol(cc.symbolName());
         final Type type = Type.typeFor(cc.type());
         method.initializeMethodParameter(symbol, type, functionStart);
@@ -1739,7 +1778,7 @@
      * and we need to spread them into their new locations.
      * @param function the function for which parameter-spreading code needs to be emitted
      */
-    private void expandParameterSlots(FunctionNode function) {
+    private void expandParameterSlots(final FunctionNode function) {
         final List<IdentNode> parameters = function.getParameters();
         // Calculate the total number of incoming parameter slots
         int currentIncomingSlot = function.needsCallee() ? 2 : 1;
@@ -1787,7 +1826,7 @@
      * doing an on-demand ("just-in-time") compilation, then we aren't generating code for inner functions.
      */
     private boolean compileOutermostOnly() {
-        return RecompilableScriptFunctionData.LAZY_COMPILATION || compiler.getCompilationEnvironment().isOnDemandCompilation();
+        return RecompilableScriptFunctionData.LAZY_COMPILATION || compiler.isOnDemandCompilation();
     }
 
     @Override
@@ -1818,10 +1857,8 @@
             unit = lc.pushCompileUnit(functionNode.getCompileUnit());
             assert lc.hasCompileUnits();
 
-            final CompilationEnvironment compEnv = compiler.getCompilationEnvironment();
-            final boolean isRestOf = compEnv.isCompileRestOf();
             final ClassEmitter classEmitter = unit.getClassEmitter();
-            pushMethodEmitter(isRestOf ? classEmitter.restOfMethod(functionNode) : classEmitter.method(functionNode));
+            pushMethodEmitter(isRestOf() ? classEmitter.restOfMethod(functionNode) : classEmitter.method(functionNode));
             method.setPreventUndefinedLoad();
             if(useOptimisticTypes()) {
                 lc.pushUnwarrantedOptimismHandlers();
@@ -1832,7 +1869,7 @@
 
             method.begin();
 
-            if (isRestOf) {
+            if (isRestOf()) {
                 final ContinuationInfo ci = new ContinuationInfo();
                 fnIdToContinuationInfo.put(fnId, ci);
                 method.gotoLoopStart(ci.getHandlerLabel());
@@ -1868,8 +1905,8 @@
                 markOptimistic = false;
             }
 
-            FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
-            if(markOptimistic) {
+            FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.BYTECODE_GENERATED);
+            if (markOptimistic) {
                 newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_DEOPTIMIZABLE);
             }
 
@@ -1972,6 +2009,7 @@
                 unit = lc.pushCompileUnit(arrayUnit.getCompileUnit());
 
                 final String className = unit.getUnitClassName();
+                assert unit != null;
                 final String name      = currentFunction.uniqueName(SPLIT_PREFIX.symbolName());
                 final String signature = methodDescriptor(type, ScriptFunction.class, Object.class, ScriptObject.class, type);
 
@@ -2213,7 +2251,7 @@
             public Boolean get() {
                 value.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
                     @Override
-                    public boolean enterFunctionNode(FunctionNode functionNode) {
+                    public boolean enterFunctionNode(final FunctionNode functionNode) {
                         return false;
                     }
 
@@ -2240,11 +2278,10 @@
 
         final List<MapTuple<Expression>> tuples = new ArrayList<>();
         final List<PropertyNode> gettersSetters = new ArrayList<>();
+        final int ccp = getCurrentContinuationEntryPoint();
+
         Expression protoNode = null;
-
         boolean restOfProperty = false;
-        final CompilationEnvironment env = compiler.getCompilationEnvironment();
-        final int ccp = env.getCurrentContinuationEntryPoint();
 
         for (final PropertyNode propertyNode : elements) {
             final Expression value = propertyNode.getValue();
@@ -2366,15 +2403,17 @@
         final Symbol rhsSymbol = rhs instanceof IdentNode ? ((IdentNode)rhs).getSymbol() : null;
         // One must be a "undefined" identifier, otherwise we can't get here
         assert lhsSymbol != null || rhsSymbol != null;
+
         final Symbol undefinedSymbol;
-        if(isUndefinedSymbol(lhsSymbol)) {
+        if (isUndefinedSymbol(lhsSymbol)) {
             undefinedSymbol = lhsSymbol;
         } else {
             assert isUndefinedSymbol(rhsSymbol);
             undefinedSymbol = rhsSymbol;
         }
 
-        if (!undefinedSymbol.isScope()) {
+        assert undefinedSymbol != null; //remove warning
+        if (undefinedSymbol == null || !undefinedSymbol.isScope()) {
             return false; //disallow undefined as local var or parameter
         }
 
@@ -2391,15 +2430,14 @@
             return false;
         }
 
-        final CompilationEnvironment  env = compiler.getCompilationEnvironment();
         // TODO: why?
-        if (env.isCompileRestOf()) {
+        if (isRestOf()) {
             return false;
         }
 
         //make sure that undefined has not been overridden or scoped as a local var
         //between us and global
-        if (!env.isGlobalSymbol(lc.getCurrentFunction(), "undefined")) {
+        if (!compiler.isGlobalSymbol(lc.getCurrentFunction(), "undefined")) {
             return false;
         }
 
@@ -2532,8 +2570,7 @@
      * @return true if the expression or any of its subexpressions was deoptimized in the current recompilation chain.
      */
     private boolean isDeoptimizedExpression(final Expression rootExpr) {
-        final CompilationEnvironment env = compiler.getCompilationEnvironment();
-        if(!env.isCompileRestOf()) {
+        if(!isRestOf()) {
             return false;
         }
         return new Supplier<Boolean>() {
@@ -2542,14 +2579,14 @@
             public Boolean get() {
                 rootExpr.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
                     @Override
-                    public boolean enterFunctionNode(FunctionNode functionNode) {
+                    public boolean enterFunctionNode(final FunctionNode functionNode) {
                         return false;
                     }
                     @Override
                     public boolean enterDefault(final Node node) {
                         if(!contains && node instanceof Optimistic) {
                             final int pp = ((Optimistic)node).getProgramPoint();
-                            contains = isValid(pp) && env.isContinuationEntryPoint(pp);
+                            contains = isValid(pp) && isContinuationEntryPoint(pp);
                         }
                         return !contains;
                     }
@@ -2806,12 +2843,11 @@
             return false;
         }
 
-        final CaseNode defaultCase = switchNode.getDefaultCase();
-        final Label breakLabel  = switchNode.getBreakLabel();
-        final int liveLocalsOnBreak = method.getUsedSlotsWithLiveTemporaries();
-
-        final boolean hasDefault = defaultCase != null;
-        if(hasDefault && cases.size() == 1) {
+        final CaseNode defaultCase       = switchNode.getDefaultCase();
+        final Label    breakLabel        = switchNode.getBreakLabel();
+        final int      liveLocalsOnBreak = method.getUsedSlotsWithLiveTemporaries();
+
+        if (defaultCase != null && cases.size() == 1) {
             // default case only
             assert cases.get(0) == defaultCase;
             loadAndDiscard(expression);
@@ -2822,7 +2858,7 @@
 
         // NOTE: it can still change in the tableswitch/lookupswitch case if there's no default case
         // but we need to add a synthetic default case for local variable conversions
-        Label defaultLabel = hasDefault? defaultCase.getEntry() : breakLabel;
+        Label defaultLabel = defaultCase != null ? defaultCase.getEntry() : breakLabel;
         final boolean hasSkipConversion = LocalVariableConversion.hasLiveConversion(switchNode);
 
         if (switchNode.isInteger()) {
@@ -2922,7 +2958,8 @@
                     method.ifne(caseNode.getEntry());
                 }
             }
-            if(hasDefault) {
+
+            if (defaultCase != null) {
                 method._goto(defaultLabel);
             } else {
                 method.beforeJoinPoint(switchNode);
@@ -3244,9 +3281,10 @@
         method.label(repeatLabel);
         final int liveLocalsOnContinue = method.getUsedSlotsWithLiveTemporaries();
 
-        final Block body = loopNode.getBody();
-        final Label breakLabel = loopNode.getBreakLabel();
+        final Block   body                  = loopNode.getBody();
+        final Label   breakLabel            = loopNode.getBreakLabel();
         final boolean testHasLiveConversion = test != null && LocalVariableConversion.hasLiveConversion(test);
+
         if(Expression.isAlwaysTrue(test)) {
             if(test != null) {
                 loadAndDiscard(test);
@@ -3254,12 +3292,14 @@
                     method.beforeJoinPoint(test);
                 }
             }
-        } else if(testHasLiveConversion) {
-            emitBranch(test.getExpression(), body.getEntryLabel(), true);
-            method.beforeJoinPoint(test);
-            method._goto(breakLabel);
-        } else {
-            emitBranch(test.getExpression(), breakLabel, false);
+        } else if (test != null) {
+            if (testHasLiveConversion) {
+                emitBranch(test.getExpression(), body.getEntryLabel(), true);
+                method.beforeJoinPoint(test);
+                method._goto(breakLabel);
+            } else {
+                emitBranch(test.getExpression(), breakLabel, false);
+            }
         }
 
         body.accept(this);
@@ -3376,7 +3416,7 @@
                 method._try(tryLabel, endLabel, catchLabel);
             }
 
-            boolean reachable = method.isReachable();
+            final boolean reachable = method.isReachable();
             if(reachable) {
                 popScope();
                 if(bodyCanThrow) {
@@ -3955,13 +3995,13 @@
     }
 
     @Override
-    public boolean enterLabelNode(LabelNode labelNode) {
+    public boolean enterLabelNode(final LabelNode labelNode) {
         labeledBlockBreakLiveLocals.push(lc.getUsedSlotCount());
         return true;
     }
 
     @Override
-    protected boolean enterDefault(Node node) {
+    protected boolean enterDefault(final Node node) {
         throw new AssertionError("Code generator entered node of type " + node.getClass().getName());
     }
 
@@ -3973,7 +4013,7 @@
         final Label falseLabel = new Label("ternary_false");
         final Label exitLabel  = new Label("ternary_exit");
 
-        Type outNarrowest = Type.narrowest(resultBounds.widest, Type.generic(Type.widestReturnType(trueExpr.getType(), falseExpr.getType())));
+        final Type outNarrowest = Type.narrowest(resultBounds.widest, Type.generic(Type.widestReturnType(trueExpr.getType(), falseExpr.getType())));
         final TypeBounds outBounds = resultBounds.notNarrowerThan(outNarrowest);
 
         emitBranch(test, falseLabel, false);
@@ -4255,9 +4295,8 @@
         assert lc.peek() == functionNode;
 
         final int fnId = functionNode.getId();
-        final CompilationEnvironment env = compiler.getCompilationEnvironment();
-
-        final RecompilableScriptFunctionData data = env.getScriptFunctionData(fnId);
+
+        final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fnId);
 
         assert data != null : functionNode.getName() + " has no data";
 
@@ -4276,7 +4315,7 @@
             createFunction.end();
         }
 
-        if (addInitializer && !initializedFunctionIds.contains(fnId) && !env.isOnDemandCompilation()) {
+        if (addInitializer && !initializedFunctionIds.contains(fnId) && !compiler.isOnDemandCompilation()) {
             functionNode.getCompileUnit().addFunctionInitializer(data, functionNode);
             initializedFunctionIds.add(fnId);
         }
@@ -4359,11 +4398,10 @@
         }
 
         MethodEmitter emit(final int ignoredArgCount) {
-            final CompilationEnvironment env = compiler.getCompilationEnvironment();
-            final int programPoint = optimistic.getProgramPoint();
-            final boolean optimisticOrContinuation = isOptimistic || env.isContinuationEntryPoint(programPoint);
-            final boolean currentContinuationEntryPoint = env.isCurrentContinuationEntryPoint(programPoint);
-            final int stackSizeOnEntry = method.getStackSize() - ignoredArgCount;
+            final int     programPoint                  = optimistic.getProgramPoint();
+            final boolean optimisticOrContinuation      = isOptimistic || isContinuationEntryPoint(programPoint);
+            final boolean currentContinuationEntryPoint = isCurrentContinuationEntryPoint(programPoint);
+            final int     stackSizeOnEntry              = method.getStackSize() - ignoredArgCount;
 
             // First store the values on the stack opportunistically into local variables. Doing it before loadStack()
             // allows us to not have to pop/load any arguments that are pushed onto it by loadStack() in the second
@@ -4387,7 +4425,8 @@
             final Label afterConsumeStack = isOptimistic || currentContinuationEntryPoint ? new Label("after_consume_stack") : null;
             if(isOptimistic) {
                 beginTry = new Label("try_optimistic");
-                catchLabel = new Label(afterConsumeStack.toString() + "_handler");
+                final String catchLabelName = (afterConsumeStack == null ? "" : afterConsumeStack.toString()) + "_handler";
+                catchLabel = new Label(catchLabelName);
                 method.label(beginTry);
             } else {
                 beginTry = catchLabel = null;
@@ -4414,6 +4453,7 @@
                 }
                 if(currentContinuationEntryPoint) {
                     final ContinuationInfo ci = getContinuationInfo();
+                    assert ci != null : "no continuation info found for " + lc.getCurrentFunction();
                     assert !ci.hasTargetLabel(); // No duplicate program points
                     ci.setTargetLabel(afterConsumeStack);
                     ci.getHandlerLabel().markAsOptimisticContinuationHandlerFor(afterConsumeStack);
@@ -4907,9 +4947,8 @@
                 method.dup(2);
                 method.pop();
                 loadConstant(getByteCodeSymbolNames(fn));
-                final CompilationEnvironment env = compiler.getCompilationEnvironment();
-                if (env.isCompileRestOf()) {
-                    loadConstant(env.getContinuationEntryPoints());
+                if (isRestOf()) {
+                    loadConstant(getContinuationEntryPoints());
                     method.invoke(INIT_REWRITE_EXCEPTION_REST_OF);
                 } else {
                     method.invoke(INIT_REWRITE_EXCEPTION);
@@ -5079,7 +5118,7 @@
     }
 
     private void generateContinuationHandler() {
-        if (!compiler.getCompilationEnvironment().isCompileRestOf()) {
+        if (!isRestOf()) {
             return;
         }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java	Thu May 15 15:28:51 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,529 +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 static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.AccessNode;
-import jdk.nashorn.internal.ir.Expression;
-import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.IdentNode;
-import jdk.nashorn.internal.ir.IndexNode;
-import jdk.nashorn.internal.ir.Optimistic;
-import jdk.nashorn.internal.objects.NativeArray;
-import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.FindProperty;
-import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Property;
-import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
-import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
-
-/**
- * Class for managing metadata during a compilation, e.g. which phases
- * should be run, should we use optimistic types, is this a lazy compilation
- * and various parameter types known to the runtime system
- */
-public final class CompilationEnvironment {
-    private final CompilationPhases phases;
-    private final boolean optimistic;
-
-    private final Context context;
-
-    private final ParamTypeMap paramTypes;
-
-    private RecompilableScriptFunctionData compiledFunction;
-
-    // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
-    // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
-    private final ScriptObject runtimeScope;
-
-    private boolean strict;
-
-    private final boolean onDemand;
-
-    /**
-     * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
-     * that using whatever was at program point 17 as an int failed.
-     */
-    private final Map<Integer, Type> invalidatedProgramPoints;
-
-    /**
-     * Contains the program point that should be used as the continuation entry point, as well as all previous
-     * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
-     * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
-     * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
-     * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
-     * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
-     * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
-     */
-    private final int[] continuationEntryPoints;
-
-    /**
-     * Compilation phases that a compilation goes through
-     */
-    public static final class CompilationPhases implements Iterable<CompilationPhase> {
-
-        /**
-         * Standard (non-lazy) compilation, that basically will take an entire script
-         * and JIT it at once. This can lead to long startup time and fewer type
-         * specializations
-         */
-        final static CompilationPhase[] SEQUENCE_EAGER_ARRAY = new CompilationPhase[] {
-            CompilationPhase.CONSTANT_FOLDING_PHASE,
-            CompilationPhase.LOWERING_PHASE,
-            CompilationPhase.SPLITTING_PHASE,
-            CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
-            CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
-            CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
-            CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
-            CompilationPhase.BYTECODE_GENERATION_PHASE
-        };
-
-        private final static List<CompilationPhase> SEQUENCE_EAGER;
-        static {
-            final LinkedList<CompilationPhase> eager = new LinkedList<>();
-            for (final CompilationPhase phase : SEQUENCE_EAGER_ARRAY) {
-                eager.add(phase);
-            }
-            SEQUENCE_EAGER = Collections.unmodifiableList(eager);
-        }
-
-        /** Singleton that describes a standard eager compilation */
-        public static CompilationPhases EAGER = new CompilationPhases(SEQUENCE_EAGER);
-
-        private final List<CompilationPhase> phases;
-
-        private CompilationPhases(final List<CompilationPhase> phases) {
-            this.phases = phases;
-        }
-
-        @SuppressWarnings("unused")
-        private CompilationPhases addFirst(final CompilationPhase phase) {
-            if (phases.contains(phase)) {
-                return this;
-            }
-            final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
-            list.addFirst(phase);
-            return new CompilationPhases(Collections.unmodifiableList(list));
-        }
-
-        private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
-            final LinkedList<CompilationPhase> list = new LinkedList<>();
-            for (final CompilationPhase p : phases) {
-                list.add(p);
-                if (p == phase) {
-                    list.add(newPhase);
-                }
-            }
-            return new CompilationPhases(Collections.unmodifiableList(list));
-        }
-
-        /**
-         * Turn a CompilationPhases into an optimistic one. NOP if already optimistic
-         * @param isOptimistic should this be optimistic
-         * @return new CompilationPhases that is optimistic or same if already optimistic
-         */
-        public CompilationPhases makeOptimistic(final boolean isOptimistic) {
-            return isOptimistic ? addAfter(CompilationPhase.LOWERING_PHASE, CompilationPhase.PROGRAM_POINT_PHASE) : this;
-        }
-
-        /**
-         * Turn a CompilationPhases into an optimistic one. NOP if already optimistic
-         * @return new CompilationPhases that is optimistic or same if already optimistic
-         */
-        public CompilationPhases makeOptimistic() {
-            return makeOptimistic(true);
-        }
-
-        private boolean contains(final CompilationPhase phase) {
-            return phases.contains(phase);
-        }
-
-        @Override
-        public Iterator<CompilationPhase> iterator() {
-            return phases.iterator();
-        }
-
-    }
-
-    /**
-     * Constructor
-     * @param context context
-     * @param phases compilation phases
-     * @param strict strict mode
-     */
-    public CompilationEnvironment(
-        final Context context,
-        final CompilationPhases phases,
-        final boolean strict) {
-        this(context, phases, null, null, null, null, null, strict, false);
-    }
-
-    /**
-     * Constructor for compilation environment of the rest-of method
-     *
-     * @param context context
-     * @param phases compilation phases
-     * @param strict strict mode
-     * @param compiledFunction the function being compiled
-     * @param runtimeScope the runtime scope in effect at the time of compilation. It can be used to evaluate types of
-     * scoped variables to guide the optimistic compilation, should the call to this method trigger code compilation.
-     * Can be null if current runtime scope is not known, but that might cause compilation of code that will need more
-     * subsequent deoptimization passes.
-     * @param paramTypeMap known parameter types if any exist
-     * @param invalidatedProgramPoints map of invalidated program points to their type
-     * @param continuationEntryPoint program points used as the continuation entry points in the current rest-of sequence
-     * @param onDemand is this an on demand compilation
-     */
-    public CompilationEnvironment(
-            final Context context,
-            final CompilationPhases phases,
-            final boolean strict,
-            final RecompilableScriptFunctionData compiledFunction,
-            final ScriptObject runtimeScope,
-            final ParamTypeMap paramTypeMap,
-            final Map<Integer, Type> invalidatedProgramPoints,
-            final int[] continuationEntryPoint,
-            final boolean onDemand) {
-            this(context, phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, runtimeScope, continuationEntryPoint, strict, onDemand);
-    }
-
-    /**
-     * Constructor
-     *
-     * @param context context
-     * @param phases compilation phases
-     * @param strict strict mode
-     * @param compiledFunction recompiled function
-     * @param runtimeScope the runtime scope in effect at the time of compilation. It can be used to evaluate types of
-     * scoped variables to guide the optimistic compilation, should the call to this method trigger code compilation.
-     * Can be null if current runtime scope is not known, but that might cause compilation of code that will need more
-     * subsequent deoptimization passes.
-     * @param paramTypeMap known parameter types
-     * @param invalidatedProgramPoints map of invalidated program points to their type
-     * @param onDemand is this an on demand compilation
-     */
-    public CompilationEnvironment(
-            final Context context,
-            final CompilationPhases phases,
-            final boolean strict,
-            final RecompilableScriptFunctionData compiledFunction,
-            final ScriptObject runtimeScope,
-            final ParamTypeMap paramTypeMap,
-            final Map<Integer, Type> invalidatedProgramPoints,
-            final boolean onDemand) {
-        this(context, phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, runtimeScope, null, strict, onDemand);
-    }
-
-    private CompilationEnvironment(
-            final Context context,
-            final CompilationPhases phases,
-            final ParamTypeMap paramTypes,
-            final Map<Integer, Type> invalidatedProgramPoints,
-            final RecompilableScriptFunctionData compiledFunction,
-            final ScriptObject runtimeScope,
-            final int[] continuationEntryPoints,
-            final boolean strict,
-            final boolean onDemand) {
-        this.context                  = context;
-        this.phases                   = phases;
-        this.paramTypes               = paramTypes;
-        this.continuationEntryPoints  = continuationEntryPoints;
-        this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
-        this.compiledFunction         = compiledFunction;
-        this.runtimeScope             = runtimeScope;
-        this.strict                   = strict;
-        this.optimistic               = phases.contains(CompilationPhase.PROGRAM_POINT_PHASE);
-        this.onDemand                 = onDemand;
-
-        // If entry point array is passed, it must have at least one element
-        assert continuationEntryPoints == null || continuationEntryPoints.length > 0;
-        assert !isCompileRestOf() || isOnDemandCompilation(); // isCompileRestOf => isRecompilation
-        // continuation entry points must be among the invalidated program points
-        assert !isCompileRestOf() || invalidatedProgramPoints != null && containsAll(invalidatedProgramPoints.keySet(), continuationEntryPoints);
-    }
-
-    Context getContext() {
-        return context;
-    }
-
-    private static boolean containsAll(final Set<Integer> set, final int[] array) {
-        for (int i = 0; i < array.length; ++i) {
-            if (!set.contains(array[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    void setData(final RecompilableScriptFunctionData data) {
-        assert this.compiledFunction == null : data;
-        this.compiledFunction = data;
-    }
-
-    boolean isStrict() {
-        return strict;
-    }
-
-    void setIsStrict(final boolean strict) {
-        this.strict = strict;
-    }
-
-    CompilationPhases getPhases() {
-        return phases;
-    }
-
-    /**
-     * Get the parameter type at a parameter position if known from previous runtime calls
-     * or optimistic profiles.
-     *
-     * @param functionNode function node to query
-     * @param pos parameter position
-     * @return known type of parameter 'pos' or null if no information exists
-     */
-    Type getParamType(final FunctionNode functionNode, final int pos) {
-        return paramTypes == null ? null : paramTypes.get(functionNode, pos);
-    }
-
-    /**
-     * Is this a compilation that generates the rest of method
-     * @return true if rest of generation
-     */
-    boolean isCompileRestOf() {
-        return continuationEntryPoints != null;
-    }
-
-    /**
-     * Is this an on-demand compilation triggered by a {@code RecompilableScriptFunctionData} - either a type
-     * specializing compilation, a deoptimizing recompilation, or a rest-of method compilation.
-     * @return true if this is an on-demand compilation, false if this is an eager compilation.
-     */
-    boolean isOnDemandCompilation() {
-        return onDemand; //data != null;
-    }
-
-    /**
-     * Is this program point one of the continuation entry points for the rest-of method being compiled?
-     * @param programPoint program point
-     * @return true if it is a continuation entry point
-     */
-    boolean isContinuationEntryPoint(final int programPoint) {
-        if (continuationEntryPoints != null) {
-            for (final int continuationEntryPoint : continuationEntryPoints) {
-                if (continuationEntryPoint == programPoint) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    int[] getContinuationEntryPoints() {
-        return continuationEntryPoints;
-    }
-
-    /**
-     * Is this program point the continuation entry points for the current rest-of method being compiled?
-     * @param programPoint program point
-     * @return true if it is the current continuation entry point
-     */
-    boolean isCurrentContinuationEntryPoint(final int programPoint) {
-        return hasCurrentContinuationEntryPoint() && getCurrentContinuationEntryPoint() == programPoint;
-    }
-
-    boolean hasCurrentContinuationEntryPoint() {
-        return continuationEntryPoints != null;
-    }
-
-    int getCurrentContinuationEntryPoint() {
-        // NOTE: we assert in the constructor that if the array is non-null, it has at least one element
-        return hasCurrentContinuationEntryPoint() ? continuationEntryPoints[0] : INVALID_PROGRAM_POINT;
-    }
-
-    /**
-     * Are optimistic types enabled ?
-     * @param node get the optimistic type for a node
-     * @return most optimistic type in current environment
-     */
-    Type getOptimisticType(final Optimistic node) {
-        assert useOptimisticTypes();
-        final int programPoint = node.getProgramPoint();
-        final Type validType = invalidatedProgramPoints.get(programPoint);
-        if (validType != null) {
-            return validType;
-        }
-        final Type mostOptimisticType = node.getMostOptimisticType();
-        final Type evaluatedType = getEvaluatedType(node);
-        if(evaluatedType != null) {
-            if(evaluatedType.widerThan(mostOptimisticType)) {
-                final Type newValidType = evaluatedType.isObject() || evaluatedType.isBoolean() ? Type.OBJECT : evaluatedType;
-                // Update invalidatedProgramPoints so we don't re-evaluate the expression next time. This is a heuristic
-                // as we're doing a tradeoff. Re-evaluating expressions on each recompile takes time, but it might
-                // notice a widening in the type of the expression and thus prevent an unnecessary deoptimization later.
-                // We'll presume though that the types of expressions are mostly stable, so if we evaluated it in one
-                // compilation, we'll keep to that and risk a low-probability deoptimization if its type gets widened
-                // in the future.
-                invalidatedProgramPoints.put(node.getProgramPoint(), newValidType);
-            }
-            return evaluatedType;
-        }
-        return mostOptimisticType;
-    }
-
-    /**
-     * Tells the compilation environment that a symbol of a particular name is a local variables in a function. Used
-     * with on-demand compilation, this will hide symbols of the same name from a parent scope and prevent them from
-     * being mistakenly found by the optimistic types heuristics.
-     * @param symbolName the name of the symbols to declare.
-     */
-    void declareLocalSymbol(final String symbolName) {
-        assert useOptimisticTypes() && isOnDemandCompilation() && runtimeScope != null;
-        if(runtimeScope.findProperty(symbolName, false) == null) {
-            runtimeScope.set(symbolName, ScriptRuntime.UNDEFINED, true);
-        }
-    }
-
-    private Type getEvaluatedType(final Optimistic expr) {
-        if(expr instanceof IdentNode) {
-            return runtimeScope == null ? null : getPropertyType(runtimeScope, ((IdentNode)expr).getName());
-        } else if(expr instanceof AccessNode) {
-            final AccessNode accessNode = (AccessNode)expr;
-            final Object base = evaluateSafely(accessNode.getBase());
-            if(!(base instanceof ScriptObject)) {
-                return null;
-            }
-            return getPropertyType((ScriptObject)base, accessNode.getProperty());
-        } else if(expr instanceof IndexNode) {
-            final IndexNode indexNode = (IndexNode)expr;
-            final Object base = evaluateSafely(indexNode.getBase());
-            if(!(base instanceof NativeArray)) {
-                // We only know how to deal with NativeArray. TODO: maybe manage buffers too
-                return null;
-            }
-            // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying
-            // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every
-            // optimistic int linkage attempt, even if the long value being returned in the first invocation would be
-            // representable as int. That way, we can presume that the array's optimistic type is the most optimistic
-            // type for which an element getter has a chance of executing successfully.
-            return ((NativeArray)base).getArray().getOptimisticType();
-        }
-        return null;
-    }
-
-    private static Type getPropertyType(final ScriptObject sobj, final String name) {
-        final FindProperty find = sobj.findProperty(name, true);
-        if(find == null) {
-            return null;
-        }
-
-        final Property property = find.getProperty();
-        final Class<?> propertyClass = property.getCurrentType();
-        if (propertyClass == null) {
-            // propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make
-            // a type assumption yet.
-            return null;
-        } else if (propertyClass.isPrimitive()) {
-            return Type.typeFor(propertyClass);
-        }
-
-        final ScriptObject owner = find.getOwner();
-        if(property.hasGetterFunction(owner)) {
-            // Can have side effects, so we can't safely evaluate it; since !propertyClass.isPrimitive(), it's Object.
-            return Type.OBJECT;
-        }
-
-        // Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed
-        // integer). Continue not making guesses for undefined.
-        final Object value = property.getObjectValue(owner, owner);
-        if(value == ScriptRuntime.UNDEFINED) {
-            return null;
-        }
-        return Type.typeFor(JSType.unboxedFieldType(value));
-    }
-
-    private Object evaluateSafely(final Expression expr) {
-        if(expr instanceof IdentNode) {
-            return runtimeScope == null ? null : evaluatePropertySafely(runtimeScope, ((IdentNode)expr).getName());
-        } else if(expr instanceof AccessNode) {
-            final AccessNode accessNode = (AccessNode)expr;
-            final Object base = evaluateSafely(accessNode.getBase());
-            if(!(base instanceof ScriptObject)) {
-                return null;
-            }
-            return evaluatePropertySafely((ScriptObject)base, accessNode.getProperty());
-        }
-        return null;
-    }
-
-    private static Object evaluatePropertySafely(final ScriptObject sobj, final String name) {
-        final FindProperty find = sobj.findProperty(name, true);
-        if(find == null) {
-            return null;
-        }
-        final Property property = find.getProperty();
-        final ScriptObject owner = find.getOwner();
-        if(property.hasGetterFunction(owner)) {
-            // Possible side effects; can't evaluate safely
-            return null;
-        }
-        return property.getObjectValue(owner, owner);
-    }
-
-    /**
-     * Should this compilation use optimistic types in general.
-     * If this is false we will only set non-object types to things that can
-     * be statically proven to be true.
-     * @return true if optimistic types should be used.
-     */
-    boolean useOptimisticTypes() {
-        return optimistic;
-    }
-
-    RecompilableScriptFunctionData getProgram() {
-        if (compiledFunction == null) {
-            return null;
-        }
-        return compiledFunction.getProgram();
-    }
-
-    RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
-        return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
-    }
-
-    boolean isGlobalSymbol(final FunctionNode functionNode, final String name) {
-        final RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId());
-        return data.isGlobalSymbol(functionNode, name);
-    }
-}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Mon May 19 15:29:42 2014 +0200
@@ -25,6 +25,11 @@
 
 package jdk.nashorn.internal.codegen;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BUILTINS_TRANSFORMED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_GENERATED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_INSTALLED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED;
@@ -34,14 +39,39 @@
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED;
+import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
 
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
 import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
+import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.SplitNode;
 import jdk.nashorn.internal.ir.debug.ASTWriter;
 import jdk.nashorn.internal.ir.debug.PrintVisitor;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.Timing;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
 
 /**
  * A compilation phase is a step in the processes of turning a JavaScript
@@ -52,15 +82,18 @@
      * Constant folding pass Simple constant folding that will make elementary
      * constructs go away
      */
-    CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
+    CONSTANT_FOLDING_PHASE(
+            EnumSet.of(
+                INITIALIZED,
+                PARSED)) {
         @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            return (FunctionNode)fn.accept(new FoldConstants(compiler.getCompilationEnvironment()));
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            return (FunctionNode)fn.accept(new FoldConstants(compiler));
         }
 
         @Override
         public String toString() {
-            return "[Constant Folding]";
+            return "'Constant Folding'";
         }
     },
 
@@ -71,15 +104,19 @@
      * flow cannot fall off the end. Replacing high level nodes with lower such
      * as runtime nodes where applicable.
      */
-    LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
+    LOWERING_PHASE(
+            EnumSet.of(
+                INITIALIZED,
+                PARSED,
+                CONSTANT_FOLDED)) {
         @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
             return (FunctionNode)fn.accept(new Lower(compiler));
         }
 
         @Override
         public String toString() {
-            return "[Control Flow Lowering]";
+            return "'Control Flow Lowering'";
         }
     },
 
@@ -88,15 +125,44 @@
      * optimistic ops a program point so that an UnwarrantedException knows from where
      * a guess went wrong when creating the continuation to roll back this execution
      */
-    PROGRAM_POINT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
+    PROGRAM_POINT_PHASE(
+            EnumSet.of(
+                INITIALIZED,
+                PARSED,
+                CONSTANT_FOLDED,
+                LOWERED)) {
         @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
             return (FunctionNode)fn.accept(new ProgramPoints());
         }
 
         @Override
         public String toString() {
-            return "[Program Point Calculation]";
+            return "'Program Point Calculation'";
+        }
+    },
+
+    TRANSFORM_BUILTINS_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED)) {
+        //we only do this if we have a param type map, otherwise this is not a specialized recompile
+        @Override
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new ApplySpecialization(compiler));
+            return (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+                @Override
+                public Node leaveFunctionNode(final FunctionNode node) {
+                    return node.setState(lc, BUILTINS_TRANSFORMED);
+                }
+            });
+        }
+
+        @Override
+        public String toString() {
+            return "'Builtin Replacement'";
         }
     },
 
@@ -104,18 +170,120 @@
      * Splitter Split the AST into several compile units based on a heuristic size calculation.
      * Split IR can lead to scope information being changed.
      */
-    SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
+    SPLITTING_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED,
+                    BUILTINS_TRANSFORMED)) {
         @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
-
-            final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn, true);
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            final CompileUnit  outermostCompileUnit = compiler.addCompileUnit(0L);
+            final FunctionNode newFunctionNode      = new Splitter(compiler, fn, outermostCompileUnit).split(fn, true);
 
             assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
+            assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName());
 
-            if (newFunctionNode.isStrict()) {
-                assert compiler.getCompilationEnvironment().isStrict();
-                compiler.getCompilationEnvironment().setIsStrict(true);
+            return newFunctionNode;
+        }
+
+        @Override
+        public String toString() {
+            return "'Code Splitting'";
+        }
+    },
+
+    SYMBOL_ASSIGNMENT_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED,
+                    BUILTINS_TRANSFORMED,
+                    SPLIT)) {
+        @Override
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            return (FunctionNode)fn.accept(new AssignSymbols(compiler));
+        }
+
+        @Override
+        public String toString() {
+            return "'Symbol Assignment'";
+        }
+    },
+
+    SCOPE_DEPTH_COMPUTATION_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED,
+                    BUILTINS_TRANSFORMED,
+                    SPLIT,
+                    SYMBOLS_ASSIGNED)) {
+        @Override
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            return (FunctionNode)fn.accept(new FindScopeDepths(compiler));
+        }
+
+        @Override
+        public String toString() {
+            return "'Scope Depth Computation'";
+        }
+    },
+
+    OPTIMISTIC_TYPE_ASSIGNMENT_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED,
+                    BUILTINS_TRANSFORMED,
+                    SPLIT,
+                    SYMBOLS_ASSIGNED,
+                    SCOPE_DEPTHS_COMPUTED)) {
+        @Override
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            if (compiler.useOptimisticTypes()) {
+                return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler));
+            }
+            return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED);
+        }
+
+        @Override
+        public String toString() {
+            return "'Optimistic Type Assignment'";
+        }
+    },
+
+    LOCAL_VARIABLE_TYPE_CALCULATION_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED,
+                    BUILTINS_TRANSFORMED,
+                    SPLIT,
+                    SYMBOLS_ASSIGNED,
+                    SCOPE_DEPTHS_COMPUTED,
+                    OPTIMISTIC_TYPES_ASSIGNED)) {
+        @Override
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler));
+
+            final ScriptEnvironment senv = compiler.getEnv();
+            final PrintWriter       err  = senv.getErr();
+
+            //TODO separate phase for the debug printouts for abstraction and clarity
+            if (senv._print_lower_ast) {
+                err.println("Lower AST for: " + quote(newFunctionNode.getName()));
+                err.println(new ASTWriter(newFunctionNode));
+            }
+
+            if (senv._print_lower_parse) {
+                err.println("Lower AST for: " + quote(newFunctionNode.getName()));
+                err.println(new PrintVisitor(newFunctionNode));
             }
 
             return newFunctionNode;
@@ -123,90 +291,150 @@
 
         @Override
         public String toString() {
-            return "[Code Splitting]";
-        }
-    },
-
-    SYMBOL_ASSIGNMENT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT)) {
-        @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            return (FunctionNode)fn.accept(new AssignSymbols(compiler.getCompilationEnvironment()));
-        }
-
-        @Override
-        public String toString() {
-            return "[Symbol Assignment]";
+            return "'Local Variable Type Calculation'";
         }
     },
 
-    SCOPE_DEPTH_COMPUTATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED)) {
-        @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            return (FunctionNode)fn.accept(new FindScopeDepths(compiler));
-        }
-
+    /**
+     * Reuse compile units, if they are already present. We are using the same compiler
+     * to recompile stuff
+     */
+    REUSE_COMPILE_UNITS_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED,
+                    BUILTINS_TRANSFORMED,
+                    SPLIT,
+                    SYMBOLS_ASSIGNED,
+                    SCOPE_DEPTHS_COMPUTED,
+                    OPTIMISTIC_TYPES_ASSIGNED,
+                    LOCAL_VARIABLE_TYPES_CALCULATED)) {
         @Override
-        public String toString() {
-            return "[Scope Depth Computation]";
-        }
-    },
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods";
+
+            final Map<CompileUnit, CompileUnit> map = new HashMap<>();
+            final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet();
+
+            final DebugLogger log = compiler.getLogger();
+
+            log.fine("Clearing bytecode cache");
+
+            for (final CompileUnit oldUnit : compiler.getCompileUnits()) {
+                CompileUnit newUnit = map.get(oldUnit);
+                assert map.get(oldUnit) == null;
+                final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName());
+                if (phases.isRestOfCompilation()) {
+                    sb.append("$restOf");
+                }
+                newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight());
+                log.info("Creating new compile unit ", oldUnit, " => ", newUnit);
+                map.put(oldUnit, newUnit);
+                assert newUnit != null;
+                newUnits.add(newUnit);
+            }
+
+            log.info("Replacing compile units in Compiler...");
+            compiler.replaceCompileUnits(newUnits);
+            log.info("Done");
+
+            //replace old compile units in function nodes, if any are assigned,
+            //for example by running the splitter on this function node in a previous
+            //partial code generation
+            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+                @Override
+                public Node leaveFunctionNode(final FunctionNode node) {
+                    final CompileUnit oldUnit = node.getCompileUnit();
+                    assert oldUnit != null : "no compile unit in function node";
+
+                    final CompileUnit newUnit = map.get(oldUnit);
+                    assert newUnit != null : "old unit has no mapping to new unit " + oldUnit;
 
-    OPTIMISTIC_TYPE_ASSIGNMENT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED)) {
-        @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            if(compiler.getCompilationEnvironment().useOptimisticTypes()) {
-                return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler.getCompilationEnvironment()));
-            }
-            return fn.setState(null, OPTIMISTIC_TYPES_ASSIGNED);
+                    log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName()));
+                    return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED);
+                }
+
+                @Override
+                public Node leaveSplitNode(final SplitNode node) {
+                    final CompileUnit oldUnit = node.getCompileUnit();
+                    assert oldUnit != null : "no compile unit in function node";
+
+                    final CompileUnit newUnit = map.get(oldUnit);
+                    assert newUnit != null : "old unit has no mapping to new unit " + oldUnit;
+
+                    log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName()));
+                    return node.setCompileUnit(lc, newUnit);
+                }
+
+                @Override
+                public Node leaveLiteralNode(final LiteralNode<?> node) {
+                    if (node instanceof ArrayLiteralNode) {
+                        final ArrayLiteralNode aln = (ArrayLiteralNode)node;
+                        if (aln.getUnits() == null) {
+                            return node;
+                        }
+                        final List<ArrayUnit> newArrayUnits = new ArrayList<>();
+                        for (final ArrayUnit au : aln.getUnits()) {
+                            final CompileUnit newUnit = map.get(au.getCompileUnit());
+                            assert newUnit != null;
+                            newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi()));
+                        }
+                        aln.setUnits(newArrayUnits);
+                    }
+                    return node;
+                }
+
+                @Override
+                public Node leaveDefault(final Node node) {
+                    return node.ensureUniqueLabels(lc);
+                }
+            });
+
+            return newFunctionNode;
         }
 
         @Override
         public String toString() {
-            return "[Optimistic Type Assignment]";
+            return "'Reuse Compile Units'";
         }
     },
 
-    LOCAL_VARIABLE_TYPE_CALCULATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED, OPTIMISTIC_TYPES_ASSIGNED)) {
-        @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            return (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler.getCompilationEnvironment()));
-        }
-
-        @Override
-        public String toString() {
-            return "[Local Variable Type Calculation]";
-        }
-    },
-
-    /**
+     /**
      * Bytecode generation:
      *
      * Generate the byte code class(es) resulting from the compiled FunctionNode
      */
-    BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED, OPTIMISTIC_TYPES_ASSIGNED, LOCAL_VARIABLE_TYPES_CALCULATED)) {
-        @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            final ScriptEnvironment env = compiler.getEnv();
+    BYTECODE_GENERATION_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED,
+                    BUILTINS_TRANSFORMED,
+                    SPLIT,
+                    SYMBOLS_ASSIGNED,
+                    SCOPE_DEPTHS_COMPUTED,
+                    OPTIMISTIC_TYPES_ASSIGNED,
+                    LOCAL_VARIABLE_TYPES_CALCULATED)) {
 
-            if (env._print_lower_ast) {
-                env.getErr().println(new ASTWriter(fn));
-            }
-
-            if (env._print_lower_parse) {
-                env.getErr().println(new PrintVisitor(fn));
-            }
+        @Override
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            final ScriptEnvironment senv = compiler.getEnv();
 
             FunctionNode newFunctionNode = fn;
 
-            final CodeGenerator codegen = new CodeGenerator(compiler);
+            compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation());
+            final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
             try {
                 newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
                 codegen.generateScopeCalls();
             } catch (final VerifyError e) {
-                if (env._verify_code || env._print_code) {
-                    env.getErr().println(e.getClass().getSimpleName() + ": "  + e.getMessage());
-                    if (env._dump_on_error) {
-                        e.printStackTrace(env.getErr());
+                if (senv._verify_code || senv._print_code) {
+                    senv.getErr().println(e.getClass().getSimpleName() + ": "  + e.getMessage());
+                    if (senv._dump_on_error) {
+                        e.printStackTrace(senv.getErr());
                     }
                 } else {
                     throw e;
@@ -228,11 +456,11 @@
                 compiler.addClass(className, bytecode);
 
                 // should we verify the generated code?
-                if (env._verify_code) {
+                if (senv._verify_code) {
                     compiler.getCodeInstaller().verify(bytecode);
                 }
 
-                DumpBytecode.dumpBytecode(env, compiler.getLogger(), bytecode, className);
+                DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className);
             }
 
             return newFunctionNode;
@@ -240,13 +468,139 @@
 
         @Override
         public String toString() {
-            return "[Bytecode Generation]";
+            return "'Bytecode Generation'";
         }
-    };
+    },
+
+     INSTALL_PHASE(
+            EnumSet.of(
+                    INITIALIZED,
+                    PARSED,
+                    CONSTANT_FOLDED,
+                    LOWERED,
+                    BUILTINS_TRANSFORMED,
+                    SPLIT,
+                    SYMBOLS_ASSIGNED,
+                    SCOPE_DEPTHS_COMPUTED,
+                    OPTIMISTIC_TYPES_ASSIGNED,
+                    LOCAL_VARIABLE_TYPES_CALCULATED,
+                    BYTECODE_GENERATED)) {
+
+        @Override
+        FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+            final DebugLogger log = compiler.getLogger();
+
+            final Map<String, Class<?>> installedClasses = new LinkedHashMap<>();
+
+            boolean first = true;
+            Class<?> rootClass = null;
+            long length = 0L;
+
+            for (final Entry<String, byte[]> entry : compiler.getBytecode().entrySet()) {
+                final String className = entry.getKey();
+                //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName();
+                final byte[] code = entry.getValue();
+                length += code.length;
+
+                final Class<?> clazz = compiler.getCodeInstaller().install(Compiler.binaryName(className), code);
+                if (first) {
+                    rootClass = clazz;
+                    first = false;
+                }
+                installedClasses.put(className, clazz);
+            }
+
+            if (rootClass == null) {
+                throw new CompilationException("Internal compiler error: root class not found!");
+            }
+
+            // do these in a loop, to use only one privileged action - this significantly
+            // reduces class installation overhead
+            log.fine("Preparing source and constant fields...");
+            try {
+                final Object[] constants = compiler.getConstantData().toArray();
+                // Need doPrivileged because these fields are private
+                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+                    @Override
+                    public Void run() throws Exception {
+                        for (final Entry<String, Class<?>> entry : installedClasses.entrySet()) {
+                            final Class<?> clazz = entry.getValue();
+                            log.fine("Initializing source for ", clazz);
+                            //use reflection to write source and constants table to installed classes
+                            final Field sourceField    = clazz.getDeclaredField(SOURCE.symbolName());
+                            sourceField.setAccessible(true);
+                            sourceField.set(null, compiler.getSource());
 
+                            log.fine("Initializing constants for ", clazz);
+                            final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
+                            constantsField.setAccessible(true);
+                            constantsField.set(null, constants);
+                        }
+                        return null;
+                    }
+                });
+            } catch (final PrivilegedActionException e) {
+                throw new RuntimeException(e);
+            }
+            log.fine("Done");
+
+            // index recompilable script function datas in the constant pool
+            final Map<RecompilableScriptFunctionData, RecompilableScriptFunctionData> rfns = new IdentityHashMap<>();
+            for (final Object constant: compiler.getConstantData().getConstants()) {
+                if (constant instanceof RecompilableScriptFunctionData) {
+                    final RecompilableScriptFunctionData rfn = (RecompilableScriptFunctionData)constant;
+                    rfns.put(rfn, rfn);
+                }
+            }
+
+            // initialize function in the compile units
+            for (final CompileUnit unit : compiler.getCompileUnits()) {
+                unit.setCode(installedClasses.get(unit.getUnitClassName()));
+                unit.initializeFunctionsCode();
+            }
+
+            // remove installed bytecode from table in case compiler is reused
+            for (final String className : installedClasses.keySet()) {
+                log.fine("Removing installed class ", quote(className), " from bytecode table...");
+                compiler.removeClass(className);
+            }
+
+            if (log.isEnabled()) {
+                final StringBuilder sb = new StringBuilder();
+
+                sb.append("Installed class '").
+                    append(rootClass.getSimpleName()).
+                    append('\'').
+                    append(" [").
+                    append(rootClass.getName()).
+                    append(", size=").
+                    append(length).
+                    append(" bytes, ").
+                    append(compiler.getCompileUnits().size()).
+                    append(" compile unit(s)]");
+
+                log.info(sb.toString());
+            }
+
+            return setStates(fn.setRootClass(null, rootClass), BYTECODE_INSTALLED);
+        }
+
+        @Override
+        public String toString() {
+            return "'Class Installation'";
+         }
+     };
+
+    /** pre conditions required for function node to which this transform is to be applied */
     private final EnumSet<CompilationState> pre;
+
+    /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
     private long startTime;
+
+    /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
     private long endTime;
+
+    /** boolean that is true upon transform completion */
     private boolean isFinished;
 
     private CompilationPhase(final EnumSet<CompilationState> pre) {
@@ -254,36 +608,56 @@
     }
 
     boolean isApplicable(final FunctionNode functionNode) {
+        //this means that all in pre are present in state. state can be larger
         return functionNode.hasState(pre);
     }
 
+    private static FunctionNode setStates(final FunctionNode functionNode, final CompilationState state) {
+        return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+            @Override
+            public Node leaveFunctionNode(final FunctionNode fn) {
+                return fn.setState(lc, state);
+           }
+        });
+    }
+
     /**
      * Start a compilation phase
+     * @param compiler
      * @param functionNode function to compile
      * @return function node
      */
-    protected FunctionNode begin(final FunctionNode functionNode) {
-        if (pre != null) {
-            // check that everything in pre is present
-            for (final CompilationState state : pre) {
-                assert functionNode.hasState(state);
-            }
-            // check that nothing else is present
-            for (final CompilationState state : CompilationState.values()) {
-                assert !(functionNode.hasState(state) && !pre.contains(state));
-            }
-        }
+    protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) {
+        compiler.getLogger().indent();
+
+        assert pre != null;
 
-        startTime = System.currentTimeMillis();
-        return functionNode;
-    }
+        if (!isApplicable(functionNode)) {
+            final StringBuilder sb = new StringBuilder("Compilation phase ");
+            sb.append(this).
+                append(" is not applicable to ").
+                append(quote(functionNode.getName())).
+                append("\n\tFunctionNode state = ").
+                append(functionNode.getState()).
+                append("\n\tRequired state     = ").
+                append(this.pre);
+
+            throw new CompilationException(sb.toString());
+         }
+
+         startTime = System.currentTimeMillis();
+
+         return functionNode;
+     }
 
     /**
      * End a compilation phase
+     * @param compiler the compiler
      * @param functionNode function node to compile
-     * @return fucntion node
+     * @return function node
      */
-    protected FunctionNode end(final FunctionNode functionNode) {
+    protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) {
+        compiler.getLogger().unindent();
         endTime = System.currentTimeMillis();
         Timing.accumulateTime(toString(), endTime - startTime);
 
@@ -303,13 +677,26 @@
         return endTime;
     }
 
-    abstract FunctionNode transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
+    abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException;
 
-    final FunctionNode apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
-        if (!isApplicable(functionNode)) {
-            throw new CompilationException("compile phase not applicable: " + this + " to " + functionNode.getName() + " state=" + functionNode.getState());
-        }
-        return end(transform(compiler, begin(functionNode)));
+    /**
+     * Apply a transform to a function node, returning the transfored function node. If the transform is not
+     * applicable, an exception is thrown. Every transform requires the function to have a certain number of
+     * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor
+     * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states.
+     *
+     * @param compiler     compiler
+     * @param phases       current complete pipeline of which this phase is one
+     * @param functionNode function node to transform
+     *
+     * @return transformed function node
+     *
+     * @throws CompilationException if function node lacks the state required to run the transform on it
+     */
+    final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException {
+        assert phases.contains(this);
+
+        return end(compiler, transform(compiler, phases, begin(compiler, functionNode)));
     }
 
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java	Mon May 19 15:29:42 2014 +0200
@@ -28,6 +28,8 @@
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.Set;
+import java.util.TreeSet;
+
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 
@@ -36,7 +38,7 @@
  */
 public final class CompileUnit implements Comparable<CompileUnit> {
     /** Current class name */
-    private final String className;
+    private String className;
 
     /** Current class generator */
     private ClassEmitter classEmitter;
@@ -67,24 +69,22 @@
 
         @Override
         public boolean equals(final Object obj) {
-            if(obj == null || obj.getClass() != FunctionInitializer.class) {
+            if (obj == null || obj.getClass() != FunctionInitializer.class) {
                 return false;
             }
             final FunctionInitializer other = (FunctionInitializer)obj;
             return data == other.data && functionNode == other.functionNode;
         }
-
-
-    }
-
-    CompileUnit(final String className, final ClassEmitter classEmitter) {
-        this(className, classEmitter, 0L);
     }
 
     CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) {
         this.className    = className;
+        this.weight       = initialWeight;
         this.classEmitter = classEmitter;
-        this.weight       = initialWeight;
+    }
+
+    static Set<CompileUnit> createCompileUnitSet() {
+        return new TreeSet<>();
     }
 
     /**
@@ -126,7 +126,7 @@
     }
 
     void initializeFunctionsCode() {
-        for(final FunctionInitializer init: functionInitializers) {
+        for(final FunctionInitializer init : functionInitializers) {
             init.initializeCode();
         }
         functionInitializers = Collections.emptySet();
@@ -173,13 +173,25 @@
         return className;
     }
 
-    @Override
-    public String toString() {
-        return "[classname=" + className + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']';
+    /**
+     * Reset the class name for this compile unit
+     * @param className new class name
+     */
+    public void setUnitClassName(final String className) {
+        this.className = className;
+    }
+
+    private static String shortName(final String name) {
+        return name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1);
     }
 
     @Override
-    public int compareTo(CompileUnit o) {
+    public String toString() {
+        return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']';
+    }
+
+    @Override
+    public int compareTo(final CompileUnit o) {
         return className.compareTo(o.className);
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Mon May 19 15:29:42 2014 +0200
@@ -27,43 +27,39 @@
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
 
 import java.io.File;
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.HashMap;
-import java.util.IdentityHashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
-import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
 
 import jdk.internal.dynalink.support.NameCodec;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
-import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.Optimistic;
 import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
 import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
 import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
+import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.Timing;
 import jdk.nashorn.internal.runtime.logging.DebugLogger;
@@ -85,10 +81,13 @@
     /** Name of the objects package */
     public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
 
-    private Source source;
+    private final Source source;
+
+    private final String sourceName;
 
-    private String sourceName;
-    private String sourceURL;
+    private final String sourceURL;
+
+    private final boolean optimistic;
 
     private final Map<String, byte[]> bytecode;
 
@@ -96,21 +95,184 @@
 
     private final ConstantData constantData;
 
-    private final CompilationEnvironment compilationEnv;
-
-    private final ScriptEnvironment scriptEnv;
-
-    private String scriptName;
-
     private final CodeInstaller<ScriptEnvironment> installer;
 
     /** logger for compiler, trampolines, splits and related code generation events
      *  that affect classes */
     private final DebugLogger log;
 
+    private final Context context;
+
+    private final TypeMap types;
+
+    // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
+    // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
+    private final TypeEvaluator typeEvaluator;
+
+    private final boolean strict;
+
+    private final boolean onDemand;
+
+    /**
+     * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
+     * that using whatever was at program point 17 as an int failed.
+     */
+    private final Map<Integer, Type> invalidatedProgramPoints;
+
+    /**
+     * Compile unit name of first compile unit - this prefix will be used for all
+     * classes that a compilation generates.
+     */
+    private final String firstCompileUnitName;
+
+    /**
+     * Contains the program point that should be used as the continuation entry point, as well as all previous
+     * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
+     * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
+     * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
+     * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
+     * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
+     * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
+     */
+    private final int[] continuationEntryPoints;
+
+    /**
+     * ScriptFunction data for what is being compile, where applicable.
+     * TODO: make this immutable, propagate it through the CompilationPhases
+     */
+    private RecompilableScriptFunctionData compiledFunction;
+
     private static boolean initialized = false;
 
     /**
+     * Compilation phases that a compilation goes through
+     */
+    public static class CompilationPhases implements Iterable<CompilationPhase> {
+
+        /** Singleton that describes a standard eager compilation - this includes code installation */
+        public final static CompilationPhases COMPILE_ALL = new CompilationPhases(
+                "Compile all",
+                new CompilationPhase[] {
+                        CompilationPhase.CONSTANT_FOLDING_PHASE,
+                        CompilationPhase.LOWERING_PHASE,
+                        CompilationPhase.PROGRAM_POINT_PHASE,
+                        CompilationPhase.TRANSFORM_BUILTINS_PHASE,
+                        CompilationPhase.SPLITTING_PHASE,
+                        CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
+                        CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
+                        CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
+                        CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
+                        CompilationPhase.BYTECODE_GENERATION_PHASE,
+                        CompilationPhase.INSTALL_PHASE
+                });
+
+        /** Compile all for a rest of method */
+        public final static CompilationPhases COMPILE_ALL_RESTOF =
+                COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE);
+
+        /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
+        public final static CompilationPhases COMPILE_ALL_NO_INSTALL =
+                COMPILE_ALL.
+                removeLast().
+                setDescription("Compile without install");
+
+        /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
+        public final static CompilationPhases COMPILE_UPTO_BYTECODE =
+                COMPILE_ALL.
+                removeLast().
+                removeLast().
+                setDescription("Compile upto bytecode");
+
+        /**
+         * Singleton that describes back end of method generation, given that we have generated the normal
+         * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
+         */
+        public final static CompilationPhases COMPILE_FROM_BYTECODE = new CompilationPhases(
+                "Generate bytecode and install",
+                new CompilationPhase[] {
+                        CompilationPhase.BYTECODE_GENERATION_PHASE,
+                        CompilationPhase.INSTALL_PHASE
+                });
+
+        /**
+         * Singleton that describes restOf method generation, given that we have generated the normal
+         * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
+         */
+        public final static CompilationPhases COMPILE_FROM_BYTECODE_RESTOF =
+                COMPILE_FROM_BYTECODE.
+                addFirst(CompilationPhase.REUSE_COMPILE_UNITS_PHASE).
+                setDescription("Generate bytecode and install - RestOf method");
+
+        private final List<CompilationPhase> phases;
+
+        private final String desc;
+
+        private CompilationPhases(final String desc, final CompilationPhase... phases) {
+            this.desc = desc;
+
+            final List<CompilationPhase> newPhases = new LinkedList<>();
+            newPhases.addAll(Arrays.asList(phases));
+            this.phases = Collections.unmodifiableList(newPhases);
+        }
+
+        @Override
+        public String toString() {
+            return "'" + desc + "' " + phases.toString();
+        }
+
+        private CompilationPhases setDescription(final String desc) {
+            return new CompilationPhases(desc, phases.toArray(new CompilationPhase[phases.size()]));
+        }
+
+        private CompilationPhases removeLast() {
+            final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
+            list.removeLast();
+            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+        }
+
+        private CompilationPhases addFirst(final CompilationPhase phase) {
+            if (phases.contains(phase)) {
+                return this;
+            }
+            final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
+            list.addFirst(phase);
+            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+        }
+
+        private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
+            final LinkedList<CompilationPhase> list = new LinkedList<>();
+            for (final CompilationPhase p : phases) {
+                list.add(p);
+                if (p == phase) {
+                    list.add(newPhase);
+                }
+            }
+            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+        }
+
+        boolean contains(final CompilationPhase phase) {
+            return phases.contains(phase);
+        }
+
+        @Override
+        public Iterator<CompilationPhase> iterator() {
+            return phases.iterator();
+        }
+
+        boolean isRestOfCompilation() {
+            return this == COMPILE_ALL_RESTOF || this == COMPILE_FROM_BYTECODE_RESTOF;
+        }
+
+        String toString(final String prefix) {
+            final StringBuilder sb = new StringBuilder();
+            for (final CompilationPhase phase : phases) {
+                sb.append(prefix).append(phase).append('\n');
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
      * This array contains names that need to be reserved at the start
      * of a compile, to avoid conflict with variable names later introduced.
      * See {@link CompilerConstants} for special names used for structures
@@ -125,29 +287,80 @@
         ARGUMENTS.symbolName()
     };
 
-    private void initCompiler(final String className, final FunctionNode functionNode) {
-        this.source = functionNode.getSource();
-        this.sourceName = functionNode.getSourceName();
-        this.sourceURL = functionNode.getSourceURL();
+    // per instance
+    private final int compilationId = COMPILATION_ID.getAndIncrement();
+
+    // per instance
+    private final AtomicInteger nextCompileUnitId = new AtomicInteger(0);
+
+    private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0);
 
-        if (functionNode.isStrict()) {
-            compilationEnv.setIsStrict(true);
-        }
-
-        final String name       = className + '$' + safeSourceName(functionNode.getSource());
-        final String uniqueName = functionNode.uniqueName(name);
-
-        this.scriptName = uniqueName;
+    /**
+     * Constructor
+     *
+     * @param context                  context
+     * @param env                      script environment
+     * @param installer                code installer
+     * @param source                   source to compile
+     * @param sourceURL                source URL, or null if not present
+     * @param isStrict                 is this a strict compilation
+     */
+    public Compiler(
+            final Context context,
+            final ScriptEnvironment env,
+            final CodeInstaller<ScriptEnvironment> installer,
+            final Source source,
+            final String sourceURL,
+            final boolean isStrict) {
+        this(context, env, installer, source, sourceURL, isStrict, false, null, null, null, null, null);
     }
 
-    private Compiler(final CompilationEnvironment compilationEnv, final ScriptEnvironment scriptEnv, final CodeInstaller<ScriptEnvironment> installer) {
-        this.scriptEnv      = scriptEnv;
-        this.compilationEnv = compilationEnv;
-        this.installer      = installer;
-        this.constantData   = new ConstantData();
-        this.compileUnits   = new TreeSet<>();
-        this.bytecode       = new LinkedHashMap<>();
-        this.log            = initLogger(compilationEnv.getContext());
+    /**
+     * Constructor
+     *
+     * @param context                  context
+     * @param env                      script environment
+     * @param installer                code installer
+     * @param source                   source to compile
+     * @param sourceURL                source URL, or null if not present
+     * @param isStrict                 is this a strict compilation
+     * @param isOnDemand               is this an on demand compilation
+     * @param compiledFunction         compiled function, if any
+     * @param types                    parameter and return value type information, if any is known
+     * @param invalidatedProgramPoints invalidated program points for recompilation
+     * @param continuationEntryPoints  continuation entry points for restof method
+     * @param runtimeScope             runtime scope for recompilation type lookup in {@code TypeEvaluator}
+     */
+    public Compiler(
+            final Context context,
+            final ScriptEnvironment env,
+            final CodeInstaller<ScriptEnvironment> installer,
+            final Source source,
+            final String sourceURL,
+            final boolean isStrict,
+            final boolean isOnDemand,
+            final RecompilableScriptFunctionData compiledFunction,
+            final TypeMap types,
+            final Map<Integer, Type> invalidatedProgramPoints,
+            final int[] continuationEntryPoints,
+            final ScriptObject runtimeScope) {
+        this.context                  = context;
+        this.installer                = installer;
+        this.constantData             = new ConstantData();
+        this.compileUnits             = CompileUnit.createCompileUnitSet();
+        this.bytecode                 = new LinkedHashMap<>();
+        this.log                      = initLogger(context);
+        this.source                   = source;
+        this.sourceURL                = sourceURL;
+        this.sourceName               = FunctionNode.getSourceName(source, sourceURL);
+        this.onDemand                 = isOnDemand;
+        this.compiledFunction         = compiledFunction;
+        this.types                    = types;
+        this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
+        this.continuationEntryPoints  = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
+        this.typeEvaluator            = new TypeEvaluator(this, runtimeScope);
+        this.firstCompileUnitName     = firstCompileUnitName(context.getEnv());
+        this.strict                   = isStrict;
 
         if (!initialized) {
             initialized = true;
@@ -155,24 +368,53 @@
                 log.warning("Running without optimistic types. This is a configuration that may be deprecated.");
             }
         }
+
+        this.optimistic = ScriptEnvironment.globalOptimistic();
+    }
+
+    private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) {
+        String baseName = new File(source.getName()).getName();
+
+        final int index = baseName.lastIndexOf(".js");
+        if (index != -1) {
+            baseName = baseName.substring(0, index);
+        }
+
+        baseName = baseName.replace('.', '_').replace('-', '_');
+        if (!env._loader_per_compile) {
+            baseName = baseName + installer.getUniqueScriptId();
+        }
+
+        final String mangled = NameCodec.encode(baseName);
+        return mangled != null ? mangled : baseName;
     }
 
-    /**
-     * Constructor - common entry point for generating code.
-     * @param env compilation environment
-     * @param installer code installer
-     */
-    public Compiler(final CompilationEnvironment env, final CodeInstaller<ScriptEnvironment> installer) {
-        this(env, installer.getOwner(), installer);
+    private String firstCompileUnitName(final ScriptEnvironment env) {
+        final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
+                append('/').
+                append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()).
+                append('$');
+
+        if (isOnDemandCompilation()) {
+            sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX);
+        }
+
+        if (compilationId > 0) {
+            sb.append(compilationId).append('$');
+        }
+
+        sb.append(Compiler.safeSourceName(env, installer, source));
+
+        return sb.toString();
     }
 
-    /**
-     * ScriptEnvironment constructor for compiler. Used only from Shell and --compile-only flag
-     * No code installer supplied
-     * @param scriptEnv script environment
-     */
-    public Compiler(final ScriptEnvironment scriptEnv) {
-        this(new CompilationEnvironment(Context.getContext(), CompilationPhases.EAGER, scriptEnv._strict), scriptEnv, null);
+    void declareLocalSymbol(final String symbolName) {
+        typeEvaluator.declareLocalSymbol(symbolName);
+    }
+
+    void setData(final RecompilableScriptFunctionData data) {
+        assert this.compiledFunction == null : data;
+        this.compiledFunction = data;
     }
 
     @Override
@@ -181,11 +423,268 @@
     }
 
     @Override
-    public DebugLogger initLogger(final Context context) {
-        return context.getLogger(this.getClass());
+    public DebugLogger initLogger(final Context ctxt) {
+        return ctxt.getLogger(this.getClass());
+    }
+
+    boolean isOnDemandCompilation() {
+        return onDemand;
+    }
+
+    boolean useOptimisticTypes() {
+        return optimistic;
+    }
+
+    Context getContext() {
+        return context;
+    }
+
+    Type getOptimisticType(final Optimistic node) {
+        return typeEvaluator.getOptimisticType(node);
+    }
+
+    void addInvalidatedProgramPoint(final int programPoint, final Type type) {
+        invalidatedProgramPoints.put(programPoint, type);
+    }
+
+    TypeMap getTypeMap() {
+        return types;
+    }
+
+    MethodType getCallSiteType(final FunctionNode fn) {
+        if (types == null || !isOnDemandCompilation()) {
+            return null;
+        }
+        return types.getCallSiteType(fn);
+    }
+
+    Type getParamType(final FunctionNode fn, final int pos) {
+        return types == null ? null : types.get(fn, pos);
+    }
+
+    /**
+     * Do a compilation job
+     *
+     * @param functionNode function node to compile
+     * @param phases phases of compilation transforms to apply to function
+
+     * @return transformed function
+     *
+     * @throws CompilationException if error occurs during compilation
+     */
+    public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
+
+        log.info("Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", phases);
+        log.indent();
+
+        final String name = DebugLogger.quote(functionNode.getName());
+
+        FunctionNode newFunctionNode = functionNode;
+
+        for (final String reservedName : RESERVED_NAMES) {
+            newFunctionNode.uniqueName(reservedName);
+        }
+
+        final boolean fine = log.levelFinerThanOrEqual(Level.FINE);
+        final boolean info = log.levelFinerThanOrEqual(Level.INFO);
+
+        long time = 0L;
+
+        for (final CompilationPhase phase : phases) {
+            if (fine) {
+                log.fine("Phase ", phase.toString(), " starting for ", name);
+            }
+
+            newFunctionNode = phase.apply(this, phases, newFunctionNode);
+
+            if (getEnv()._print_mem_usage) {
+                printMemoryUsage(functionNode, phase.toString());
+            }
+
+            final long duration = Timing.isEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L;
+            time += duration;
+
+            if (fine) {
+                final StringBuilder sb = new StringBuilder();
+
+                sb.append("Phase ").
+                    append(phase.toString()).
+                    append(" done for function ").
+                    append(name);
+
+                if (duration > 0L) {
+                    sb.append(" in ").
+                        append(duration).
+                        append(" ms ");
+                }
+
+                log.fine(sb);
+            }
+        }
+
+        log.unindent();
+
+        if (info) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("Compile job for ").
+                append(newFunctionNode.getSource()).
+                append(':').
+                append(DebugLogger.quote(newFunctionNode.getName())).
+                append(" finished");
+
+            if (time > 0L) {
+                sb.append(" in ").
+                    append(time).
+                    append(" ms");
+            }
+
+            log.info(sb);
+        }
+
+        return newFunctionNode;
+    }
+
+    Source getSource() {
+        return source;
+    }
+
+    Map<String, byte[]> getBytecode() {
+        return Collections.unmodifiableMap(bytecode);
     }
 
-    private void printMemoryUsage(final String phaseName, final FunctionNode functionNode) {
+    byte[] getBytecode(final String className) {
+        return bytecode.get(className);
+    }
+
+    CompileUnit getFirstCompileUnit() {
+        assert !compileUnits.isEmpty();
+        return compileUnits.iterator().next();
+    }
+
+    Set<CompileUnit> getCompileUnits() {
+        return compileUnits;
+    }
+
+    ConstantData getConstantData() {
+        return constantData;
+    }
+
+    CodeInstaller<ScriptEnvironment> getCodeInstaller() {
+        return installer;
+    }
+
+    void addClass(final String name, final byte[] code) {
+        bytecode.put(name, code);
+    }
+
+    void removeClass(final String name) {
+        assert bytecode.get(name) != null;
+        bytecode.remove(name);
+    }
+
+    ScriptEnvironment getEnv() {
+        return context.getEnv();
+    }
+
+    String getSourceURL() {
+        return sourceURL;
+    }
+
+    String nextCompileUnitName() {
+        final StringBuilder sb = new StringBuilder(firstCompileUnitName);
+        final int cuid = nextCompileUnitId.getAndIncrement();
+        if (cuid > 0) {
+            sb.append("$cu").append(cuid);
+        }
+
+        return sb.toString();
+    }
+
+    void clearCompileUnits() {
+        compileUnits.clear();
+    }
+
+    CompileUnit addCompileUnit(final long initialWeight) {
+        final CompileUnit compileUnit = createCompileUnit(initialWeight);
+        compileUnits.add(compileUnit);
+        log.fine("Added compile unit ", compileUnit);
+        return compileUnit;
+    }
+
+    CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
+        final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
+        final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
+
+        classEmitter.begin();
+
+        final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE));
+        initMethod.begin();
+        initMethod.load(Type.OBJECT, 0);
+        initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
+        initMethod.returnVoid();
+        initMethod.end();
+
+        return compileUnit;
+    }
+
+    private CompileUnit createCompileUnit(final long initialWeight) {
+        return createCompileUnit(nextCompileUnitName(), initialWeight);
+    }
+
+    boolean isStrict() {
+        return strict;
+    }
+
+    void replaceCompileUnits(final Set<CompileUnit> newUnits) {
+        compileUnits.clear();
+        compileUnits.addAll(newUnits);
+    }
+
+    CompileUnit findUnit(final long weight) {
+        for (final CompileUnit unit : compileUnits) {
+            if (unit.canHold(weight)) {
+                unit.addWeight(weight);
+                return unit;
+            }
+        }
+
+        return addCompileUnit(weight);
+    }
+
+    /**
+     * Convert a package/class name to a binary name.
+     *
+     * @param name Package/class name.
+     * @return Binary name.
+     */
+    public static String binaryName(final String name) {
+        return name.replace('/', '.');
+    }
+
+    RecompilableScriptFunctionData getProgram() {
+        if (compiledFunction == null) {
+            return null;
+        }
+        return compiledFunction.getProgram();
+    }
+
+    RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
+        return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
+    }
+
+    boolean isGlobalSymbol(final FunctionNode fn, final String name) {
+        return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name);
+    }
+
+    int[] getContinuationEntryPoints() {
+        return continuationEntryPoints;
+    }
+
+    Type getInvalidatedProgramPointType(final int programPoint) {
+        return invalidatedProgramPoints.get(programPoint);
+    }
+
+    private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) {
         if (!log.isEnabled()) {
             return;
         }
@@ -227,304 +726,4 @@
             }
         }
     }
-
-    CompilationEnvironment getCompilationEnvironment() {
-        return compilationEnv;
-    }
-
-    /**
-     * Execute the compilation this Compiler was created with with default class name
-     * @param functionNode function node to compile from its current state
-     * @throws CompilationException if something goes wrong
-     * @return function node that results from code transforms
-     */
-    public FunctionNode compile(final FunctionNode functionNode) throws CompilationException {
-        return compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
-    }
-
-    /**
-     * Execute the compilation this Compiler was created with
-     * @param className    class name for the compile
-     * @param functionNode function node to compile from its current state
-     * @throws CompilationException if something goes wrong
-     * @return function node that results from code transforms
-     */
-    public FunctionNode compile(final String className, final FunctionNode functionNode) throws CompilationException {
-        try {
-            return compileInternal(className, functionNode);
-        } catch (final AssertionError e) {
-            throw new AssertionError("Assertion failure compiling " + functionNode.getSource(), e);
-        }
-    }
-
-    private FunctionNode compileInternal(final String className, final FunctionNode functionNode) throws CompilationException {
-        FunctionNode newFunctionNode = functionNode;
-
-        initCompiler(className, newFunctionNode); //TODO move this state into functionnode?
-
-        for (final String reservedName : RESERVED_NAMES) {
-            newFunctionNode.uniqueName(reservedName);
-        }
-
-        final boolean fine = log.levelFinerThanOrEqual(Level.FINE);
-        final boolean info = log.levelFinerThanOrEqual(Level.INFO);
-
-        long time = 0L;
-
-        for (final CompilationPhase phase : compilationEnv.getPhases()) {
-            newFunctionNode = phase.apply(this, newFunctionNode);
-
-            if (scriptEnv._print_mem_usage) {
-                printMemoryUsage(phase.toString(), newFunctionNode);
-            }
-
-            final long duration = Timing.isEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L;
-            time += duration;
-
-            if (fine) {
-                final StringBuilder sb = new StringBuilder();
-
-                sb.append(phase.toString()).
-                    append(" done for function '").
-                    append(newFunctionNode.getName()).
-                    append('\'');
-
-                if (duration > 0L) {
-                    sb.append(" in ").
-                        append(duration).
-                        append(" ms ");
-                }
-
-                log.fine(sb);
-            }
-        }
-
-        if (info) {
-            final StringBuilder sb = new StringBuilder();
-            sb.append("Compile job for '").
-                append(newFunctionNode.getSource()).
-                append(':').
-                append(newFunctionNode.getName()).
-                append("' finished");
-
-            if (time > 0L) {
-                sb.append(" in ").
-                    append(time).
-                    append(" ms");
-            }
-
-            log.info(sb);
-        }
-
-        return newFunctionNode;
-    }
-
-    private Class<?> install(final String className, final byte[] code) {
-        log.fine("Installing class ", className);
-
-        final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
-
-        try {
-            final Object[] constants = getConstantData().toArray();
-            // Need doPrivileged because these fields are private
-            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
-                @Override
-                public Void run() throws Exception {
-                    //use reflection to write source and constants table to installed classes
-                    final Field sourceField    = clazz.getDeclaredField(SOURCE.symbolName());
-                    final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
-                    sourceField.setAccessible(true);
-                    constantsField.setAccessible(true);
-                    sourceField.set(null, source);
-                    constantsField.set(null, constants);
-                    return null;
-                }
-            });
-        } catch (final PrivilegedActionException e) {
-            throw new RuntimeException(e);
-        }
-
-        return clazz;
-    }
-
-    /**
-     * Install compiled classes into a given loader
-     * @param functionNode function node to install - must be in {@link CompilationState#EMITTED} state
-     * @return root script class - if there are several compile units they will also be installed
-     */
-    public Class<?> install(final FunctionNode functionNode) {
-        final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
-
-        assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has unexpected compilation state";
-
-        final Map<String, Class<?>> installedClasses = new HashMap<>();
-
-        final String   rootClassName = firstCompileUnitName();
-        final byte[]   rootByteCode  = bytecode.get(rootClassName);
-        final Class<?> rootClass     = install(rootClassName, rootByteCode);
-
-        int length = rootByteCode.length;
-
-        installedClasses.put(rootClassName, rootClass);
-
-        for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
-            final String className = entry.getKey();
-            if (className.equals(rootClassName)) {
-                continue;
-            }
-            final byte[] code = entry.getValue();
-            length += code.length;
-
-            installedClasses.put(className, install(className, code));
-        }
-
-        final Map<RecompilableScriptFunctionData, RecompilableScriptFunctionData> rfns = new IdentityHashMap<>();
-        for(final Object constant: getConstantData().constants) {
-            if(constant instanceof RecompilableScriptFunctionData) {
-                final RecompilableScriptFunctionData rfn = (RecompilableScriptFunctionData)constant;
-                rfns.put(rfn, rfn);
-            }
-        }
-
-        for (final CompileUnit unit : compileUnits) {
-            unit.setCode(installedClasses.get(unit.getUnitClassName()));
-            unit.initializeFunctionsCode();
-        }
-
-        final StringBuilder sb;
-        if (log.isEnabled()) {
-            sb = new StringBuilder();
-            sb.append("Installed class '").
-                append(rootClass.getSimpleName()).
-                append('\'').
-                append(" bytes=").
-                append(length).
-                append('.');
-            if (bytecode.size() > 1) {
-                sb.append(' ').append(bytecode.size()).append(" compile units.");
-            }
-        } else {
-            sb = null;
-        }
-
-        if (Timing.isEnabled()) {
-            final long duration = System.currentTimeMillis() - t0;
-            Timing.accumulateTime("[Code Installation]", duration);
-            if (sb != null) {
-                sb.append(" Install time: ").append(duration).append(" ms");
-            }
-        }
-
-        if (sb != null) {
-            log.fine(sb);
-        }
-
-        return rootClass;
-    }
-
-    Set<CompileUnit> getCompileUnits() {
-        return compileUnits;
-    }
-
-    ConstantData getConstantData() {
-        return constantData;
-    }
-
-    CodeInstaller<ScriptEnvironment> getCodeInstaller() {
-        return installer;
-    }
-
-    void addClass(final String name, final byte[] code) {
-        bytecode.put(name, code);
-    }
-
-    ScriptEnvironment getEnv() {
-        return this.scriptEnv;
-    }
-
-    String getSourceURL() {
-        return sourceURL;
-    }
-
-    private String safeSourceName(final Source src) {
-        String baseName = new File(src.getName()).getName();
-
-        final int index = baseName.lastIndexOf(".js");
-        if (index != -1) {
-            baseName = baseName.substring(0, index);
-        }
-
-        baseName = baseName.replace('.', '_').replace('-', '_');
-        if (!scriptEnv._loader_per_compile) {
-            baseName = baseName + installer.getUniqueScriptId();
-        }
-
-        final String mangled = NameCodec.encode(baseName);
-        return mangled != null ? mangled : baseName;
-    }
-
-    private int nextCompileUnitIndex() {
-        return compileUnits.size() + 1;
-    }
-
-    String firstCompileUnitName() {
-        return SCRIPTS_PACKAGE + '/' + scriptName;
-    }
-
-    private String nextCompileUnitName() {
-        return firstCompileUnitName() + '$' + nextCompileUnitIndex();
-    }
-
-    CompileUnit addCompileUnit(final long initialWeight) {
-        return addCompileUnit(nextCompileUnitName(), initialWeight);
-    }
-
-    CompileUnit addCompileUnit(final String unitClassName) {
-        return addCompileUnit(unitClassName, 0L);
-    }
-
-    private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
-        final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
-        compileUnits.add(compileUnit);
-        log.fine("Added compile unit ", compileUnit);
-        return compileUnit;
-    }
-
-    private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
-        final ClassEmitter classEmitter = new ClassEmitter(compilationEnv.getContext(), sourceName, unitClassName, compilationEnv.isStrict());
-        final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
-
-        classEmitter.begin();
-
-        final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE));
-        initMethod.begin();
-        initMethod.load(Type.OBJECT, 0);
-        initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
-        initMethod.returnVoid();
-        initMethod.end();
-
-        return compileUnit;
-    }
-
-    CompileUnit findUnit(final long weight) {
-        for (final CompileUnit unit : compileUnits) {
-            if (unit.canHold(weight)) {
-                unit.addWeight(weight);
-                return unit;
-            }
-        }
-
-        return addCompileUnit(weight);
-    }
-
-    /**
-     * Convert a package/class name to a binary name.
-     *
-     * @param name Package/class name.
-     * @return Binary name.
-     */
-    public static String binaryName(final String name) {
-        return name.replace('/', '.');
-    }
-
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java	Mon May 19 15:29:42 2014 +0200
@@ -27,9 +27,12 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
 
@@ -37,7 +40,7 @@
  * Manages constants needed by code generation.  Objects are maintained in an
  * interning maps to remove duplicates.
  */
-class ConstantData {
+final class ConstantData {
     /** Constant table. */
     final List<Object> constants;
 
@@ -206,6 +209,10 @@
         return index;
     }
 
+    Collection<Object> getConstants() {
+        return Collections.unmodifiableList(constants);
+    }
+
     Object[] toArray() {
         return constants.toArray();
     }
--- a/nashorn/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Mon May 19 15:29:42 2014 +0200
@@ -60,7 +60,6 @@
 final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Loggable {
 
     private final Compiler compiler;
-    private final CompilationEnvironment env;
     private final Map<Integer, Map<Integer, RecompilableScriptFunctionData>> fnIdToNestedFunctions = new HashMap<>();
     private final Map<Integer, Map<String, Integer>> externalSymbolDepths = new HashMap<>();
     private final Map<Integer, Set<String>> internalSymbols = new HashMap<>();
@@ -73,8 +72,7 @@
     FindScopeDepths(final Compiler compiler) {
         super(new LexicalContext());
         this.compiler = compiler;
-        this.env      = compiler.getCompilationEnvironment();
-        this.log      = initLogger(compiler.getCompilationEnvironment().getContext());
+        this.log      = initLogger(compiler.getContext());
     }
 
     @Override
@@ -165,7 +163,7 @@
 
     @Override
     public boolean enterFunctionNode(final FunctionNode functionNode) {
-        if (env.isOnDemandCompilation()) {
+        if (compiler.isOnDemandCompilation()) {
             return true;
         }
 
@@ -189,8 +187,8 @@
         final String name = functionNode.getName();
         FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.SCOPE_DEPTHS_COMPUTED);
 
-        if (env.isOnDemandCompilation()) {
-            final RecompilableScriptFunctionData data = env.getScriptFunctionData(newFunctionNode.getId());
+        if (compiler.isOnDemandCompilation()) {
+            final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId());
             assert data != null : newFunctionNode.getName() + " lacks data";
             if (data.inDynamicContext()) {
                 log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope.");
@@ -214,7 +212,7 @@
         final String      allocatorClassName = Compiler.binaryName(getClassName(fieldCount));
         final PropertyMap allocatorMap       = PropertyMap.newMap(null, 0, fieldCount, 0);
         final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
-                compiler.getCompilationEnvironment().getContext(),
+                compiler.getContext(),
                 newFunctionNode,
                 compiler.getCodeInstaller(),
                 allocatorClassName,
@@ -231,7 +229,7 @@
                 fnIdToNestedFunctions.get(parentFn.getId()).put(fnId, data);
             }
         } else {
-            env.setData(data);
+            compiler.setData(data);
         }
 
         if (isDynamicScopeBoundary(functionNode)) {
@@ -269,7 +267,7 @@
 
     @Override
     public boolean enterBlock(final Block block) {
-        if (env.isOnDemandCompilation()) {
+        if (compiler.isOnDemandCompilation()) {
             return true;
         }
 
@@ -290,7 +288,7 @@
         block.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
             @Override
             public final boolean enterDefault(final Node node) {
-                if (!env.isOnDemandCompilation()) {
+                if (!compiler.isOnDemandCompilation()) {
                     if (node instanceof IdentNode) {
                         final Symbol symbol = ((IdentNode)node).getSymbol();
                         if (symbol != null && symbol.isScope()) {
@@ -351,7 +349,7 @@
 
     @Override
     public Node leaveBlock(final Block block) {
-        if (env.isOnDemandCompilation()) {
+        if (compiler.isOnDemandCompilation()) {
             return block;
         }
         if (isDynamicScopeBoundary(block)) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java	Mon May 19 15:29:42 2014 +0200
@@ -60,9 +60,9 @@
 
     private final DebugLogger log;
 
-    FoldConstants(final CompilationEnvironment env) {
+    FoldConstants(final Compiler compiler) {
         super(new LexicalContext());
-        this.log = initLogger(env.getContext());
+        this.log = initLogger(compiler.getContext());
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/codegen/Label.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Label.java	Mon May 19 15:29:42 2014 +0200
@@ -30,6 +30,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
+
 import jdk.nashorn.internal.codegen.types.Type;
 
 /**
@@ -93,7 +94,7 @@
         }
 
         Type peek(final int n) {
-            int pos = sp - 1 - n;
+            final int pos = sp - 1 - n;
             return pos < 0 ? null : data[pos];
         }
 
@@ -168,6 +169,7 @@
         private void mergeVariableTypes(final Stack joinOrigin, final int toSlot) {
             final ListIterator<Type> it1 = localVariableTypes.listIterator();
             final Iterator<Type> it2 = joinOrigin.localVariableTypes.iterator();
+
             for(int i = 0; i < toSlot; ++i) {
                 final Type thisType = it1.next();
                 final Type otherType = it2.next();
@@ -194,11 +196,13 @@
             mergeVariableTypes(joinOrigin, firstTemp);
         }
 
-        private int getFirstDeadLocal(List<Type> types) {
+        private int getFirstDeadLocal(final List<Type> types) {
             int i = types.size();
             for(final ListIterator<Type> it = types.listIterator(i);
                 it.hasPrevious() && it.previous() == Type.UNKNOWN;
-                --i); // no body
+                --i) {
+                // no body
+            }
 
             // Respect symbol boundaries; we never chop off half a symbol's storage
             while(!symbolBoundary.get(i - 1)) {
@@ -253,7 +257,7 @@
          * @return a list of widest local variable slot types.
          */
         List<Type> getWidestLiveLocals(final List<Type> lvarTypes) {
-            List<Type> widestLiveLocals = new ArrayList<>(lvarTypes);
+            final List<Type> widestLiveLocals = new ArrayList<>(lvarTypes);
             boolean keepNextValue = true;
             final int size = widestLiveLocals.size();
             for(int i = size - 1; i-- > 0;) {
@@ -523,7 +527,6 @@
         this.id   = label.id;
     }
 
-
     jdk.internal.org.objectweb.asm.Label getLabel() {
         if (this.label == null) {
             this.label = new jdk.internal.org.objectweb.asm.Label();
--- a/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java	Mon May 19 15:29:42 2014 +0200
@@ -106,7 +106,7 @@
     }
 
     private static class JumpTarget {
-        private List<JumpOrigin> origins = new LinkedList<>();
+        private final List<JumpOrigin> origins = new LinkedList<>();
         private Map<Symbol, LvarType> types = Collections.emptyMap();
 
         void addOrigin(final JoinPredecessor originNode, final Map<Symbol, LvarType> originTypes) {
@@ -143,7 +143,7 @@
 
     private LocalVariableConversion createConversion(final Symbol symbol, final LvarType branchLvarType,
             final Map<Symbol, LvarType> joinLvarTypes, final LocalVariableConversion next) {
-        LvarType targetType = joinLvarTypes.get(symbol);
+        final LvarType targetType = joinLvarTypes.get(symbol);
         assert targetType != null;
         if(targetType == branchLvarType) {
             return next;
@@ -193,7 +193,8 @@
                         union = cloneMap(types2);
                     }
                 }
-                if(!(matches1 || matches2)) {
+                if(!(matches1 || matches2) && union != null) { //remove overly enthusiastic "union can be null" warning
+                    assert union != null;
                     union.put(symbol, widest);
                 }
             }
@@ -344,7 +345,7 @@
         // Int64 type anyway, so this loss of precision is actually more conformant to the specification...
         return LvarType.values()[Math.max(t1.ordinal(), t2.ordinal())];
     }
-    private final CompilationEnvironment env;
+    private final Compiler compiler;
     private final Map<Label, JumpTarget> jumpTargets = new IdentityHashMap<>();
     // Local variable type mapping at the currently evaluated point. No map instance is ever modified; setLvarType() always
     // allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current
@@ -378,9 +379,9 @@
     // variables).
     private final Deque<Label> catchLabels = new ArrayDeque<>();
 
-    LocalVariableTypesCalculator(final CompilationEnvironment env) {
+    LocalVariableTypesCalculator(final Compiler compiler) {
         super(new LexicalContext());
-        this.env = env;
+        this.compiler = compiler;
     }
 
     private JumpTarget createJumpTarget(final Label label) {
@@ -449,7 +450,7 @@
 
     @Override
     public boolean enterBlock(final Block block) {
-        for(Symbol symbol: block.getSymbols()) {
+        for(final Symbol symbol: block.getSymbols()) {
             if(symbol.isBytecodeLocal() && getLocalVariableTypeOrNull(symbol) == null) {
                 setType(symbol, LvarType.UNDEFINED);
             }
@@ -569,7 +570,7 @@
                 // Parameter is not necessarily bytecode local as it can be scoped due to nested context use, but it
                 // must have a slot if we aren't in a function with vararg signature.
                 assert symbol.hasSlot();
-                final Type callSiteParamType = env.getParamType(functionNode, pos);
+                final Type callSiteParamType = compiler.getParamType(functionNode, pos);
                 final LvarType paramType = callSiteParamType == null ? LvarType.OBJECT : toLvarType(callSiteParamType);
                 setType(symbol, paramType);
                 // Make sure parameter slot for its incoming value is not marked dead. NOTE: this is a heuristic. Right
@@ -1079,7 +1080,7 @@
             // If the function is split, the ":return" symbol is used and needs a slot. Note we can't mark the return
             // symbol as used in enterSplitNode, as we don't know the final return type of the function earlier than
             // here.
-            Symbol retSymbol = getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN);
+            final Symbol retSymbol = getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN);
             retSymbol.setHasSlotFor(returnType);
             retSymbol.setNeedsSlot(true);
         }
@@ -1100,10 +1101,10 @@
         FunctionNode newFunction = functionNode;
         final NodeVisitor<LexicalContext> applyChangesVisitor = new NodeVisitor<LexicalContext>(new LexicalContext()) {
             private boolean inOuterFunction = true;
-            private Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>();
+            private final Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>();
 
             @Override
-            protected boolean enterDefault(Node node) {
+            protected boolean enterDefault(final Node node) {
                 if(!inOuterFunction) {
                     return false;
                 }
@@ -1115,7 +1116,7 @@
 
             @Override
             public boolean enterFunctionNode(final FunctionNode fn) {
-                if(env.isOnDemandCompilation()) {
+                if(compiler.isOnDemandCompilation()) {
                     // Only calculate nested function local variable types if we're doing eager compilation
                     return false;
                 }
@@ -1125,7 +1126,7 @@
 
             @SuppressWarnings("fallthrough")
             @Override
-            public Node leaveBinaryNode(BinaryNode binaryNode) {
+            public Node leaveBinaryNode(final BinaryNode binaryNode) {
                 if(binaryNode.isComparison()) {
                     final Expression lhs = binaryNode.lhs();
                     final Expression rhs = binaryNode.rhs();
@@ -1173,16 +1174,16 @@
             }
 
             @Override
-            public Node leaveFunctionNode(FunctionNode nestedFunctionNode) {
+            public Node leaveFunctionNode(final FunctionNode nestedFunctionNode) {
                 inOuterFunction = true;
                 final FunctionNode newNestedFunction = (FunctionNode)nestedFunctionNode.accept(
-                        new LocalVariableTypesCalculator(env));
+                        new LocalVariableTypesCalculator(compiler));
                 lc.replace(nestedFunctionNode, newNestedFunction);
                 return newNestedFunction;
             }
 
             @Override
-            public Node leaveIdentNode(IdentNode identNode) {
+            public Node leaveIdentNode(final IdentNode identNode) {
                 final IdentNode original = (IdentNode)joinPredecessors.pop();
                 final Symbol symbol = identNode.getSymbol();
                 if(symbol == null) {
@@ -1205,7 +1206,7 @@
             }
 
             @Override
-            public Node leaveLiteralNode(LiteralNode<?> literalNode) {
+            public Node leaveLiteralNode(final LiteralNode<?> literalNode) {
                 if(literalNode instanceof ArrayLiteralNode) {
                     ((ArrayLiteralNode)literalNode).analyze();
                 }
@@ -1370,9 +1371,9 @@
             final Symbol symbol = ((IdentNode)node).getSymbol();
             conversion = createConversion(symbol, branchLvarTypes.get(symbol), joinLvarTypes, null);
         } else {
-            for(Map.Entry<Symbol, LvarType> entry: branchLvarTypes.entrySet()) {
+            for(final Map.Entry<Symbol, LvarType> entry: branchLvarTypes.entrySet()) {
                 final Symbol symbol = entry.getKey();
-                LvarType branchLvarType = entry.getValue();
+                final LvarType branchLvarType = entry.getValue();
                 conversion = createConversion(symbol, branchLvarType, joinLvarTypes, conversion);
             }
         }
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Mon May 19 15:29:42 2014 +0200
@@ -146,7 +146,7 @@
         });
 
         this.installer = compiler.getCodeInstaller();
-        this.log       = initLogger(compiler.getCompilationEnvironment().getContext());
+        this.log       = initLogger(compiler.getContext());
     }
 
     @Override
@@ -268,12 +268,12 @@
     }
 
     @Override
-    public Node leaveIN(BinaryNode binaryNode) {
+    public Node leaveIN(final BinaryNode binaryNode) {
         return new RuntimeNode(binaryNode);
     }
 
     @Override
-    public Node leaveINSTANCEOF(BinaryNode binaryNode) {
+    public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
         return new RuntimeNode(binaryNode);
     }
 
@@ -289,7 +289,7 @@
     }
 
     @Override
-    public Node leaveCaseNode(CaseNode caseNode) {
+    public Node leaveCaseNode(final CaseNode caseNode) {
         // Try to represent the case test as an integer
         final Node test = caseNode.getTest();
         if (test instanceof LiteralNode) {
@@ -526,7 +526,7 @@
         return spliceFinally(newTryNode, rethrows, finallyBody);
     }
 
-    private TryNode ensureUnconditionalCatch(TryNode tryNode) {
+    private TryNode ensureUnconditionalCatch(final TryNode tryNode) {
         final List<CatchNode> catches = tryNode.getCatches();
         if(catches == null || catches.isEmpty() || catches.get(catches.size() - 1).getExceptionCondition() == null) {
             return tryNode;
--- a/nashorn/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java	Mon May 19 15:29:42 2014 +0200
@@ -66,16 +66,16 @@
  */
 final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
 
-    final CompilationEnvironment env;
+    final Compiler compiler;
 
     // Per-function bit set of program points that must never be optimistic.
     final Deque<BitSet> neverOptimistic = new ArrayDeque<>();
     // Per-function depth of split nodes
     final IntDeque splitDepth = new IntDeque();
 
-    OptimisticTypesCalculator(final CompilationEnvironment env) {
+    OptimisticTypesCalculator(final Compiler compiler) {
         super(new LexicalContext());
-        this.env = env;
+        this.compiler = compiler;
     }
 
     @Override
@@ -85,7 +85,7 @@
     }
 
     @Override
-    public boolean enterPropertyNode(PropertyNode propertyNode) {
+    public boolean enterPropertyNode(final PropertyNode propertyNode) {
         if(propertyNode.getKeyName().equals(ScriptObject.PROTO_PROPERTY_NAME)) {
             tagNeverOptimistic(propertyNode.getValue());
         }
@@ -149,7 +149,7 @@
 
     @Override
     public boolean enterFunctionNode(final FunctionNode functionNode) {
-        if (!neverOptimistic.isEmpty() && env.isOnDemandCompilation()) {
+        if (!neverOptimistic.isEmpty() && compiler.isOnDemandCompilation()) {
             // This is a nested function, and we're doing on-demand compilation. In these compilations, we never descend
             // into nested functions.
             return false;
@@ -190,13 +190,13 @@
     }
 
     @Override
-    public boolean enterSplitNode(SplitNode splitNode) {
+    public boolean enterSplitNode(final SplitNode splitNode) {
         splitDepth.getAndIncrement();
         return true;
     }
 
     @Override
-    public Node leaveSplitNode(SplitNode splitNode) {
+    public Node leaveSplitNode(final SplitNode splitNode) {
         final int depth = splitDepth.decrementAndGet();
         assert depth >= 0;
         return splitNode;
@@ -257,7 +257,7 @@
     private Expression leaveOptimistic(final Optimistic opt) {
         final int pp = opt.getProgramPoint();
         if(isValid(pp) && !inSplitNode() && !neverOptimistic.peek().get(pp)) {
-            return (Expression)opt.setType(env.getOptimisticType(opt));
+            return (Expression)opt.setType(compiler.getOptimisticType(opt));
         }
         return (Expression)opt;
     }
--- a/nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java	Thu May 15 15:28:51 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2010-2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  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.lang.invoke.MethodType;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.FunctionNode;
-
-/**
- * A data structure that maps one or several function nodes (by their unique id:s, not by
- * the FunctionNode object itself, due to copy on write changing it several times through
- * code generation.
- */
-public class ParamTypeMap {
-    final Map<Integer, Type[]> map = new HashMap<>();
-
-    /**
-     * Constructor
-     * @param functionNode functionNode
-     * @param type         method type found at runtime corresponding to parameter guess
-     */
-    public ParamTypeMap(final FunctionNode functionNode, final MethodType type) {
-        this(functionNode.getId(), type);
-    }
-
-    /**
-     * Constructor
-     * @param functionNodeId function node id
-     * @param type           method type found at runtime corresponding to parameter guess
-     */
-    public ParamTypeMap(final int functionNodeId, final MethodType type) {
-        final Type[] types = new Type[type.parameterCount()];
-        int pos = 0;
-        for (final Class<?> p : type.parameterArray()) {
-            types[pos++] = Type.typeFor(p);
-        }
-        map.put(functionNodeId, types);
-    }
-
-    ParamTypeMap(final Map<FunctionNode, Type[]> typeMap) {
-        for (final Map.Entry<FunctionNode, Type[]> entry : typeMap.entrySet()) {
-            map.put(entry.getKey().getId(), entry.getValue());
-        }
-    }
-    /**
-     * Get the parameter type for this parameter position, or
-     * null if now known
-     * @param functionNode functionNode
-     * @param pos position
-     * @return parameter type for this callsite if known
-     */
-    Type get(final FunctionNode functionNode, final int pos) {
-        final Type[] types = map.get(functionNode.getId());
-        assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this;
-        if (types != null && pos < types.length) {
-            return types[pos];
-        }
-        return null;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("\n[ParamTypeMap]\n");
-        if (map.isEmpty()) {
-            sb.append("\t{}");
-        } else {
-            for (final Map.Entry<Integer, Type[]> entry : map.entrySet()) {
-                sb.append('\t').append(entry.getKey() + "=>" + ((entry.getValue() == null) ? "[]" : Arrays.toString(entry.getValue()))).append('\n');
-            }
-        }
-        return sb.toString();
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java	Mon May 19 15:29:42 2014 +0200
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  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.AccessNode;
+import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.Optimistic;
+import jdk.nashorn.internal.objects.NativeArray;
+import jdk.nashorn.internal.runtime.FindProperty;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * Functionality for using a runtime scope to look up value types.
+ * Used during recompilation.
+ */
+final class TypeEvaluator {
+    final Compiler compiler;
+    final ScriptObject runtimeScope;
+
+    TypeEvaluator(final Compiler compiler, final ScriptObject runtimeScope) {
+        this.compiler = compiler;
+        this.runtimeScope = runtimeScope;
+    }
+
+    Type getOptimisticType(final Optimistic node) {
+        assert compiler.useOptimisticTypes();
+
+        final int  programPoint = node.getProgramPoint();
+        final Type validType    = compiler.getInvalidatedProgramPointType(programPoint);
+
+        if (validType != null) {
+            return validType;
+        }
+
+        final Type mostOptimisticType = node.getMostOptimisticType();
+        final Type evaluatedType      = getEvaluatedType(node);
+
+        if (evaluatedType != null) {
+            if (evaluatedType.widerThan(mostOptimisticType)) {
+                final Type newValidType = evaluatedType.isObject() || evaluatedType.isBoolean() ? Type.OBJECT : evaluatedType;
+                // Update invalidatedProgramPoints so we don't re-evaluate the expression next time. This is a heuristic
+                // as we're doing a tradeoff. Re-evaluating expressions on each recompile takes time, but it might
+                // notice a widening in the type of the expression and thus prevent an unnecessary deoptimization later.
+                // We'll presume though that the types of expressions are mostly stable, so if we evaluated it in one
+                // compilation, we'll keep to that and risk a low-probability deoptimization if its type gets widened
+                // in the future.
+                compiler.addInvalidatedProgramPoint(node.getProgramPoint(), newValidType);
+            }
+            return evaluatedType;
+        }
+        return mostOptimisticType;
+    }
+
+    private static Type getPropertyType(final ScriptObject sobj, final String name) {
+        final FindProperty find = sobj.findProperty(name, true);
+        if (find == null) {
+            return null;
+        }
+
+        final Property property      = find.getProperty();
+        final Class<?> propertyClass = property.getCurrentType();
+        if (propertyClass == null) {
+            // propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make
+            // a type assumption yet.
+            return null;
+        } else if (propertyClass.isPrimitive()) {
+            return Type.typeFor(propertyClass);
+        }
+
+        final ScriptObject owner = find.getOwner();
+        if (property.hasGetterFunction(owner)) {
+            // Can have side effects, so we can't safely evaluate it; since !propertyClass.isPrimitive(), it's Object.
+            return Type.OBJECT;
+        }
+
+        // Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed
+        // integer).
+        final Object value = property.getObjectValue(owner, owner);
+        if (value == ScriptRuntime.UNDEFINED) {
+            return null;
+        }
+        return Type.typeFor(JSType.unboxedFieldType(value));
+    }
+
+    void declareLocalSymbol(final String symbolName) {
+        assert
+            compiler.useOptimisticTypes() &&
+            compiler.isOnDemandCompilation() &&
+            runtimeScope != null :
+                "useOptimistic=" +
+                    compiler.useOptimisticTypes() +
+                    " isOnDemand=" +
+                    compiler.isOnDemandCompilation() +
+                    " scope="+runtimeScope;
+
+        if (runtimeScope.findProperty(symbolName, false) == null) {
+            runtimeScope.set(symbolName, ScriptRuntime.UNDEFINED, true);
+        }
+    }
+
+    private Object evaluateSafely(final Expression expr) {
+        if (expr instanceof IdentNode) {
+            return runtimeScope == null ? null : evaluatePropertySafely(runtimeScope, ((IdentNode)expr).getName());
+        }
+
+        if (expr instanceof AccessNode) {
+            final AccessNode accessNode = (AccessNode)expr;
+            final Object     base       = evaluateSafely(accessNode.getBase());
+            if (!(base instanceof ScriptObject)) {
+                return null;
+            }
+            return evaluatePropertySafely((ScriptObject)base, accessNode.getProperty());
+        }
+
+        return null;
+    }
+
+    private static Object evaluatePropertySafely(final ScriptObject sobj, final String name) {
+        final FindProperty find = sobj.findProperty(name, true);
+        if (find == null) {
+            return null;
+        }
+        final Property     property = find.getProperty();
+        final ScriptObject owner    = find.getOwner();
+        if (property.hasGetterFunction(owner)) {
+            // Possible side effects; can't evaluate safely
+            return null;
+        }
+        return property.getObjectValue(owner, owner);
+    }
+
+
+    private Type getEvaluatedType(final Optimistic expr) {
+        if (expr instanceof IdentNode) {
+            if (runtimeScope == null) {
+                return null;
+            }
+            return getPropertyType(runtimeScope, ((IdentNode)expr).getName());
+        }
+
+        if (expr instanceof AccessNode) {
+            final AccessNode accessNode = (AccessNode)expr;
+            final Object base = evaluateSafely(accessNode.getBase());
+            if (!(base instanceof ScriptObject)) {
+                return null;
+            }
+            return getPropertyType((ScriptObject)base, accessNode.getProperty());
+        }
+
+        if (expr instanceof IndexNode) {
+            final IndexNode indexNode = (IndexNode)expr;
+            final Object    base = evaluateSafely(indexNode.getBase());
+            if(!(base instanceof NativeArray)) {
+                // We only know how to deal with NativeArray. TODO: maybe manage buffers too
+                return null;
+            }
+            // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying
+            // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every
+            // optimistic int linkage attempt, even if the long value being returned in the first invocation would be
+            // representable as int. That way, we can presume that the array's optimistic type is the most optimistic
+            // type for which an element getter has a chance of executing successfully.
+            return ((NativeArray)base).getArray().getOptimisticType();
+        }
+
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java	Mon May 19 15:29:42 2014 +0200
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2010-2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  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.lang.invoke.MethodType;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.runtime.ScriptFunction;
+
+/**
+ * A data structure that maps one or several function nodes (by their unique id:s, not by
+ * the FunctionNode object itself, due to copy on write changing it several times through
+ * code generation.
+ */
+public class TypeMap {
+    private final Map<Integer, Type[]> paramTypeMap  = new HashMap<>();
+    private final Map<Integer, Type>   returnTypeMap = new HashMap<>();
+    private final boolean needsCallee;
+
+    /**
+     * Constructor
+     * @param functionNodeId function node id
+     * @param type           method type found at runtime corresponding to parameter guess
+     * @param needsCallee    does the function using this type map need a callee
+     */
+    public TypeMap(final int functionNodeId, final MethodType type, final boolean needsCallee) {
+        final Type[] types = new Type[type.parameterCount()];
+        int pos = 0;
+        for (final Class<?> p : type.parameterArray()) {
+            types[pos++] = Type.typeFor(p);
+        }
+        paramTypeMap.put(functionNodeId, types);
+        returnTypeMap.put(functionNodeId, Type.typeFor(type.returnType()));
+
+        this.needsCallee = needsCallee;
+    }
+
+    MethodType getCallSiteType(final FunctionNode functionNode) {
+        final Type[] types = paramTypeMap.get(functionNode.getId());
+        if (types == null) {
+            return null;
+        }
+
+        MethodType mt = MethodType.methodType(returnTypeMap.get(functionNode.getId()).getTypeClass());
+        if (needsCallee) {
+            mt = mt.appendParameterTypes(ScriptFunction.class);
+        }
+
+        mt = mt.appendParameterTypes(Object.class); //this
+
+        for (final Type type : types) {
+            if (type == null) {
+                return null; // not all parameter information is supplied
+            }
+            mt = mt.appendParameterTypes(type.getTypeClass());
+        }
+
+        return mt;
+    }
+
+    /**
+     * Does the function using this TypeMap need a callee argument. This is used
+     * to compute correct param index offsets in {@link jdk.nashorn.internal.codegen.ApplySpecialization}
+     * @return true if a callee is needed, false otherwise
+     */
+    public boolean needsCallee() {
+        return needsCallee;
+    }
+
+    /**
+     * Get the parameter type for this parameter position, or
+     * null if now known
+     * @param functionNode functionNode
+     * @param pos position
+     * @return parameter type for this callsite if known
+     */
+    Type get(final FunctionNode functionNode, final int pos) {
+        final Type[] types = paramTypeMap.get(functionNode.getId());
+        assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this;
+        if (types != null && pos < types.length) {
+            return types[pos];
+        }
+        return null;
+    }
+
+    boolean has(final FunctionNode functionNode) {
+        final int id = functionNode.getId();
+        final Type[] paramTypes = paramTypeMap.get(id);
+        assert (paramTypes == null) == (returnTypeMap.get(id) == null) : "inconsistent param and return types in param map";
+        return paramTypes != null;
+    }
+
+    @Override
+    public String toString() {
+        return toString("");
+    }
+
+    String toString(final String prefix) {
+        final StringBuilder sb = new StringBuilder();
+
+        if (paramTypeMap.isEmpty()) {
+            sb.append(prefix).append("\t<empty>");
+            return sb.toString();
+        }
+
+        for (final Map.Entry<Integer, Type[]> entry : paramTypeMap.entrySet()) {
+            final int id = entry.getKey();
+            sb.append(prefix).append('\t');
+            sb.append("function ").append(id).append('\n');
+            sb.append(prefix).append("\t\tparamTypes=");
+            if (entry.getValue() == null) {
+                sb.append("[]");
+            } else {
+                sb.append(Arrays.toString(entry.getValue()));
+            }
+            sb.append('\n');
+            sb.append(prefix).append("\t\treturnType=");
+            final Type ret = returnTypeMap.get(id);
+            sb.append(ret == null ? "N/A" : ret);
+            sb.append('\n');
+        }
+
+        return sb.toString();
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java	Mon May 19 15:29:42 2014 +0200
@@ -69,16 +69,18 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
 
-        optimisticTypeToString(sb);
+        if (printType) {
+            optimisticTypeToString(sb);
+        }
 
         if (needsParen) {
             sb.append('(');
         }
 
-        base.toString(sb);
+        base.toString(sb, printType);
 
         if (needsParen) {
             sb.append(')');
@@ -113,7 +115,7 @@
     }
 
     @Override
-    public AccessNode setProgramPoint(int programPoint) {
+    public AccessNode setProgramPoint(final int programPoint) {
         if (this.programPoint == programPoint) {
             return this;
         }
--- a/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java	Mon May 19 15:29:42 2014 +0200
@@ -97,7 +97,7 @@
     }
 
     @Override
-    public Type getType(Function<Symbol, Type> localVariableTypes) {
+    public Type getType(final Function<Symbol, Type> localVariableTypes) {
         return type == null ? getMostPessimisticType() : type;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java	Mon May 19 15:29:42 2014 +0200
@@ -143,7 +143,7 @@
 
     private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
         @Override
-        public Type apply(Symbol t) {
+        public Type apply(final Symbol t) {
             return null;
         }
     };
@@ -250,7 +250,7 @@
         }
     }
 
-    private static Type booleanToInt(Type type) {
+    private static Type booleanToInt(final Type type) {
         return type == Type.BOOLEAN ? Type.INT : type;
     }
 
@@ -291,7 +291,7 @@
     }
 
     @Override
-    public BinaryNode setAssignmentDest(Expression n) {
+    public BinaryNode setAssignmentDest(final Expression n) {
         return setLHS(n);
     }
 
@@ -377,7 +377,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         final TokenType tokenType = tokenType();
 
         final boolean lhsParen = tokenType.needsParens(lhs().tokenType(), true);
@@ -387,7 +387,7 @@
             sb.append('(');
         }
 
-        lhs().toString(sb);
+        lhs().toString(sb, printType);
 
         if (lhsParen) {
             sb.append(')');
@@ -420,7 +420,7 @@
         if (rhsParen) {
             sb.append('(');
         }
-        rhs().toString(sb);
+        rhs().toString(sb, printType);
         if (rhsParen) {
             sb.append(')');
         }
@@ -531,7 +531,7 @@
     }
 
     @Override
-    public BinaryNode setType(Type type) {
+    public BinaryNode setType(final Type type) {
         if (this.type == type) {
             return this;
         }
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java	Mon May 19 15:29:42 2014 +0200
@@ -107,7 +107,7 @@
         this(token, finish, statements.toArray(new Statement[statements.size()]));
     }
 
-    private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, LocalVariableConversion conversion) {
+    private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, final LocalVariableConversion conversion) {
         super(block);
         this.statements = statements;
         this.flags      = flags;
@@ -184,9 +184,9 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         for (final Node statement : statements) {
-            statement.toString(sb);
+            statement.toString(sb, printType);
             sb.append(';');
         }
     }
@@ -378,7 +378,7 @@
 
     @Override
     public List<Label> getLabels() {
-        return Collections.singletonList(breakLabel);
+        return Collections.unmodifiableList(Arrays.asList(entryLabel, breakLabel));
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/ir/BlockStatement.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BlockStatement.java	Mon May 19 15:29:42 2014 +0200
@@ -70,7 +70,7 @@
      * @return a block statement with the new statements. It will have the line number, and token of the
      * original statement.
      */
-    public static BlockStatement createReplacement(final Statement stmt, int finish, final List<Statement> newStmts) {
+    public static BlockStatement createReplacement(final Statement stmt, final int finish, final List<Statement> newStmts) {
         return new BlockStatement(stmt.getLineNumber(), new Block(stmt.getToken(), finish, newStmts));
     }
 
@@ -89,8 +89,8 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
-        block.toString(sb);
+    public void toString(final StringBuilder sb, final boolean printType) {
+        block.toString(sb, printType);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/ir/BreakableNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BreakableNode.java	Mon May 19 15:29:42 2014 +0200
@@ -25,14 +25,13 @@
 
 package jdk.nashorn.internal.ir;
 
-import java.util.List;
 import jdk.nashorn.internal.codegen.Label;
 
 /**
  * This class represents a node from which control flow can execute
  * a {@code break} statement
  */
-public interface BreakableNode extends LexicalContextNode, JoinPredecessor {
+public interface BreakableNode extends LexicalContextNode, JoinPredecessor, Labels {
     /**
      * Ensure that any labels in this breakable node are unique so
      * that new jumps won't go to old parts of the tree. Used for
@@ -56,11 +55,4 @@
      */
     public Label getBreakLabel();
 
-    /**
-     * Return the labels associated with this node. Breakable nodes that
-     * aren't LoopNodes only have a break label - the location immediately
-     * afterwards the node in code
-     * @return list of labels representing locations around this node
-     */
-    public List<Label> getLabels();
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/BreakableStatement.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BreakableStatement.java	Mon May 19 15:29:42 2014 +0200
@@ -91,11 +91,11 @@
      */
     @Override
     public List<Label> getLabels() {
-        return Collections.singletonList(breakLabel);
+        return Collections.unmodifiableList(Collections.singletonList(breakLabel));
     }
 
     @Override
-    public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, LocalVariableConversion conversion) {
+    public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
         if(this.conversion == conversion) {
             return this;
         }
--- a/nashorn/src/jdk/nashorn/internal/ir/CallNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CallNode.java	Mon May 19 15:29:42 2014 +0200
@@ -185,7 +185,7 @@
     }
 
     @Override
-    public Type getType(Function<Symbol, Type> localVariableTypes) {
+    public Type getType(final Function<Symbol, Type> localVariableTypes) {
         return optimisticType == null ? Type.OBJECT : optimisticType;
     }
 
@@ -225,10 +225,14 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
-        optimisticTypeToString(sb);
+    public void toString(final StringBuilder sb, final boolean printType) {
+        if (printType) {
+            optimisticTypeToString(sb);
+        }
+
         final StringBuilder fsb = new StringBuilder();
-        function.toString(fsb);
+        function.toString(fsb, printType);
+
         if (isApplyToCall()) {
             sb.append(fsb.toString().replace("apply", "[apply => call]"));
         } else {
@@ -246,7 +250,7 @@
                 first = false;
             }
 
-            arg.toString(sb);
+            arg.toString(sb, printType);
         }
 
         sb.append(')');
--- a/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java	Mon May 19 15:29:42 2014 +0200
@@ -25,6 +25,9 @@
 
 package jdk.nashorn.internal.ir;
 
+import java.util.Collections;
+import java.util.List;
+
 import jdk.nashorn.internal.codegen.Label;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -34,7 +37,7 @@
  * Case nodes are not BreakableNodes, but the SwitchNode is
  */
 @Immutable
-public final class CaseNode extends Node implements JoinPredecessor {
+public final class CaseNode extends Node implements JoinPredecessor, Labels {
     /** Test expression. */
     private final Expression test;
 
@@ -97,10 +100,10 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printTypes) {
         if (test != null) {
             sb.append("case ");
-            test.toString(sb);
+            test.toString(sb, printTypes);
             sb.append(':');
         } else {
             sb.append("default:");
@@ -162,4 +165,9 @@
         }
         return new CaseNode(this, test, body, conversion);
     }
+
+    @Override
+    public List<Label> getLabels() {
+        return Collections.unmodifiableList(Collections.singletonList(entry));
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java	Mon May 19 15:29:42 2014 +0200
@@ -95,13 +95,13 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printTypes) {
         sb.append(" catch (");
-        exception.toString(sb);
+        exception.toString(sb, printTypes);
 
         if (exceptionCondition != null) {
             sb.append(" if ");
-            exceptionCondition.toString(sb);
+            exceptionCondition.toString(sb, printTypes);
         }
         sb.append(')');
     }
--- a/nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java	Mon May 19 15:29:42 2014 +0200
@@ -64,7 +64,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printTypes) {
         sb.append(';');
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/Expression.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Expression.java	Mon May 19 15:29:42 2014 +0200
@@ -26,7 +26,9 @@
 package jdk.nashorn.internal.ir;
 
 import java.util.function.Function;
+
 import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
 
 /**
  * Common superclass for all expression nodes. Expression nodes can have
@@ -34,9 +36,11 @@
  *
  */
 public abstract class Expression extends Node {
+    static final String OPT_IDENTIFIER = "%";
+
     private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
         @Override
-        public Type apply(Symbol t) {
+        public Type apply(final Symbol t) {
             return null;
         }
     };
@@ -109,22 +113,22 @@
         return getType().narrowerThan(getWidestOperationType());
     }
 
-    static final String OPT_IDENTIFIER = "%";
-
     void optimisticTypeToString(final StringBuilder sb) {
         optimisticTypeToString(sb, isOptimistic());
     }
 
-    void optimisticTypeToString(final StringBuilder sb, boolean optimistic) {
+    void optimisticTypeToString(final StringBuilder sb, final boolean optimistic) {
         sb.append('{');
         final Type type = getType();
         final String desc = type == Type.UNDEFINED ? "U" : type.getDescriptor();
 
         sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : desc);
-        if(isOptimistic() && optimistic) {
+        if (isOptimistic() && optimistic) {
             sb.append(OPT_IDENTIFIER);
-            sb.append('_');
-            sb.append(((Optimistic)this).getProgramPoint());
+            final int pp = ((Optimistic)this).getProgramPoint();
+            if (UnwarrantedOptimismException.isValid(pp)) {
+                sb.append('_').append(pp);
+            }
         }
         sb.append('}');
     }
@@ -152,7 +156,7 @@
      * @param test a test expression used as a predicate of a branch or a loop.
      * @return true if the expression is not null and {@link #isAlwaysFalse()}.
      */
-    public static boolean isAlwaysFalse(Expression test) {
+    public static boolean isAlwaysFalse(final Expression test) {
         return test != null && test.isAlwaysFalse();
     }
 
@@ -163,7 +167,7 @@
      * @param test a test expression used as a predicate of a branch or a loop.
      * @return true if the expression is null or {@link #isAlwaysFalse()}.
      */
-    public static boolean isAlwaysTrue(Expression test) {
+    public static boolean isAlwaysTrue(final Expression test) {
         return test == null || test.isAlwaysTrue();
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java	Mon May 19 15:29:42 2014 +0200
@@ -71,8 +71,8 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
-        expression.toString(sb);
+    public void toString(final StringBuilder sb, final boolean printTypes) {
+        expression.toString(sb, printTypes);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/ir/ForNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ForNode.java	Mon May 19 15:29:42 2014 +0200
@@ -71,7 +71,7 @@
     }
 
     private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
-            final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, LocalVariableConversion conversion) {
+            final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, final LocalVariableConversion conversion) {
         super(forNode, test, body, controlFlowEscapes, conversion);
         this.init   = init;
         this.modify = modify;
@@ -82,7 +82,7 @@
     }
 
     @Override
-    public Node ensureUniqueLabels(LexicalContext lc) {
+    public Node ensureUniqueLabels(final LexicalContext lc) {
         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
     }
 
@@ -100,25 +100,25 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printTypes) {
         sb.append("for");
         LocalVariableConversion.toString(conversion, sb).append(' ');
 
         if (isForIn()) {
-            init.toString(sb);
+            init.toString(sb, printTypes);
             sb.append(" in ");
-            modify.toString(sb);
+            modify.toString(sb, printTypes);
         } else {
             if (init != null) {
-                init.toString(sb);
+                init.toString(sb, printTypes);
             }
             sb.append("; ");
             if (test != null) {
-                test.toString(sb);
+                test.toString(sb, printTypes);
             }
             sb.append("; ");
             if (modify != null) {
-                modify.toString(sb);
+                modify.toString(sb, printTypes);
             }
         }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Mon May 19 15:29:42 2014 +0200
@@ -33,6 +33,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.Function;
+
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -79,6 +80,10 @@
         CONSTANT_FOLDED,
         /** method has been lowered */
         LOWERED,
+        /** program points have been assigned to unique locations */
+        PROGRAM_POINTS_ASSIGNED,
+        /** any transformations of builtins have taken place, e.g. apply=>call */
+        BUILTINS_TRANSFORMED,
         /** method has been split */
         SPLIT,
         /** method has had symbols assigned */
@@ -87,11 +92,16 @@
         SCOPE_DEPTHS_COMPUTED,
         /** method has had types calculated*/
         OPTIMISTIC_TYPES_ASSIGNED,
-        /** method has had types calculated*/
+        /** method has had types calculated */
         LOCAL_VARIABLE_TYPES_CALCULATED,
+        /** compile units reused (optional) */
+        COMPILE_UNITS_REUSED,
         /** method has been emitted to bytecode */
-        EMITTED
-    }
+        BYTECODE_GENERATED,
+        /** method has been installed */
+        BYTECODE_INSTALLED
+    };
+
     /** Source of entity. */
     private final Source source;
 
@@ -147,6 +157,9 @@
     /** Line number of function start */
     private final int lineNumber;
 
+    /** Root class for function */
+    private final Class<?> rootClass;
+
     /** Is anonymous function flag. */
     public static final int IS_ANONYMOUS                = 1 << 0;
 
@@ -292,6 +305,7 @@
         this.compileUnit      = null;
         this.body             = null;
         this.thisProperties   = 0;
+        this.rootClass        = null;
     }
 
     private FunctionNode(
@@ -305,8 +319,10 @@
         final EnumSet<CompilationState> compilationState,
         final Block body,
         final List<IdentNode> parameters,
-        final int thisProperties) {
+        final int thisProperties,
+        final Class<?> rootClass) {
         super(functionNode);
+
         this.lineNumber       = functionNode.lineNumber;
         this.flags            = flags;
         this.sourceURL        = sourceURL;
@@ -318,6 +334,7 @@
         this.body             = body;
         this.parameters       = parameters;
         this.thisProperties   = thisProperties;
+        this.rootClass        = rootClass;
 
         // the fields below never change - they are final and assigned in constructor
         this.source          = functionNode.source;
@@ -368,7 +385,17 @@
      * @return name for the script source
      */
     public String getSourceName() {
-        return sourceURL != null? sourceURL : source.getName();
+        return getSourceName(source, sourceURL);
+    }
+
+    /**
+     * static source name getter
+     * @param source
+     * @param sourceURL
+     * @return
+     */
+    public static String getSourceName(final Source source, final String sourceURL) {
+        return sourceURL != null ? sourceURL : source.getName();
     }
 
     /**
@@ -391,7 +418,22 @@
             return this;
         }
 
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        newSourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     /**
@@ -417,11 +459,17 @@
      * @return true of the node is in the given state
      */
     public boolean hasState(final EnumSet<CompilationState> state) {
-        return compilationState.equals(state);
+        //this.compilationState >= state, or we fail
+        for (final CompilationState cs : state) {
+            if (!hasState(cs)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
-     * Check whether the state of this FunctionNode contains a given compilation
+     * Check whether the state of this FunctionNode contains a given compilation<
      * state.
      *
      * A node can be in many states at once, e.g. both lowered and initialized.
@@ -449,7 +497,22 @@
         }
         final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
         newState.add(state);
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        newState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     /**
@@ -462,7 +525,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printTypes) {
         sb.append('[').
             append(returnType).
             append(']').
@@ -472,7 +535,7 @@
 
         if (ident != null) {
             sb.append(' ');
-            ident.toString(sb);
+            ident.toString(sb, printTypes);
         }
 
         sb.append('(');
@@ -482,7 +545,7 @@
             if (parameter.getSymbol() != null) {
                 sb.append('[').append(parameter.getType()).append(']').append(' ');
             }
-            parameter.toString(sb);
+            parameter.toString(sb, printTypes);
             if (iter.hasNext()) {
                 sb.append(", ");
             }
@@ -506,7 +569,22 @@
         if (this.flags == flags) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     @Override
@@ -642,10 +720,28 @@
      * @return new function node if body changed, same if not
      */
     public FunctionNode setBody(final LexicalContext lc, final Block body) {
-        if(this.body == body) {
+        if (this.body == body) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags |
+                            (body.needsScope() ?
+                                    FunctionNode.HAS_SCOPE_BLOCK :
+                                    0),
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     /**
@@ -726,7 +822,22 @@
         if (this.thisProperties == thisProperties) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     /**
@@ -772,7 +883,22 @@
         if (this.lastToken == lastToken) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     /**
@@ -793,7 +919,22 @@
         if (this.name.equals(name)) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     /**
@@ -844,7 +985,22 @@
         if (this.parameters == parameters) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     /**
@@ -874,7 +1030,7 @@
     }
 
     @Override
-    public Type getType(Function<Symbol, Type> localVariableTypes) {
+    public Type getType(final Function<Symbol, Type> localVariableTypes) {
         return FUNCTION_TYPE;
     }
 
@@ -922,7 +1078,8 @@
                 compilationState,
                 body,
                 parameters,
-                thisProperties
+                thisProperties,
+                rootClass
                 ));
    }
 
@@ -954,7 +1111,22 @@
         if (this.compileUnit == compileUnit) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
     }
 
     /**
@@ -975,4 +1147,41 @@
     public Symbol compilerConstant(final CompilerConstants cc) {
         return body.getExistingSymbol(cc.symbolName());
     }
+
+    /**
+     * Get the root class that this function node compiles to
+     * @return root class
+     */
+    public Class<?> getRootClass() {
+        return rootClass;
+    }
+
+    /**
+     * Reset the root class that this function is compiled to
+     * @see Compiler
+     * @param lc lexical context
+     * @param rootClass root class
+     * @return function node or a new one if state was changed
+     */
+    public FunctionNode setRootClass(final LexicalContext lc, final Class<?> rootClass) {
+        if (this.rootClass == rootClass) {
+            return this;
+        }
+        return Node.replaceInLexicalContext(
+                lc,
+                this,
+                new FunctionNode(
+                        this,
+                        lastToken,
+                        flags,
+                        sourceURL,
+                        name,
+                        returnType,
+                        compileUnit,
+                        compilationState,
+                        body,
+                        parameters,
+                        thisProperties,
+                        rootClass));
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Mon May 19 15:29:42 2014 +0200
@@ -114,7 +114,7 @@
     }
 
     @Override
-    public Type getType(Function<Symbol, Type> localVariableTypes) {
+    public Type getType(final Function<Symbol, Type> localVariableTypes) {
         if(type != null) {
             return type;
         } else if(symbol != null && symbol.isScope()) {
@@ -139,8 +139,10 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
-        optimisticTypeToString(sb, symbol == null || !symbol.hasSlot());
+    public void toString(final StringBuilder sb, final boolean printType) {
+        if (printType) {
+            optimisticTypeToString(sb, symbol == null || !symbol.hasSlot());
+        }
         sb.append(name);
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/IfNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IfNode.java	Mon May 19 15:29:42 2014 +0200
@@ -92,9 +92,9 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printTypes) {
         sb.append("if (");
-        test.toString(sb);
+        test.toString(sb, printTypes);
         sb.append(')');
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java	Mon May 19 15:29:42 2014 +0200
@@ -65,23 +65,25 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         final boolean needsParen = tokenType().needsParens(base.tokenType(), true);
 
         if (needsParen) {
             sb.append('(');
         }
 
-        optimisticTypeToString(sb);
+        if (printType) {
+            optimisticTypeToString(sb);
+        }
 
-        base.toString(sb);
+        base.toString(sb, printType);
 
         if (needsParen) {
             sb.append(')');
         }
 
         sb.append('[');
-        index.toString(sb);
+        index.toString(sb, printType);
         sb.append(']');
     }
 
@@ -105,7 +107,7 @@
      * @param index new index expression
      * @return a node equivalent to this one except for the requested change.
      */
-    public IndexNode setIndex(Expression index) {
+    public IndexNode setIndex(final Expression index) {
         if(this.index == index) {
             return this;
         }
@@ -129,7 +131,7 @@
     }
 
     @Override
-    public IndexNode setProgramPoint(int programPoint) {
+    public IndexNode setProgramPoint(final int programPoint) {
         if (this.programPoint == programPoint) {
             return this;
         }
--- a/nashorn/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java	Mon May 19 15:29:42 2014 +0200
@@ -70,7 +70,7 @@
     }
 
     @Override
-    public Type getType(Function<Symbol, Type> localVariableTypes) {
+    public Type getType(final Function<Symbol, Type> localVariableTypes) {
         return expression.getType(localVariableTypes);
     }
 
@@ -110,7 +110,7 @@
     }
 
     @Override
-    public Node accept(NodeVisitor<? extends LexicalContext> visitor) {
+    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
         if(visitor.enterJoinPredecessorExpression(this)) {
             final Expression expr = getExpression();
             return visitor.leaveJoinPredecessorExpression(expr == null ? this : setExpression((Expression)expr.accept(visitor)));
@@ -119,9 +119,9 @@
     }
 
     @Override
-    public void toString(StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         if(expression != null) {
-            expression.toString(sb);
+            expression.toString(sb, printType);
         }
         if(conversion != null) {
             conversion.toString(sb);
--- a/nashorn/src/jdk/nashorn/internal/ir/JumpStatement.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/JumpStatement.java	Mon May 19 15:29:42 2014 +0200
@@ -72,7 +72,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append(getStatementName());
 
         if (labelName != null) {
--- a/nashorn/src/jdk/nashorn/internal/ir/LabelNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/LabelNode.java	Mon May 19 15:29:42 2014 +0200
@@ -82,7 +82,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append(labelName).append(':');
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/Labels.java	Mon May 19 15:29:42 2014 +0200
@@ -0,0 +1,44 @@
+/*
+ * 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.ir;
+
+import java.util.List;
+
+import jdk.nashorn.internal.codegen.Label;
+
+/**
+ * Interface that can be used to get a list of all labels in a node
+ */
+public interface Labels {
+
+    /**
+     * Return the labels associated with this node. Breakable nodes that
+     * aren't LoopNodes only have a break label - the location immediately
+     * afterwards the node in code
+     * @return list of labels representing locations around this node
+     */
+    public List<Label> getLabels();
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Mon May 19 15:29:42 2014 +0200
@@ -29,6 +29,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
+
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.types.ArrayType;
 import jdk.nashorn.internal.codegen.types.Type;
@@ -210,7 +211,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         if (value == null) {
             sb.append("null");
         } else {
@@ -448,7 +449,7 @@
         }
 
         @Override
-        public void toString(final StringBuilder sb) {
+        public void toString(final StringBuilder sb, final boolean printType) {
             sb.append('\"');
             sb.append(value);
             sb.append('\"');
@@ -496,7 +497,7 @@
         }
 
         @Override
-        public void toString(final StringBuilder sb) {
+        public void toString(final StringBuilder sb, final boolean printType) {
             sb.append(value.toString());
         }
     }
@@ -880,7 +881,7 @@
         }
 
         @Override
-        public Node accept(LexicalContext lc, NodeVisitor<? extends LexicalContext> visitor) {
+        public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
             if (visitor.enterLiteralNode(this)) {
                 final List<Expression> oldValue = Arrays.asList(value);
                 final List<Expression> newValue = Node.accept(visitor, Expression.class, oldValue);
@@ -894,7 +895,7 @@
         }
 
         @Override
-        public void toString(final StringBuilder sb) {
+        public void toString(final StringBuilder sb, final boolean printType) {
             sb.append('[');
             boolean first = true;
             for (final Node node : value) {
@@ -905,7 +906,7 @@
                 if (node == null) {
                     sb.append("undefined");
                 } else {
-                    node.toString(sb);
+                    node.toString(sb, printType);
                 }
                 first = false;
             }
--- a/nashorn/src/jdk/nashorn/internal/ir/LoopNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/LoopNode.java	Mon May 19 15:29:42 2014 +0200
@@ -26,7 +26,9 @@
 package jdk.nashorn.internal.ir;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
+
 import jdk.nashorn.internal.codegen.Label;
 
 /**
@@ -126,7 +128,7 @@
 
     @Override
     public List<Label> getLabels() {
-        return Arrays.asList(breakLabel, continueLabel);
+        return Collections.unmodifiableList(Arrays.asList(breakLabel, continueLabel));
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/ir/Node.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Node.java	Mon May 19 15:29:42 2014 +0200
@@ -27,6 +27,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.parser.TokenType;
@@ -129,7 +130,19 @@
      *
      * @param sb a StringBuilder
      */
-    public abstract void toString(StringBuilder sb);
+    public void toString(final StringBuilder sb) {
+        toString(sb, true);
+    }
+
+    /**
+     * Print logic that decides whether to show the optimistic type
+     * or not - for example it should not be printed after just parse,
+     * when it hasn't been computed, or has been set to a trivially provable
+     * value
+     * @param sb   string builder
+     * @param printType print type?
+     */
+    public abstract void toString(final StringBuilder sb, final boolean printType);
 
     /**
      * Check if this node has terminal flags, i.e. ends or breaks control flow
--- a/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java	Mon May 19 15:29:42 2014 +0200
@@ -28,6 +28,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -73,7 +74,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append('{');
 
         if (!elements.isEmpty()) {
@@ -86,7 +87,7 @@
                 }
                 first = false;
 
-                element.toString(sb);
+                element.toString(sb, printType);
             }
             sb.append(' ');
         }
--- a/nashorn/src/jdk/nashorn/internal/ir/Optimistic.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Optimistic.java	Mon May 19 15:29:42 2014 +0200
@@ -37,9 +37,6 @@
  * @see BinaryNode (local calculations to strongly typed bytecode)
  * @see UnaryNode  (local calculations to strongly typed bytecode)
  * @see CallNode   (dynamicCall)
- *
- * TODO : to be implemented are
- *
  * @see AccessNode (dynamicGet)
  * @see IdentNode  (dynamicGet)
  */
--- a/nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java	Mon May 19 15:29:42 2014 +0200
@@ -94,25 +94,25 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         if (value instanceof FunctionNode && ((FunctionNode)value).getIdent() != null) {
             value.toString(sb);
         }
 
         if (value != null) {
-            ((Node)key).toString(sb);
+            ((Node)key).toString(sb, printType);
             sb.append(": ");
-            value.toString(sb);
+            value.toString(sb, printType);
         }
 
         if (getter != null) {
             sb.append(' ');
-            getter.toString(sb);
+            getter.toString(sb, printType);
         }
 
         if (setter != null) {
             sb.append(' ');
-            setter.toString(sb);
+            setter.toString(sb, printType);
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java	Mon May 19 15:29:42 2014 +0200
@@ -27,7 +27,6 @@
 
 import static jdk.nashorn.internal.parser.TokenType.RETURN;
 import static jdk.nashorn.internal.parser.TokenType.YIELD;
-
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 
@@ -100,11 +99,11 @@
 
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append(isYield() ? "yield" : "return");
         if (expression != null) {
             sb.append(' ');
-            expression.toString(sb);
+            expression.toString(sb, printType);
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java	Mon May 19 15:29:42 2014 +0200
@@ -460,7 +460,7 @@
      * Return type for the ReferenceNode
      */
     @Override
-    public Type getType(Function<Symbol, Type> localVariableTypes) {
+    public Type getType(final Function<Symbol, Type> localVariableTypes) {
         return request.getReturnType();
     }
 
@@ -478,7 +478,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append("ScriptRuntime.");
         sb.append(request);
         sb.append('(');
@@ -492,7 +492,7 @@
                 first = false;
             }
 
-            arg.toString(sb);
+            arg.toString(sb, printType);
         }
 
         sb.append(')');
--- a/nashorn/src/jdk/nashorn/internal/ir/SplitNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/SplitNode.java	Mon May 19 15:29:42 2014 +0200
@@ -25,8 +25,12 @@
 
 package jdk.nashorn.internal.ir;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Label;
 import jdk.nashorn.internal.ir.annotations.Immutable;
@@ -36,7 +40,7 @@
  * Node indicating code is split across classes.
  */
 @Immutable
-public class SplitNode extends LexicalContextStatement {
+public class SplitNode extends LexicalContextStatement implements Labels {
     /** Split node method name. */
     private final String name;
 
@@ -62,12 +66,12 @@
         this.compileUnit = compileUnit;
     }
 
-    private SplitNode(final SplitNode splitNode, final Block body) {
+    private SplitNode(final SplitNode splitNode, final Block body, final CompileUnit compileUnit, final Map<Label, JoinPredecessor> jumps) {
         super(splitNode);
         this.name        = splitNode.name;
         this.body        = body;
-        this.compileUnit = splitNode.compileUnit;
-        this.jumps = splitNode.jumps;
+        this.compileUnit = compileUnit;
+        this.jumps       = jumps;
     }
 
     /**
@@ -82,7 +86,7 @@
         if (this.body == body) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body));
+        return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps));
     }
 
     @Override
@@ -94,11 +98,11 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append("<split>(");
         sb.append(compileUnit.getClass().getSimpleName());
         sb.append(") ");
-        body.toString(sb);
+        body.toString(sb, printType);
     }
 
     /**
@@ -118,13 +122,26 @@
     }
 
     /**
+     * Set the compile unit for this split node
+     * @param lc lexical context
+     * @param compileUnit compile unit
+     * @return new node if changed, otherwise same node
+     */
+    public SplitNode setCompileUnit(final LexicalContext lc, final CompileUnit compileUnit) {
+        if (this.compileUnit == compileUnit) {
+            return this;
+        }
+        return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps));
+    }
+
+    /**
      * Adds a jump that crosses this split node's boundary (it originates within the split node, and goes to a target
      * outside of it).
      * @param jumpOrigin the join predecessor that's the origin of the jump
      * @param targetLabel the label that's the target of the jump.
      */
     public void addJump(final JoinPredecessor jumpOrigin, final Label targetLabel) {
-        if(jumps == null) {
+        if (jumps == null) {
             jumps = new HashMap<>();
         }
         jumps.put(targetLabel, jumpOrigin);
@@ -138,4 +155,9 @@
     public JoinPredecessor getJumpOrigin(final Label targetLabel) {
         return jumps == null ? null : jumps.get(targetLabel);
     }
+
+    @Override
+    public List<Label> getLabels() {
+        return Collections.unmodifiableList(new ArrayList<>(jumps.keySet()));
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java	Mon May 19 15:29:42 2014 +0200
@@ -28,6 +28,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+
 import jdk.nashorn.internal.codegen.Label;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -111,9 +112,9 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append("switch (");
-        expression.toString(sb);
+        expression.toString(sb, printType);
         sb.append(')');
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/Symbol.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Symbol.java	Mon May 19 15:29:42 2014 +0200
@@ -138,7 +138,7 @@
     protected Symbol(final String name, final int flags, final int slot) {
         this.name       = name;
         this.flags      = flags;
-        this.firstSlot       = slot;
+        this.firstSlot  = slot;
         this.fieldIndex = -1;
         if(shouldTrace()) {
             trace("CREATE SYMBOL " + name);
--- a/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java	Mon May 19 15:29:42 2014 +0200
@@ -26,9 +26,11 @@
 package jdk.nashorn.internal.ir;
 
 import java.util.function.Function;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.parser.TokenType;
 
 /**
  * TernaryNode represent the ternary operator {@code ?:}. Note that for control-flow calculation reasons its branch
@@ -76,15 +78,16 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
-        final boolean testParen  = tokenType().needsParens(getTest().tokenType(), true);
-        final boolean trueParen  = tokenType().needsParens(getTrueExpression().tokenType(), false);
-        final boolean falseParen = tokenType().needsParens(getFalseExpression().tokenType(), false);
+    public void toString(final StringBuilder sb, final boolean printType) {
+        final TokenType tokenType  = tokenType();
+        final boolean   testParen  = tokenType.needsParens(getTest().tokenType(), true);
+        final boolean   trueParen  = tokenType.needsParens(getTrueExpression().tokenType(), false);
+        final boolean   falseParen = tokenType.needsParens(getFalseExpression().tokenType(), false);
 
         if (testParen) {
             sb.append('(');
         }
-        getTest().toString(sb);
+        getTest().toString(sb, printType);
         if (testParen) {
             sb.append(')');
         }
@@ -94,7 +97,7 @@
         if (trueParen) {
             sb.append('(');
         }
-        getTrueExpression().toString(sb);
+        getTrueExpression().toString(sb, printType);
         if (trueParen) {
             sb.append(')');
         }
@@ -104,7 +107,7 @@
         if (falseParen) {
             sb.append('(');
         }
-        getFalseExpression().toString(sb);
+        getFalseExpression().toString(sb, printType);
         if (falseParen) {
             sb.append(')');
         }
@@ -118,7 +121,7 @@
     }
 
     @Override
-    public Type getType(Function<Symbol, Type> localVariableTypes) {
+    public Type getType(final Function<Symbol, Type> localVariableTypes) {
         return Type.widestReturnType(getTrueExpression().getType(localVariableTypes), getFalseExpression().getType(localVariableTypes));
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java	Mon May 19 15:29:42 2014 +0200
@@ -83,13 +83,13 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append("throw ");
 
         if (expression != null) {
-            expression.toString(sb);
+            expression.toString(sb, printType);
         }
-        if(conversion != null) {
+        if (conversion != null) {
             conversion.toString(sb);
         }
     }
--- a/nashorn/src/jdk/nashorn/internal/ir/TryNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/TryNode.java	Mon May 19 15:29:42 2014 +0200
@@ -28,6 +28,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 
@@ -71,7 +72,7 @@
         this.conversion  = null;
     }
 
-    private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, LocalVariableConversion conversion) {
+    private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion) {
         super(tryNode);
         this.body        = body;
         this.catchBlocks = catchBlocks;
@@ -120,7 +121,7 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append("try ");
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Mon May 19 15:29:42 2014 +0200
@@ -34,6 +34,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.annotations.Immutable;
@@ -123,7 +124,7 @@
 
     private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
         @Override
-        public Type apply(Symbol t) {
+        public Type apply(final Symbol t) {
             return null;
         }
     };
@@ -166,7 +167,7 @@
     }
 
     @Override
-    public UnaryNode setAssignmentDest(Expression n) {
+    public UnaryNode setAssignmentDest(final Expression n) {
         return setExpression(n);
     }
 
@@ -209,13 +210,15 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
-        toString(sb, new Runnable() {
-            @Override
-            public void run() {
-                sb.append(getExpression().toString());
-            }
-        });
+    public void toString(final StringBuilder sb, final boolean printType) {
+        toString(sb,
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        getExpression().toString(sb, printType);
+                    }
+                },
+                printType);
     }
 
     /**
@@ -223,9 +226,10 @@
      * operand to a specified runnable.
      * @param sb the string builder to use
      * @param rhsStringBuilder the runnable that appends the string representation of the operand to the string builder
+     * @param printType should we print type
      * when invoked.
      */
-    public void toString(final StringBuilder sb, final Runnable rhsStringBuilder) {
+    public void toString(final StringBuilder sb, final Runnable rhsStringBuilder, final boolean printType) {
         final TokenType tokenType = tokenType();
         final String    name      = tokenType.getName();
         final boolean   isPostfix = tokenType == DECPOSTFIX || tokenType == INCPOSTFIX;
@@ -233,7 +237,7 @@
         if (isOptimistic()) {
             sb.append(Expression.OPT_IDENTIFIER);
         }
-        boolean rhsParen   = tokenType.needsParens(getExpression().tokenType(), false);
+        boolean rhsParen = tokenType.needsParens(getExpression().tokenType(), false);
 
         if (!isPostfix) {
             if (name == null) {
@@ -321,7 +325,7 @@
     }
 
     @Override
-    public Type getType(Function<Symbol, Type> localVariableTypes) {
+    public Type getType(final Function<Symbol, Type> localVariableTypes) {
         final Type widest = getWidestOperationType(localVariableTypes);
         if(type == null) {
             return widest;
@@ -330,7 +334,7 @@
     }
 
     @Override
-    public UnaryNode setType(Type type) {
+    public UnaryNode setType(final Type type) {
         if (this.type == type) {
             return this;
         }
--- a/nashorn/src/jdk/nashorn/internal/ir/VarNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/VarNode.java	Mon May 19 15:29:42 2014 +0200
@@ -138,13 +138,13 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append("var ");
-        name.toString(sb);
+        name.toString(sb, printType);
 
         if (init != null) {
             sb.append(" = ");
-            init.toString(sb);
+            init.toString(sb, printType);
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/WhileNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/WhileNode.java	Mon May 19 15:29:42 2014 +0200
@@ -60,7 +60,7 @@
      * @param controlFlowEscapes control flow escapes?
      * @param conversion TODO
      */
-    private WhileNode(final WhileNode whileNode, final JoinPredecessorExpression test, final Block body, final boolean controlFlowEscapes, LocalVariableConversion conversion) {
+    private WhileNode(final WhileNode whileNode, final JoinPredecessorExpression test, final Block body, final boolean controlFlowEscapes, final LocalVariableConversion conversion) {
         super(whileNode, test, body, controlFlowEscapes, conversion);
         this.isDoWhile = whileNode.isDoWhile;
     }
@@ -133,9 +133,9 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append("while (");
-        test.toString(sb);
+        test.toString(sb, printType);
         sb.append(')');
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/WithNode.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/WithNode.java	Mon May 19 15:29:42 2014 +0200
@@ -79,9 +79,9 @@
     }
 
     @Override
-    public void toString(final StringBuilder sb) {
+    public void toString(final StringBuilder sb, final boolean printType) {
         sb.append("with (");
-        expression.toString(sb);
+        expression.toString(sb, printType);
         sb.append(')');
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Mon May 19 15:29:42 2014 +0200
@@ -28,7 +28,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Block;
@@ -92,7 +91,7 @@
         final Parser       parser     = new Parser(context.getEnv(), new Source(name, code), new Context.ThrowErrorManager(), context.getEnv()._strict, context.getLogger(Parser.class));
         final JSONWriter   jsonWriter = new JSONWriter(includeLoc);
         try {
-            final FunctionNode functionNode = parser.parse(CompilerConstants.PROGRAM.symbolName());
+            final FunctionNode functionNode = parser.parse(); //symbol name is ":program", default
             functionNode.accept(jsonWriter);
             return jsonWriter.getString();
         } catch (final ParserException e) {
@@ -102,7 +101,7 @@
     }
 
     @Override
-    public boolean enterJoinPredecessorExpression(JoinPredecessorExpression joinPredecessorExpression) {
+    public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinPredecessorExpression) {
         final Expression expr = joinPredecessorExpression.getExpression();
         if(expr != null) {
             expr.accept(this);
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java	Mon May 19 15:29:42 2014 +0200
@@ -52,8 +52,6 @@
  * this fact and will report incorrect sizes, as it will presume the default JVM
  * behavior.
  */
-
-@SuppressWarnings("StaticNonFinalUsedInInitialization")
 public class ObjectSizeCalculator {
 
     /**
@@ -307,7 +305,7 @@
         public ClassSizeInfo(final Class<?> clazz) {
             long newFieldsSize = 0;
             final List<Field> newReferenceFields = new LinkedList<>();
-            for (Field f : clazz.getDeclaredFields()) {
+            for (final Field f : clazz.getDeclaredFields()) {
                 if (Modifier.isStatic(f.getModifiers())) {
                     continue;
                 }
@@ -338,10 +336,10 @@
         }
 
         public void enqueueReferencedObjects(final Object obj, final ObjectSizeCalculator calc) {
-            for (Field f : referenceFields) {
+            for (final Field f : referenceFields) {
                 try {
                     calc.enqueue(f.get(obj));
-                } catch (IllegalAccessException e) {
+                } catch (final IllegalAccessException e) {
                     final AssertionError ae = new AssertionError(
                             "Unexpected denial of access to " + f);
                     ae.initCause(e);
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Mon May 19 15:29:42 2014 +0200
@@ -77,25 +77,30 @@
     /** Print line numbers */
     private final boolean printLineNumbers;
 
+    /** Print inferred and optimistic types */
+    private final boolean printTypes;
+
     private int lastLineNumber = -1;
 
     /**
      * Constructor.
      */
     public PrintVisitor() {
-        this(true);
+        this(true, true);
     }
 
     /**
      * Constructor
      *
      * @param printLineNumbers  should line number nodes be included in the output?
+     * @param printTypes        should we print optimistic and inferred types?
      */
-    public PrintVisitor(final boolean printLineNumbers) {
+    public PrintVisitor(final boolean printLineNumbers, final boolean printTypes) {
         super(new LexicalContext());
         this.EOLN             = System.lineSeparator();
         this.sb               = new StringBuilder();
         this.printLineNumbers = printLineNumbers;
+        this.printTypes       = printTypes;
     }
 
     /**
@@ -104,7 +109,7 @@
      * @param root  a node from which to start printing code
      */
     public PrintVisitor(final Node root) {
-        this(root, true);
+        this(root, true, true);
     }
 
     /**
@@ -112,9 +117,10 @@
      *
      * @param root              a node from which to start printing code
      * @param printLineNumbers  should line numbers nodes be included in the output?
+     * @param printTypes        should we print optimistic and inferred types?
      */
-    public PrintVisitor(final Node root, final boolean printLineNumbers) {
-        this(printLineNumbers);
+    public PrintVisitor(final Node root, final boolean printLineNumbers, final boolean printTypes) {
+        this(printLineNumbers, printTypes);
         visit(root);
     }
 
@@ -142,27 +148,27 @@
 
     @Override
     public boolean enterDefault(final Node node) {
-        node.toString(sb);
+        node.toString(sb, printTypes);
         return false;
     }
 
     @Override
     public boolean enterContinueNode(final ContinueNode node) {
-        node.toString(sb);
+        node.toString(sb, printTypes);
         printLocalVariableConversion(node);
         return false;
     }
 
     @Override
     public boolean enterBreakNode(final BreakNode node) {
-        node.toString(sb);
+        node.toString(sb, printTypes);
         printLocalVariableConversion(node);
         return false;
     }
 
     @Override
     public boolean enterThrowNode(final ThrowNode node) {
-        node.toString(sb);
+        node.toString(sb, printTypes);
         printLocalVariableConversion(node);
         return false;
     }
@@ -240,15 +246,15 @@
     }
 
     @Override
-    public boolean enterJoinPredecessorExpression(JoinPredecessorExpression expr) {
+    public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression expr) {
         expr.getExpression().accept(this);
         printLocalVariableConversion(expr);
         return false;
     }
 
     @Override
-    public boolean enterIdentNode(IdentNode identNode) {
-        identNode.toString(sb);
+    public boolean enterIdentNode(final IdentNode identNode) {
+        identNode.toString(sb, printTypes);
         printLocalVariableConversion(identNode);
         return true;
     }
@@ -264,7 +270,7 @@
             public void run() {
                 unaryNode.getExpression().accept(PrintVisitor.this);
             }
-        });
+        }, printTypes);
         return false;
     }
 
@@ -276,21 +282,21 @@
 
     @Override
     public boolean enterForNode(final ForNode forNode) {
-        forNode.toString(sb);
+        forNode.toString(sb, printTypes);
         forNode.getBody().accept(this);
         return false;
     }
 
     @Override
     public boolean enterFunctionNode(final FunctionNode functionNode) {
-        functionNode.toString(sb);
+        functionNode.toString(sb, printTypes);
         enterBlock(functionNode.getBody());
         return false;
     }
 
     @Override
     public boolean enterIfNode(final IfNode ifNode) {
-        ifNode.toString(sb);
+        ifNode.toString(sb, printTypes);
         ifNode.getPass().accept(this);
 
         final Block fail = ifNode.getFail();
@@ -313,7 +319,7 @@
         indent -= TABWIDTH;
         indent();
         indent += TABWIDTH;
-        labeledNode.toString(sb);
+        labeledNode.toString(sb, printTypes);
         labeledNode.getBody().accept(this);
         printLocalVariableConversion(labeledNode);
         return false;
@@ -321,7 +327,7 @@
 
     @Override
     public boolean enterSplitNode(final SplitNode splitNode) {
-        splitNode.toString(sb);
+        splitNode.toString(sb, printTypes);
         sb.append(EOLN);
         indent += TABWIDTH;
         indent();
@@ -339,7 +345,7 @@
 
     @Override
     public boolean enterSwitchNode(final SwitchNode switchNode) {
-        switchNode.toString(sb);
+        switchNode.toString(sb, printTypes);
         sb.append(" {");
 
         final List<CaseNode> cases = switchNode.getCases();
@@ -347,7 +353,7 @@
         for (final CaseNode caseNode : cases) {
             sb.append(EOLN);
             indent();
-            caseNode.toString(sb);
+            caseNode.toString(sb, printTypes);
             printLocalVariableConversion(caseNode);
             indent += TABWIDTH;
             caseNode.getBody().accept(this);
@@ -370,7 +376,7 @@
 
     @Override
     public boolean enterTryNode(final TryNode tryNode) {
-        tryNode.toString(sb);
+        tryNode.toString(sb, printTypes);
         printLocalVariableConversion(tryNode);
         tryNode.getBody().accept(this);
 
@@ -378,7 +384,7 @@
 
         for (final Block catchBlock : catchBlocks) {
             final CatchNode catchNode = (CatchNode)catchBlock.getStatements().get(0);
-            catchNode.toString(sb);
+            catchNode.toString(sb, printTypes);
             catchNode.getBody().accept(this);
         }
 
@@ -395,7 +401,7 @@
     @Override
     public boolean enterVarNode(final VarNode varNode) {
         sb.append("var ");
-        varNode.getName().toString(sb);
+        varNode.getName().toString(sb, printTypes);
         printLocalVariableConversion(varNode.getName());
         final Node init = varNode.getInit();
         if (init != null) {
@@ -413,9 +419,9 @@
             sb.append("do");
             whileNode.getBody().accept(this);
             sb.append(' ');
-            whileNode.toString(sb);
+            whileNode.toString(sb, printTypes);
         } else {
-            whileNode.toString(sb);
+            whileNode.toString(sb, printTypes);
             whileNode.getBody().accept(this);
         }
 
@@ -424,7 +430,7 @@
 
     @Override
     public boolean enterWithNode(final WithNode withNode) {
-        withNode.toString(sb);
+        withNode.toString(sb, printTypes);
         withNode.getBody().accept(this);
 
         return false;
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java	Mon May 19 15:29:42 2014 +0200
@@ -713,14 +713,15 @@
     }
 
     private static void concatToList(final ArrayList<Object> list, final Object obj) {
-        final boolean isScriptArray = isArray(obj);
+        final boolean isScriptArray  = isArray(obj);
         final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject;
-        if (isScriptArray || obj instanceof Iterable || obj != null && obj.getClass().isArray()) {
+        if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) {
             final Iterator<Object> iter = arrayLikeIterator(obj, true);
             if (iter.hasNext()) {
                 for (int i = 0; iter.hasNext(); ++i) {
                     final Object value = iter.next();
-                    if (value == ScriptRuntime.UNDEFINED && isScriptObject && !((ScriptObject)obj).has(i)) {
+                    final boolean lacksIndex = obj != null && !((ScriptObject)obj).has(i);
+                    if (value == ScriptRuntime.UNDEFINED && isScriptObject && lacksIndex) {
                         // TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling
                         // UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE,
                         // RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Mon May 19 15:29:42 2014 +0200
@@ -234,20 +234,7 @@
      * @return function node resulting from successful parse
      */
     public FunctionNode parse() {
-        return parse(PROGRAM.symbolName());
-    }
-
-    /**
-     * Execute parse and return the resulting function node.
-     * Errors will be thrown and the error manager will contain information
-     * if parsing should fail
-     *
-     * @param scriptName name for the script, given to the parsed FunctionNode
-     *
-     * @return function node resulting from successful parse
-     */
-    public FunctionNode parse(final String scriptName) {
-        return parse(scriptName, 0, source.getLength(), false);
+        return parse(PROGRAM.symbolName(), 0, source.getLength(), false);
     }
 
     /**
@@ -3184,7 +3171,7 @@
 
     @Override
     public String toString() {
-        return "[JavaScript Parsing]";
+        return "'JavaScript Parsing'";
     }
 
     private static void markEval(final LexicalContext lc) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Mon May 19 15:29:42 2014 +0200
@@ -34,9 +34,13 @@
 import java.lang.invoke.MethodType;
 import java.lang.invoke.MutableCallSite;
 import java.lang.invoke.SwitchPoint;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.logging.Level;
+
+import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
 import jdk.nashorn.internal.codegen.types.ArrayType;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
@@ -556,6 +560,43 @@
     }
 
     /**
+     * Debug function for printing out all invalidated program points and their
+     * invalidation mapping to next type
+     * @param ipp
+     * @return string describing the ipp map
+     */
+    private static String toStringInvalidations(final Map<Integer, Type> ipp) {
+        if (ipp == null) {
+            return "";
+        }
+
+        final StringBuilder sb = new StringBuilder();
+
+        for (final Iterator<Map.Entry<Integer, Type>> iter = ipp.entrySet().iterator(); iter.hasNext(); ) {
+            final Map.Entry<Integer, Type> entry = iter.next();
+            final char bct = entry.getValue().getBytecodeStackType();
+
+            sb.append('[').
+                    append(entry.getKey()).
+                    append("->").
+                    append(bct == 'A' ? 'O' : bct).
+                    append(']');
+
+            if (iter.hasNext()) {
+                sb.append(' ');
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private void logRecompile(final String reason, final FunctionNode fn, final MethodType callSiteType, final Map<Integer, Type> ipp) {
+        if (log.isEnabled()) {
+            log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", callSiteType, " ", toStringInvalidations(ipp));
+        }
+    }
+
+    /**
      * Handles a {@link RewriteException} raised during the execution of this function by recompiling (if needed) the
      * function with an optimistic assumption invalidated at the program point indicated by the exception, and then
      * executing a rest-of method to complete the execution with the deoptimized version.
@@ -567,45 +608,90 @@
      */
     private MethodHandle handleRewriteException(final OptimismInfo oldOptimismInfo, final RewriteException re) {
         if (log.isEnabled()) {
-            log.info(new RecompilationEvent(Level.INFO, re, re.getReturnValueNonDestructive()), "\tRewriteException ", re.getMessageShort());
+            log.info(new RecompilationEvent(Level.INFO, re, re.getReturnValueNonDestructive()), "RewriteException ", re.getMessageShort());
         }
 
         final MethodType type = type();
+
         // Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if
         // this function doesn't have a callee parameter.
-        final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ? type : type.insertParameterTypes(0, ScriptFunction.class);
-
-        final FunctionNode fn = oldOptimismInfo.recompile(callSiteType, re);
+        final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ?
+                type :
+                type.insertParameterTypes(0, ScriptFunction.class);
+        final boolean shouldRecompile = oldOptimismInfo.requestRecompile(re);
+        final boolean  canBeDeoptimized;
 
-        final boolean canBeDeoptimized;
-        if (fn != null) {
-            //is recompiled
-            assert optimismInfo == oldOptimismInfo;
-            canBeDeoptimized = fn.canBeDeoptimized();
-            if (log.isEnabled()) {
-                log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ")", canBeDeoptimized ? " can still be deoptimized." : " is completely deoptimized.");
-            }
-            final MethodHandle newInvoker = oldOptimismInfo.data.lookup(fn);
-            invoker = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType()));
-            constructor = null; // Will be regenerated when needed
-            // Note that we only adjust the switch point after we set the invoker/constructor. This is important.
-            if (canBeDeoptimized) {
-                // Otherwise, set a new switch point.
-                oldOptimismInfo.newOptimisticAssumptions();
-            } else {
-                // If we got to a point where we no longer have optimistic assumptions, let the optimism info go.
-                optimismInfo = null;
-            }
-        } else {
+        FunctionNode fn = oldOptimismInfo.reparse();
+        final Compiler compiler = oldOptimismInfo.getCompiler(fn, callSiteType, re); //set to non rest-of
+
+        if (!shouldRecompile) {
             // It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
             // recompiled a deoptimized version for an inner invocation.
+            // We still need to do the rest of from the beginning
             canBeDeoptimized = canBeDeoptimized();
             assert !canBeDeoptimized || optimismInfo == oldOptimismInfo;
+            logRecompile("Rest-of compilation [STANDALONE] ", fn, callSiteType, oldOptimismInfo.invalidatedProgramPoints);
+            return restOfHandle(oldOptimismInfo, compiler.compile(fn, CompilationPhases.COMPILE_ALL_RESTOF), canBeDeoptimized);
+        }
+
+        logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, callSiteType, oldOptimismInfo.invalidatedProgramPoints);
+        fn = compiler.compile(fn, CompilationPhases.COMPILE_UPTO_BYTECODE);
+        log.info("Reusable IR generated");
+
+        assert optimismInfo == oldOptimismInfo;
+
+        // compile the rest of the function, and install it
+        log.info("Generating and installing bytecode from reusable IR...");
+        logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, oldOptimismInfo.invalidatedProgramPoints);
+        final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE);
+
+        FunctionNode fn2 = oldOptimismInfo.reparse();
+        fn2 = compiler.compile(fn2, CompilationPhases.COMPILE_UPTO_BYTECODE);
+        log.info("Done.");
+
+        canBeDeoptimized = normalFn.canBeDeoptimized();
+
+        if (log.isEnabled()) {
+            log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ") ", canBeDeoptimized ? " can still be deoptimized." : " is completely deoptimized.");
         }
 
-        final MethodHandle restOf = changeReturnType(oldOptimismInfo.compileRestOfMethod(callSiteType, re), Object.class);
+        log.info("Looking up invoker...");
+
+        final MethodHandle newInvoker = oldOptimismInfo.data.lookup(fn);
+        invoker     = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType()));
+        constructor = null; // Will be regenerated when needed
+
+        log.info("Done: ", invoker);
+        final MethodHandle restOf = restOfHandle(oldOptimismInfo, compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE_RESTOF), canBeDeoptimized);
+
+        // Note that we only adjust the switch point after we set the invoker/constructor. This is important.
+        if (canBeDeoptimized) {
+            oldOptimismInfo.newOptimisticAssumptions(); // Otherwise, set a new switch point.
+        } else {
+            optimismInfo = null; // If we got to a point where we no longer have optimistic assumptions, let the optimism info go.
+        }
+
+        return restOf;
+    }
+
+    private MethodHandle restOfHandle(final OptimismInfo info, final FunctionNode restOfFunction, final boolean canBeDeoptimized) {
+        assert info != null;
+        assert restOfFunction.getCompileUnit().getUnitClassName().indexOf("restOf") != -1;
+        final MethodHandle restOf =
+                changeReturnType(
+                        info.data.lookupWithExplicitType(
+                                restOfFunction,
+                                MH.type(restOfFunction.getReturnType().getTypeClass(),
+                                        RewriteException.class)),
+                        Object.class);
+
+        if (!canBeDeoptimized) {
+            return restOf;
+        }
+
         // If rest-of is itself optimistic, we must make sure that we can repeat a deoptimization if it, too hits an exception.
-        return canBeDeoptimized ? MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler()) : restOf;
+        return MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler());
+
     }
 
     private static class OptimismInfo {
@@ -625,23 +711,34 @@
             optimisticAssumptions = new SwitchPoint();
         }
 
-        FunctionNode recompile(final MethodType callSiteType, final RewriteException e) {
-            final Type retType = e.getReturnType();
+        boolean requestRecompile(final RewriteException e) {
+            final Type retType            = e.getReturnType();
             final Type previousFailedType = invalidatedProgramPoints.put(e.getProgramPoint(), retType);
+
             if (previousFailedType != null && !previousFailedType.narrowerThan(retType)) {
-                final StackTraceElement[] stack = e.getStackTrace();
-                final String functionId = stack.length == 0 ? data.getName() : stack[0].getClassName() + "." + stack[0].getMethodName();
+                final StackTraceElement[] stack      = e.getStackTrace();
+                final String              functionId = stack.length == 0 ?
+                        data.getName() :
+                        stack[0].getClassName() + "." + stack[0].getMethodName();
+
                 log.info("RewriteException for an already invalidated program point ", e.getProgramPoint(), " in ", functionId, ". This is okay for a recursive function invocation, but a bug otherwise.");
-                return null;
+
+                return false;
             }
+
             SwitchPoint.invalidateAll(new SwitchPoint[] { optimisticAssumptions });
-            return data.compile(callSiteType, invalidatedProgramPoints, e.getRuntimeScope(), "Deoptimizing recompilation", data.getDefaultTransform(callSiteType));
+
+            return true;
         }
 
-        MethodHandle compileRestOfMethod(final MethodType callSiteType, final RewriteException e) {
+        Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final RewriteException e) {
+            return data.getCompiler(fn, actualCallSiteType, e.getRuntimeScope(), invalidatedProgramPoints, getEntryPoints(e));
+        }
+
+        private static int[] getEntryPoints(final RewriteException e) {
             final int[] prevEntryPoints = e.getPreviousContinuationEntryPoints();
             final int[] entryPoints;
-            if(prevEntryPoints == null) {
+            if (prevEntryPoints == null) {
                 entryPoints = new int[1];
             } else {
                 final int l = prevEntryPoints.length;
@@ -649,7 +746,11 @@
                 System.arraycopy(prevEntryPoints, 0, entryPoints, 1, l);
             }
             entryPoints[0] = e.getProgramPoint();
-            return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints, e.getRuntimeScope(), data.getDefaultTransform(callSiteType));
+            return entryPoints;
+        }
+
+        FunctionNode reparse() {
+            return data.reparse();
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Mon May 19 15:29:42 2014 +0200
@@ -63,6 +63,14 @@
         return '\'' + name + "' code=" + functions;
     }
 
+    private static MethodType widen(final MethodType cftype) {
+        final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
+        for (int i = 0; i < cftype.parameterCount(); i++) {
+            paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
+        }
+        return MH.type(cftype.returnType(), paramTypes);
+    }
+
     /**
      * Used to find an apply to call version that fits this callsite.
      * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
@@ -82,15 +90,10 @@
                 continue;
             }
 
-            final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
-            for (int i = 0; i < cftype.parameterCount(); i++) {
-                paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
-            }
-
-            if (MH.type(cftype.returnType(), paramTypes).equals(type)) {
+            if (widen(cftype).equals(widen(type))) {
                 return cf;
             }
-        }
+         }
 
         return null;
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Mon May 19 15:29:42 2014 +0200
@@ -57,10 +57,8 @@
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
-import jdk.nashorn.internal.codegen.CompilationEnvironment;
-import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
 import jdk.nashorn.internal.codegen.Compiler;
-import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.debug.ASTWriter;
@@ -944,7 +942,7 @@
         }
 
         if (env._print_parse) {
-            getErr().println(new PrintVisitor(functionNode));
+            getErr().println(new PrintVisitor(functionNode, true, false));
         }
 
         if (env._parse_only) {
@@ -956,18 +954,17 @@
         final CodeSource   cs     = new CodeSource(url, (CodeSigner[])null);
         final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
 
-        final CompilationPhases phases = CompilationEnvironment.CompilationPhases.EAGER;
+        final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
+
         final Compiler compiler = new Compiler(
-                new CompilationEnvironment(
-                    this,
-                    phases.
-                        makeOptimistic(
-                            ScriptEnvironment.globalOptimistic()),
-                    strict),
-                installer);
+                this,
+                env,
+                installer,
+                source,
+                functionNode.getSourceURL(),
+                strict | functionNode.isStrict());
 
-        final FunctionNode newFunctionNode = compiler.compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
-        script = compiler.install(newFunctionNode);
+        script = compiler.compile(functionNode, phases).getRootClass();
         cacheClass(source, script);
 
         return script;
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Mon May 19 15:29:42 2014 +0200
@@ -32,19 +32,16 @@
 import java.lang.invoke.MethodType;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
+
 import jdk.internal.dynalink.support.NameCodec;
-import jdk.nashorn.internal.codegen.ApplySpecialization;
-import jdk.nashorn.internal.codegen.CompilationEnvironment;
-import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
 import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.codegen.FunctionSignature;
-import jdk.nashorn.internal.codegen.ParamTypeMap;
+import jdk.nashorn.internal.codegen.TypeMap;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.LexicalContext;
@@ -70,7 +67,7 @@
     public static final boolean LAZY_COMPILATION = Options.getBooleanProperty("nashorn.lazy");
 
     /** Prefix used for all recompiled script classes */
-    public static final String RECOMPILATION_PREFIX = "Script$Recompilation$";
+    public static final String RECOMPILATION_PREFIX = "Recompilation$";
 
     /** Unique function node id for this function node */
     private final int functionNodeId;
@@ -113,9 +110,6 @@
 
     private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
 
-    /** Unique id for classes needed to wrap recompiled script functions */
-    private static final AtomicInteger RECOMPILE_ID = new AtomicInteger(0);
-
     private final DebugLogger log;
 
     private final Map<String, Integer> externalScopeDepths;
@@ -338,7 +332,7 @@
         }
     }
 
-    private FunctionNode reparse(final String scriptName) {
+    FunctionNode reparse() {
         final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID;
         // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node
         final int descPosition = Token.descPosition(token);
@@ -355,145 +349,80 @@
             parser.setFunctionName(functionName);
         }
 
-        final FunctionNode program = parser.parse(scriptName, descPosition, Token.descLength(token), true);
+        final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true);
         // Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a
         // single function, extract it from the program.
         return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName).setSourceURL(null,  sourceURL);
     }
 
-    private static String stringifyInvalidations(final Map<Integer, Type> ipp) {
-        if (ipp == null) {
-            return "";
+    TypeMap typeMap(final MethodType fnCallSiteType) {
+        if (fnCallSiteType == null) {
+            return null;
         }
-        final StringBuilder sb = new StringBuilder();
-        final Iterator<Map.Entry<Integer, Type>> iter = ipp.entrySet().iterator();
-        while (iter.hasNext()) {
-            final Map.Entry<Integer, Type> entry = iter.next();
-            final char bct = entry.getValue().getBytecodeStackType();
-            sb.append('[').
-                    append(entry.getKey()).
-                    append("->").
-                    append(bct == 'A' ? 'O' : bct).
-                    append(']');
-            if (iter.hasNext()) {
-                sb.append(' ');
-            }
+
+        if (CompiledFunction.isVarArgsType(fnCallSiteType)) {
+            return null;
         }
-        return sb.toString();
+
+        return new TypeMap(functionNodeId, explicitParams(fnCallSiteType), needsCallee());
     }
 
-    private FunctionNodeTransform getNopTransform() {
-        return new FunctionNodeTransform() {
-            @Override
-            FunctionNode apply(final FunctionNode functionNode) {
-                return functionNode;
-            }
-
-            @Override
-            int getArity() {
-                return RecompilableScriptFunctionData.this.getArity();
-            }
-
-            @Override
-            public String toString() {
-                return "[NopTransform]";
-            }
-        };
+    private static ScriptObject newLocals(final ScriptObject runtimeScope) {
+        final ScriptObject locals = Global.newEmptyInstance();
+        locals.setProto(runtimeScope);
+        return locals;
     }
 
-    FunctionNodeTransform getDefaultTransform(final MethodType callSiteType) {
-        return new ApplyToCallTransform(this, callSiteType);
-    }
-
-    private ParamTypeMap typeMap(final MethodType fnCallSiteType, final FunctionNodeTransform tr) {
-        if (isVariableArity() && !tr.wasTransformed()) {
-            return null;
-        }
-        return new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType, tr.getArity()));
+    private Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
+        return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
     }
 
-    MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
-        if (log.isEnabled()) {
-            log.info("Rest-of compilation of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
-        }
-
-        final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet() + "$restOf";
-
-        FunctionNode fn = tr.apply(reparse(scriptName));
-        final ParamTypeMap ptm = typeMap(fnCallSiteType, tr);
-
-        final Compiler compiler = new Compiler(
-                new CompilationEnvironment(
-                    context,
-                    CompilationPhases.EAGER.makeOptimistic(),
-                    isStrict(),
-                    this,
-                    runtimeScope,
-                    ptm,
-                    invalidatedProgramPoints,
-                    continuationEntryPoints,
-                    true
-                    ),
-                installer);
-
-        fn = compiler.compile(scriptName, fn);
-        compiler.install(fn);
-
-        // look up the rest of method
-        return lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
+    Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType, final ScriptObject runtimeScope, final Map<Integer, Type> ipp, final int[] cep) {
+        return new Compiler(
+                context,
+                context.getEnv(),
+                installer,
+                functionNode.getSource(),  // source
+                functionNode.getSourceURL(),
+                isStrict() | functionNode.isStrict(), // is strict
+                true,       // is on demand
+                this,       // compiledFunction, i.e. this RecompilableScriptFunctionData
+                typeMap(actualCallSiteType), // type map
+                ipp, // invalidated program points
+                cep, // continuation entry points
+                runtimeScope); // runtime scope
     }
 
-    private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
+    private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
         // We're creating an empty script object for holding local variables. AssignSymbols will populate it with
         // explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
         // CompilationEnvironment#declareLocalSymbol()).
-        final ScriptObject locals = Global.newEmptyInstance();
-        locals.setProto(runtimeScope);
-
-        return compile(actualCallSiteType, null, locals, "Type specialized compilation", tr);
-    }
-
-    FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final ScriptObject runtimeScope, final String reason, final FunctionNodeTransform tr) {
-        final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet();
-        final MethodType fnCallSiteType = actualCallSiteType == null ? null : actualCallSiteType.changeParameterType(0, ScriptFunction.class);
 
         if (log.isEnabled()) {
-            log.info(reason, " of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
+            log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType);
         }
 
-        FunctionNode fn = tr.apply(reparse(scriptName));
-
-        final ParamTypeMap ptm = fnCallSiteType == null ? null : typeMap(fnCallSiteType, tr);
-
-        final CompilationPhases phases = CompilationPhases.EAGER;
-        final Compiler compiler = new Compiler(
-            new CompilationEnvironment(
-                    context,
-                    phases.makeOptimistic(ScriptEnvironment.globalOptimistic()),
-                    isStrict(),
-                    this,
-                    runtimeScope,
-                    ptm,
-                    invalidatedProgramPoints,
-                    true),
-            installer);
-
-        fn = compiler.compile(scriptName, fn);
-        compiler.install(fn);
-
-        return fn;
+        final FunctionNode fn = reparse();
+        return getCompiler(fn, actualCallSiteType, runtimeScope).compile(fn, CompilationPhases.COMPILE_ALL);
     }
 
-    private static MethodType explicitParams(final MethodType callSiteType, final int arity) {
+    Context getContext() {
+        return context;
+    }
+
+    private MethodType explicitParams(final MethodType callSiteType) {
+        if (CompiledFunction.isVarArgsType(callSiteType)) {
+            return null;
+        }
+
         final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type
         final int callSiteParamCount = noCalleeThisType.parameterCount();
 
         // Widen parameters of reference types to Object as we currently don't care for specialization among reference
         // types. E.g. call site saying (ScriptFunction, Object, String) should still link to (ScriptFunction, Object, Object)
-        final int minParams = Math.min(callSiteParamCount, arity);
         final Class<?>[] paramTypes = noCalleeThisType.parameterArray();
         boolean changed = false;
-        for (int i = 0; i < minParams; ++i) {
+        for (int i = 0; i < paramTypes.length; ++i) {
             final Class<?> paramType = paramTypes[i];
             if (!(paramType.isPrimitive() || paramType == Object.class)) {
                 paramTypes[i] = Object.class;
@@ -502,14 +431,10 @@
         }
         final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType;
 
-        // Match arity
-        if (callSiteParamCount < arity) {
-            return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(arity - callSiteParamCount, Object.class));
-        } else if (callSiteParamCount > arity) {
-            return generalized.dropParameterTypes(arity, callSiteParamCount);
-        } else {
-            return generalized;
+        if (callSiteParamCount < getArity()) {
+            return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(getArity() - callSiteParamCount, Object.class));
         }
+        return generalized;
     }
 
     private FunctionNode extractFunctionFromScript(final FunctionNode script) {
@@ -531,10 +456,12 @@
     }
 
     MethodHandle lookup(final FunctionNode fn) {
+        final MethodType type = new FunctionSignature(fn).getMethodType();
+        log.info("Looking up ", DebugLogger.quote(fn.getName()), " type=", type);
         return lookupWithExplicitType(fn, new FunctionSignature(fn).getMethodType());
     }
 
-    private MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) {
+    MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) {
         return lookupCodeMethod(fn.getCompileUnit(), targetType);
     }
 
@@ -615,10 +542,11 @@
         synchronized (code) {
             CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
             if (existingBest == null) {
-                existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, getNopTransform()), callSiteType);
+                existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
             }
 
             assert existingBest != null;
+            //we are calling a vararg method with real args
             boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
 
             //if the best one is an apply to call, it has to match the callsite exactly
@@ -632,7 +560,7 @@
             }
 
             if (applyToCall) {
-                final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope, new ApplyToCallTransform(this, callSiteType));
+                final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope);
                 if (fn.hasOptimisticApplyToCall()) { //did the specialization work
                     existingBest = addCode(fn, callSiteType);
                 }
@@ -725,66 +653,4 @@
 
         return true;
     }
-
-    private static abstract class FunctionNodeTransform {
-
-        abstract int getArity();
-
-        boolean wasTransformed() {
-            return false;
-        }
-
-        abstract FunctionNode apply(final FunctionNode functionNode);
-    }
-
-    /**
-     * Helper class for transforming apply calls to calls
-     */
-    private static class ApplyToCallTransform extends FunctionNodeTransform {
-        private final RecompilableScriptFunctionData data;
-        private final MethodType actualCallSiteType;
-        private int arity;
-        private FunctionNode initialFunctionNode;
-        private FunctionNode transformedFunctionNode;
-
-        ApplyToCallTransform(final RecompilableScriptFunctionData data, final MethodType actualCallSiteType) {
-            this.data = data;
-            this.actualCallSiteType = actualCallSiteType;
-            this.arity = data.getArity();
-        }
-
-        @Override
-        public FunctionNode apply(final FunctionNode functionNode) {
-            this.initialFunctionNode = functionNode;
-            if (data.isVariableArity() && !CompiledFunction.isVarArgsType(actualCallSiteType)) {
-                final ApplySpecialization spec = new ApplySpecialization(data.context, data, functionNode, actualCallSiteType);
-                if (spec.transform()) {
-                    setTransformedFunctionNode(spec.getFunctionNode());
-                    return transformedFunctionNode;
-                }
-            }
-            return functionNode;
-        }
-
-        private void setTransformedFunctionNode(final FunctionNode transformedFunctionNode) {
-            this.transformedFunctionNode = transformedFunctionNode;
-            assert !transformedFunctionNode.isVarArg();
-            this.arity = transformedFunctionNode.getParameters().size();
-        }
-
-        @Override
-        public int getArity() {
-            return arity;
-        }
-
-        @Override
-        public boolean wasTransformed() {
-            return initialFunctionNode != transformedFunctionNode;
-        }
-
-        @Override
-        public String toString() {
-            return "[ApplyToCallTransform]";
-        }
-    }
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java	Mon May 19 15:29:42 2014 +0200
@@ -230,7 +230,7 @@
             return (Object[])obj;
         }
 
-        assert obj instanceof int[] || obj instanceof long[] || obj instanceof double[] : obj.getClass().getName();
+        assert obj instanceof int[] || obj instanceof long[] || obj instanceof double[] : obj + " is " + obj.getClass().getName();
 
         final int l = Array.getLength(obj);
         final Object[] out = new Object[l];
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Mon May 19 15:29:42 2014 +0200
@@ -505,11 +505,12 @@
         final String  name       = getName();
         final boolean isUnstable = request.isCallSiteUnstable();
         final boolean scopeCall  = NashornCallSiteDescriptor.isScope(desc);
-
         final boolean isCall     = !scopeCall && data.isBuiltin() && "call".equals(name);
         final boolean isApply    = !scopeCall && data.isBuiltin() && "apply".equals(name);
 
-        if (isUnstable && !(isApply || isCall)) {
+        final boolean isApplyOrCall = isCall | isApply;
+
+        if (isUnstable && !isApplyOrCall) {
             //megamorphic - replace call with apply
             final MethodHandle handle;
             //ensure that the callsite is vararg so apply can consume it
@@ -534,7 +535,7 @@
         MethodHandle guard = null;
 
         // Special handling of Function.apply and Function.call. Note we must be invoking
-        if ((isApply || isCall) && !isUnstable) {
+        if (isApplyOrCall && !isUnstable) {
             final Object[] args = request.getArguments();
             if (Bootstrap.isCallable(args[1])) {
                 return createApplyOrCallCall(isApply, desc, request, args);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptLoader.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptLoader.java	Mon May 19 15:29:42 2014 +0200
@@ -49,7 +49,7 @@
     }
 
     @Override
-    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+    protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
         checkPackageAccess(name);
         if (name.startsWith(NASHORN_PKG_PREFIX)) {
             return context.getSharedLoader().loadClass(name);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Mon May 19 15:29:42 2014 +0200
@@ -2260,21 +2260,22 @@
         final boolean      scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
 
         if (find != null) {
-            final Object   value        = find.getObjectValue();
-            ScriptFunction func         = null;
-            MethodHandle   methodHandle = null;
+            final Object   value = find.getObjectValue();
+            ScriptFunction func  = null;
+            MethodHandle   mh    = null;
 
             if (value instanceof ScriptFunction) {
                 func = (ScriptFunction)value;
-                methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
+                mh   = getCallMethodHandle(func, desc.getMethodType(), name);
             }
 
-            if (methodHandle != null) {
-                if (scopeAccess && func.isStrict()) {
-                    methodHandle = bindTo(methodHandle, UNDEFINED);
+            if (mh != null) {
+                assert func != null;
+                if (scopeAccess && func != null && func.isStrict()) {
+                    mh = bindTo(mh, UNDEFINED);
                 }
                 return new GuardedInvocation(
-                        methodHandle,
+                        mh,
                         //TODO this always does a scriptobject check
                         getKnownFunctionPropertyGuard(
                                 getMap(),
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java	Mon May 19 15:29:42 2014 +0200
@@ -39,7 +39,7 @@
  * It can be invoked repeatedly to create multiple adapter classes from the same bytecode; adapter classes that have
  * class-level overrides must be re-created for every set of such overrides. Note that while this class is named
  * "class loader", it does not, in fact, extend {@code ClassLoader}, but rather uses them internally. Instances of this
- * class are normally created by {@link JavaAdapterBytecodeGenerator}.
+ * class are normally created by {@code JavaAdapterBytecodeGenerator}.
  */
 final class JavaAdapterClassLoader {
     private static final AccessControlContext CREATE_LOADER_ACC_CTXT = ClassAndLoader.createPermAccCtxt("createClassLoader");
--- a/nashorn/src/jdk/nashorn/tools/Shell.java	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/tools/Shell.java	Mon May 19 15:29:42 2014 +0200
@@ -39,7 +39,7 @@
 import java.util.ResourceBundle;
 import jdk.nashorn.api.scripting.NashornException;
 import jdk.nashorn.internal.codegen.Compiler;
-import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.debug.ASTWriter;
 import jdk.nashorn.internal.ir.debug.PrintVisitor;
@@ -259,8 +259,14 @@
                 }
 
                 //null - pass no code installer - this is compile only
-                new Compiler(env).
-                    compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
+                new Compiler(
+                       context,
+                       env,
+                       null,
+                       functionNode.getSource(),
+                       functionNode.getSourceURL(),
+                       env._strict | functionNode.isStrict()).
+                       compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL);
             }
         } finally {
             env.getOut().flush();
--- a/nashorn/test/script/trusted/JDK-8006529.js	Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/test/script/trusted/JDK-8006529.js	Mon May 19 15:29:42 2014 +0200
@@ -42,7 +42,9 @@
 var forName             = java.lang.Class["forName(String)"];
 var Parser              = forName("jdk.nashorn.internal.parser.Parser").static
 var Compiler            = forName("jdk.nashorn.internal.codegen.Compiler").static
+var CompilationPhases   = forName("jdk.nashorn.internal.codegen.Compiler$CompilationPhases").static;
 var Context             = forName("jdk.nashorn.internal.runtime.Context").static
+var CodeInstaller       = forName("jdk.nashorn.internal.runtime.CodeInstaller").static
 var ScriptEnvironment   = forName("jdk.nashorn.internal.runtime.ScriptEnvironment").static
 var Source              = forName("jdk.nashorn.internal.runtime.Source").static
 var FunctionNode        = forName("jdk.nashorn.internal.ir.FunctionNode").static
@@ -54,9 +56,11 @@
 var ThrowErrorManager   = forName("jdk.nashorn.internal.runtime.Context$ThrowErrorManager").static
 var ErrorManager        = forName("jdk.nashorn.internal.runtime.ErrorManager").static
 var Debug               = forName("jdk.nashorn.internal.runtime.Debug").static
+var String              = forName("java.lang.String").static
+var boolean             = Java.type("boolean");
 
 var parseMethod = Parser.class.getMethod("parse");
-var compileMethod = Compiler.class.getMethod("compile", FunctionNode.class);
+var compileMethod = Compiler.class.getMethod("compile", FunctionNode.class, CompilationPhases.class);
 var getBodyMethod = FunctionNode.class.getMethod("getBody");
 var getStatementsMethod = Block.class.getMethod("getStatements");
 var getInitMethod = VarNode.class.getMethod("getInit");
@@ -65,6 +69,7 @@
 var lhsMethod = BinaryNode.class.getMethod("lhs")
 var binaryRhsMethod = BinaryNode.class.getMethod("rhs")
 var debugIdMethod = Debug.class.getMethod("id", java.lang.Object.class)
+var compilePhases = CompilationPhases.class.getField("COMPILE_UPTO_BYTECODE").get(null);
 
 // These are method names of methods in FunctionNode class
 var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'usesSelfSymbol', 'isSplit', 'hasEval', 'allVarsInScope', 'isStrict']
@@ -115,22 +120,23 @@
 
 var SourceConstructor = Source.class.getConstructor(java.lang.String.class, java.lang.String.class)
 var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
-var CompilerConstructor = Compiler.class.getConstructor(ScriptEnvironment.class)
+var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, String.class, boolean.class);
 
 // compile(script) -- compiles a script specified as a string with its 
 // source code, returns a jdk.nashorn.internal.ir.FunctionNode object 
 // representing it.
-function compile(source) {
+function compile(source, phases) {
     var source = SourceConstructor.newInstance("<no name>", source);
 
-    var env = getEnvMethod.invoke(getContextMethod.invoke(null))
+    var ctxt = getContextMethod.invoke(null);
+    var env = getEnvMethod.invoke(ctxt);
 
     var parser   = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance());
     var func     = parseMethod.invoke(parser);
 
-    var compiler = CompilerConstructor.newInstance(env);
+    var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, null, false);
 
-    return compileMethod.invoke(compiler, func);
+    return compileMethod.invoke(compiler, func, phases);
 };
 
 var allAssertions = (function() {
@@ -166,8 +172,8 @@
 // assertions are true in the first function in the given script; "script"
 // is a string with the source text of the script.
 function testFirstFn(script) {
-    arguments[0] = getFirstFunction(compile(script))
-    test.apply(null, arguments)
+    arguments[0] = getFirstFunction(compile(script, compilePhases));
+    test.apply(null, arguments);
 }
 
 // ---------------------------------- ACTUAL TESTS START HERE --------------