changeset 88:43e32b36153c

8008199: Lazy compilation and trampoline implementation Summary: The code pipeline now supports lazy compilation, which can be used to only compile certain FunctionNodes and leave others be, saving startup time. When these uncompiled nodes are hit, a trampoline will force them to be recompiled. This can also be used to specialize compilation fixing parameter types and return types to a callsite specific compilation. This will give performance. Reviewed-by: attila, sundar
author lagergren
date Thu, 14 Feb 2013 13:01:52 +0100
parents 8c72a2bec1be
children 5a820fb11814
files src/jdk/nashorn/internal/codegen/Attr.java src/jdk/nashorn/internal/codegen/ClassEmitter.java src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/CompilationPhase.java src/jdk/nashorn/internal/codegen/CompileUnit.java src/jdk/nashorn/internal/codegen/Compiler.java src/jdk/nashorn/internal/codegen/ConstantData.java src/jdk/nashorn/internal/codegen/FinalizeTypes.java src/jdk/nashorn/internal/codegen/FoldConstants.java src/jdk/nashorn/internal/codegen/FunctionSignature.java src/jdk/nashorn/internal/codegen/Lower.java src/jdk/nashorn/internal/codegen/SharedScopeCall.java src/jdk/nashorn/internal/codegen/Splitter.java src/jdk/nashorn/internal/codegen/WeighNodes.java src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java src/jdk/nashorn/internal/codegen/objects/ObjectClassGenerator.java src/jdk/nashorn/internal/ir/FunctionNode.java src/jdk/nashorn/internal/ir/debug/JSONWriter.java src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java src/jdk/nashorn/internal/objects/Global.java src/jdk/nashorn/internal/objects/NativeJava.java src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java src/jdk/nashorn/internal/parser/Lexer.java src/jdk/nashorn/internal/parser/Parser.java src/jdk/nashorn/internal/runtime/CodeInstaller.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/DebugLogger.java src/jdk/nashorn/internal/runtime/FindProperty.java src/jdk/nashorn/internal/runtime/ScriptFunction.java src/jdk/nashorn/internal/runtime/ScriptFunctionData.java src/jdk/nashorn/internal/runtime/ScriptingFunctions.java src/jdk/nashorn/tools/Shell.java test/script/trusted/JDK-8006529.js test/src/jdk/nashorn/internal/parser/ParserTest.java test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java
diffstat 37 files changed, 1427 insertions(+), 1046 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk/nashorn/internal/codegen/Attr.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/Attr.java	Thu Feb 14 13:01:52 2013 +0100
@@ -84,7 +84,6 @@
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.Source;
 
 /**
  * This is the attribution pass of the code generator. Attr takes Lowered IR,
@@ -102,11 +101,8 @@
  */
 
 final class Attr extends NodeOperatorVisitor {
-    /** Current compiler. */
-    private final Compiler compiler;
-
-    /** Current source. */
-    private final Source source;
+    /** Context compiler. */
+    private final Context context;
 
     /**
      * Local definitions in current block (to discriminate from function
@@ -122,16 +118,16 @@
      */
     private Set<String> localUses;
 
-    private static final DebugLogger LOG = new DebugLogger("attr");
+    private static final DebugLogger LOG   = new DebugLogger("attr");
+    private static final boolean     DEBUG = LOG.isEnabled();
 
     /**
      * Constructor.
      *
      * @param compiler the compiler
      */
-    Attr(final Compiler compiler) {
-        this.compiler = compiler;
-        this.source   = compiler.getSource();
+    Attr(final Context context) {
+        this.context = context;
     }
 
     @Override
@@ -231,6 +227,11 @@
     @Override
     public Node enter(final FunctionNode functionNode) {
         start(functionNode, false);
+        if (functionNode.isLazy()) {
+            LOG.info("LAZY: " + functionNode.getName());
+            end(functionNode);
+            return null;
+        }
 
         clearLocalDefs();
         clearLocalUses();
@@ -257,7 +258,7 @@
         }
 
         if (functionNode.isScript()) {
-            initFromPropertyMap(compiler.getContext(), functionNode);
+            initFromPropertyMap(context, functionNode);
         }
 
         // Add function name as local symbol
@@ -378,7 +379,7 @@
                 final FunctionNode functionNode = (FunctionNode)symbol.getNode();
                 assert functionNode.getCalleeNode() != null;
 
-                final VarNode var = new VarNode(source, functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
+                final VarNode var = new VarNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
                 //newTemporary(Type.OBJECT, var); //ScriptFunction? TODO
 
                 functionNode.setNeedsSelfSymbol(var);
@@ -489,15 +490,21 @@
         if (functionNode != null) {
             functionNode.addReferencingParentBlock(getCurrentBlock());
         }
-        end(referenceNode);
-
         return referenceNode;
     }
 
     @Override
     public Node leave(final ReferenceNode referenceNode) {
         newTemporary(Type.OBJECT, referenceNode); //reference node type is always an object, i.e. the scriptFunction. the function return type varies though
+
+        final FunctionNode functionNode = referenceNode.getReference();
+        //assert !functionNode.getType().isUnknown() || functionNode.isLazy() : functionNode.getType();
+        if (functionNode.isLazy()) {
+            LOG.info("Lazy function node call reference: " + functionNode.getName() + " => Promoting to OBJECT");
+            functionNode.setReturnType(Type.OBJECT);
+        }
         end(referenceNode);
+
         return referenceNode;
     }
 
@@ -546,7 +553,7 @@
             type = Type.OBJECT;
         }
 
-        switchNode.setTag(newInternal(compiler.uniqueName(SWITCH_TAG_PREFIX.tag()), type));
+        switchNode.setTag(newInternal(getCurrentFunctionNode().uniqueName(SWITCH_TAG_PREFIX.tag()), type));
 
         end(switchNode);
 
@@ -1111,7 +1118,7 @@
     @Override
     public Node leave(final ForNode forNode) {
         if (forNode.isForIn()) {
-            forNode.setIterator(newInternal(getCurrentFunctionNode(), compiler.uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
+            forNode.setIterator(newInternal(getCurrentFunctionNode(), getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
             /*
              * Iterators return objects, so we need to widen the scope of the
              * init variable if it, for example, has been assigned double type
@@ -1321,7 +1328,7 @@
     }
 
     private Symbol exceptionSymbol() {
-        return newInternal(compiler.uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
+        return newInternal(getCurrentFunctionNode().uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
     }
 
     /**
@@ -1382,6 +1389,11 @@
                     }
                 }
 
+                @Override
+                public Node enter(final FunctionNode node) {
+                    return node.isLazy() ? null : node;
+                }
+
                 /**
                  * Eg.
                  *
@@ -1553,17 +1565,19 @@
     }
 
     private Node start(final Node node, final boolean printNode) {
-        final StringBuilder sb = new StringBuilder();
+        if (DEBUG) {
+            final StringBuilder sb = new StringBuilder();
 
-        sb.append("[ENTER ").
-            append(name(node)).
-            append("] ").
-            append(printNode ? node.toString() : "").
-            append(" in '").
-            append(getCurrentFunctionNode().getName()).
-            append("'");
-        LOG.info(sb.toString());
-        LOG.indent();
+            sb.append("[ENTER ").
+                append(name(node)).
+                append("] ").
+                append(printNode ? node.toString() : "").
+                append(" in '").
+                append(getCurrentFunctionNode().getName()).
+                append("'");
+            LOG.info(sb.toString());
+            LOG.indent();
+        }
 
         return node;
     }
@@ -1573,24 +1587,26 @@
     }
 
     private Node end(final Node node, final boolean printNode) {
-        final StringBuilder sb = new StringBuilder();
+        if (DEBUG) {
+            final StringBuilder sb = new StringBuilder();
 
-        sb.append("[LEAVE ").
-            append(name(node)).
-            append("] ").
-            append(printNode ? node.toString() : "").
-            append(" in '").
-            append(getCurrentFunctionNode().getName());
+            sb.append("[LEAVE ").
+                append(name(node)).
+                append("] ").
+                append(printNode ? node.toString() : "").
+                append(" in '").
+                append(getCurrentFunctionNode().getName());
 
-        if (node.getSymbol() == null) {
-            sb.append(" <NO SYMBOL>");
-        } else {
-            sb.append(" <symbol=").append(node.getSymbol()).append('>');
+            if (node.getSymbol() == null) {
+                sb.append(" <NO SYMBOL>");
+            } else {
+                sb.append(" <symbol=").append(node.getSymbol()).append('>');
+            }
+
+            LOG.unindent();
+            LOG.info(sb.toString());
         }
 
-        LOG.unindent();
-        LOG.info(sb.toString());
-
         return node;
     }
 }
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Thu Feb 14 13:01:52 2013 +0100
@@ -52,6 +52,8 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.HashSet;
@@ -166,8 +168,8 @@
      * @param unitClassName Compile unit class name.
      * @param strictMode    Should we generate this method in strict mode
      */
-    ClassEmitter(final Compiler compiler, final String unitClassName, final boolean strictMode) {
-        this(compiler.getContext(),
+    ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) {
+        this(context,
              new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
                 private static final String OBJECT_CLASS  = "java/lang/Object";
 
@@ -187,13 +189,23 @@
         this.unitClassName        = unitClassName;
         this.constantMethodNeeded = new HashSet<>();
 
-        cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, Compiler.pathName(jdk.nashorn.internal.scripts.JS$.class.getName()), null);
-        cw.visitSource(compiler.getSource().getName(), null);
+        cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, pathName(jdk.nashorn.internal.scripts.JS$.class.getName()), null);
+        cw.visitSource(sourceName, null);
 
         defineCommonStatics(strictMode);
     }
 
     /**
+     * Convert a binary name to a package/class name.
+     *
+     * @param name Binary name.
+     * @return Package/class name.
+     */
+    private static String pathName(final String name) {
+        return name.replace('.', '/');
+    }
+
+    /**
      * Define the static fields common in all scripts.
      * @param strictMode Should we generate this method in strict mode
      */
@@ -295,7 +307,7 @@
      * Ensure a get constant method is issued for the class.
      * @param cls Class of constant.
      */
-    public void needGetConstantMethod(final Class<?> cls) {
+    void needGetConstantMethod(final Class<?> cls) {
         constantMethodNeeded.add(cls);
     }
 
@@ -348,22 +360,15 @@
 
     /**
      * Disassemble an array of byte code.
-     *
-     * @param context   the context
      * @param bytecode  byte array representing bytecode
+     * @return disassembly as human readable string
      */
-    public static void disassemble(final Context context, final byte[] bytecode) {
-        new ClassReader(bytecode).accept(new TraceClassVisitor(context.getErr()), 0);
-    }
-
-    /**
-     * Verify an array of byte code as a valid Java class
-     *
-     * @param context  the context
-     * @param bytecode the bytecode array
-     */
-    public static void verify(final Context context, final byte[] bytecode) {
-        context.verify(bytecode);
+    public static String disassemble(final byte[] bytecode) {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (final PrintWriter pw = new PrintWriter(baos)) {
+            new ClassReader(bytecode).accept(new TraceClassVisitor(pw), 0);
+        }
+        return new String(baos.toByteArray());
     }
 
     /**
@@ -459,7 +464,7 @@
         final MethodVisitor mv = cw.visitMethod(
             ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
             functionNode.getName(),
-            FunctionSignature.functionSignature(functionNode),
+            new FunctionSignature(functionNode).toString(),
             null,
             null);
 
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Feb 14 13:01:52 2013 +0100
@@ -25,8 +25,10 @@
 
 package jdk.nashorn.internal.codegen;
 
+import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
 import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF;
@@ -35,6 +37,7 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
 import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
@@ -48,6 +51,7 @@
 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
 
 import java.io.PrintWriter;
+import java.lang.invoke.MethodHandle;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumSet;
@@ -63,8 +67,8 @@
 import jdk.nashorn.internal.codegen.MethodEmitter.Label;
 import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode;
 import jdk.nashorn.internal.codegen.objects.FieldObjectCreator;
-import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
 import jdk.nashorn.internal.codegen.objects.MapCreator;
+import jdk.nashorn.internal.codegen.objects.ObjectCreator;
 import jdk.nashorn.internal.codegen.objects.ObjectMapCreator;
 import jdk.nashorn.internal.codegen.types.ArrayType;
 import jdk.nashorn.internal.codegen.types.Type;
@@ -111,11 +115,13 @@
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Lexer.RegexToken;
 import jdk.nashorn.internal.parser.TokenType;
+import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.Scope;
 import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Source;
@@ -143,12 +149,18 @@
  */
 public final class CodeGenerator extends NodeOperatorVisitor {
 
-    /** Current compiler */
+    /** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */
+    private static final String GLOBAL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "Global";
+
+    /** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */
+    private static final String SCRIPTFUNCTION_IMPL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl";
+
+    private static final String SCRIPTFUNCTION_TRAMPOLINE_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionTrampolineImpl";
+
+    /** Constant data & installation. The only reason the compiler keeps this is because it is assigned
+     *  by reflection in class installation */
     private final Compiler compiler;
 
-    /** Compiler context */
-    private final Context context;
-
     /** Call site flags given to the code generator to be used for all generated call sites */
     private final int callSiteFlags;
 
@@ -168,17 +180,7 @@
      */
     CodeGenerator(final Compiler compiler) {
         this.compiler      = compiler;
-        this.context       = compiler.getContext();
-        this.callSiteFlags = context._callsite_flags;
-    }
-
-    /**
-     * Get the compiler
-     *
-     * @return the compiler used
-     */
-    public Compiler getCompiler() {
-        return compiler;
+        this.callSiteFlags = compiler.getContext()._callsite_flags;
     }
 
     /**
@@ -320,7 +322,7 @@
          */
         final CodeGenerator codegen = this;
 
-        node.accept(new NodeVisitor(compileUnit, method) {
+        node.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
             @Override
             public Node enter(final IdentNode identNode) {
                 loadIdent(identNode);
@@ -534,7 +536,7 @@
         final FunctionNode currentFunction = getCurrentFunctionNode();
         final Block        currentBlock    = getCurrentBlock();
 
-        function.accept(new NodeVisitor(compileUnit, method) {
+        function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
 
             private void sharedScopeCall(final IdentNode identNode, final int flags) {
                 final Symbol symbol = identNode.getSymbol();
@@ -651,7 +653,7 @@
                 final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
 
                 if (callee.needsCallee()) {
-                    new FunctionObjectCreator(CodeGenerator.this, callee).makeObject(method);
+                    newFunctionObject(callee);
                 }
 
                 if (callee.isStrictMode()) { // self is undefined
@@ -969,14 +971,18 @@
 
     @Override
     public Node enter(final FunctionNode functionNode) {
+        if (functionNode.isLazy()) {
+            return null;
+        }
+
         if (functionNode.testResolved()) {
             return null;
         }
 
-        compileUnit = functionNode.getCompileUnit();
-        assert compileUnit != null;
-
-        method = compileUnit.getClassEmitter().method(functionNode);
+        setCurrentCompileUnit(functionNode.getCompileUnit());
+        assert getCurrentCompileUnit() != null;
+
+        method = getCurrentCompileUnit().getClassEmitter().method(functionNode);
         functionNode.setMethodEmitter(method);
         // Mark end for variable tables.
         method.begin();
@@ -1100,18 +1106,18 @@
         final Type elementType = arrayType.getElementType();
 
         if (units != null) {
-            final CompileUnit   savedCompileUnit = compileUnit;
-            final MethodEmitter savedMethod      = method;
+            final CompileUnit   savedCompileUnit = getCurrentCompileUnit();
+            final MethodEmitter savedMethod      = getCurrentMethodEmitter();
 
             try {
                 for (final ArrayUnit unit : units) {
-                    compileUnit = unit.getCompileUnit();
-
-                    final String className = compileUnit.getUnitClassName();
-                    final String name      = compiler.uniqueName(SPLIT_PREFIX.tag());
+                    setCurrentCompileUnit(unit.getCompileUnit());
+
+                    final String className = getCurrentCompileUnit().getUnitClassName();
+                    final String name      = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag());
                     final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
 
-                    method = compileUnit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
+                    method = getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
                     method.setFunctionNode(getCurrentFunctionNode());
                     method.begin();
 
@@ -1135,8 +1141,8 @@
                     savedMethod.invokeStatic(className, name, signature);
                 }
             } finally {
-                compileUnit = savedCompileUnit;
-                method      = savedMethod;
+                setCurrentCompileUnit(savedCompileUnit);
+                setCurrentMethodEmitter(savedMethod);
             }
 
             return method;
@@ -1186,8 +1192,8 @@
      * @param string string to load
      */
     public void loadConstant(final String string) {
-        final String       unitClassName = compileUnit.getUnitClassName();
-        final ClassEmitter classEmitter  = compileUnit.getClassEmitter();
+        final String       unitClassName = getCurrentCompileUnit().getUnitClassName();
+        final ClassEmitter classEmitter  = getCurrentCompileUnit().getClassEmitter();
         final int          index         = compiler.getConstantData().add(string);
 
         method.load(index);
@@ -1202,8 +1208,8 @@
      * @param object object to load
      */
     public void loadConstant(final Object object) {
-        final String       unitClassName = compileUnit.getUnitClassName();
-        final ClassEmitter classEmitter  = compileUnit.getClassEmitter();
+        final String       unitClassName = getCurrentCompileUnit().getUnitClassName();
+        final ClassEmitter classEmitter  = getCurrentCompileUnit().getClassEmitter();
         final int          index         = compiler.getConstantData().add(object);
         final Class<?>     cls           = object.getClass();
 
@@ -1272,14 +1278,14 @@
             return loadRegexToken(regexToken);
         }
         // emit field
-        final String       regexName    = compiler.uniqueName(REGEX_PREFIX.tag());
-        final ClassEmitter classEmitter = compileUnit.getClassEmitter();
+        final String       regexName    = getCurrentFunctionNode().uniqueName(REGEX_PREFIX.tag());
+        final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
 
         classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
         regexFieldCount++;
 
         // get field, if null create new regex, finally clone regex object
-        method.getStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class));
+        method.getStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
         method.dup();
         final Label cachedLabel = new Label("cached");
         method.ifnonnull(cachedLabel);
@@ -1287,7 +1293,7 @@
         method.pop();
         loadRegexToken(regexToken);
         method.dup();
-        method.putStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class));
+        method.putStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
 
         method.label(cachedLabel);
         globalRegExpCopy();
@@ -1409,7 +1415,7 @@
             return null;
         }
 
-        new FunctionObjectCreator(this, referenceNode.getReference()).makeObject(method);
+        newFunctionObject(referenceNode.getReference());
 
         return null;
     }
@@ -1551,6 +1557,10 @@
         return true;
     }
 
+    private static boolean isReducible(final Request request) {
+        return Request.isComparison(request) || request == Request.ADD;
+    }
+
     @Override
     public Node enter(final RuntimeNode runtimeNode) {
         if (runtimeNode.testResolved()) {
@@ -1563,9 +1573,10 @@
          *
          * TODO - remove this - Access Specializer will always know after Attr/Lower
          */
-        if (runtimeNode.isPrimitive() && !runtimeNode.isFinal()) {
+        if (runtimeNode.isPrimitive() && !runtimeNode.isFinal() && isReducible(runtimeNode.getRequest())) {
             final Node lhs = runtimeNode.getArgs().get(0);
-            final Node rhs = runtimeNode.getArgs().size() > 1 ? runtimeNode.getArgs().get(1) : null;
+            assert runtimeNode.getArgs().size() > 1 : runtimeNode + " must have two args";
+            final Node rhs = runtimeNode.getArgs().get(1);
 
             final Type   type   = runtimeNode.getType();
             final Symbol symbol = runtimeNode.getSymbol();
@@ -1709,7 +1720,7 @@
             method.end();
         } catch (final Throwable t) {
             Context.printStackTrace(t);
-            final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + compiler.getSource().getName());
+            final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentFunctionNode().getSource().getName());
             e.initCause(t);
             throw e;
         }
@@ -1898,7 +1909,7 @@
         method._new(ECMAException.class).dup();
 
         final Node   expression = throwNode.getExpression();
-        final Source source     = compiler.getSource();
+        final Source source     = throwNode.getSource();
         final int    position   = throwNode.position();
         final int    line       = source.getLine(position);
         final int    column     = source.getColumn(position);
@@ -2928,7 +2939,7 @@
         if (scopeCalls.containsKey(scopeCall)) {
             return scopeCalls.get(scopeCall);
         }
-        scopeCall.setClassAndName(compileUnit, compiler);
+        scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
         scopeCalls.put(scopeCall, scopeCall);
         return scopeCall;
     }
@@ -2947,7 +2958,7 @@
         if (scopeCalls.containsKey(scopeCall)) {
             return scopeCalls.get(scopeCall);
         }
-        scopeCall.setClassAndName(compileUnit, compiler);
+        scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
         scopeCalls.put(scopeCall, scopeCall);
         return scopeCall;
     }
@@ -2959,12 +2970,12 @@
      * @param ident identifier for block or function where applicable
      */
     private void printSymbols(final Block block, final String ident) {
-        if (!context._print_symbols) {
+        if (!compiler.getContext()._print_symbols) {
             return;
         }
 
         @SuppressWarnings("resource")
-        final PrintWriter out = context.getErr();
+        final PrintWriter out = compiler.getContext().getErr();
         out.println("[BLOCK in '" + ident + "']");
         if (!block.printSymbols(out)) {
             out.println("<no symbols>");
@@ -3057,7 +3068,7 @@
              * on the stack throughout the store and used at the end to execute it
              */
 
-            target.accept(new NodeVisitor(compileUnit, method) {
+            target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
                 @Override
                 public Node enter(final IdentNode node) {
                     if (targetSymbol.isScope()) {
@@ -3124,7 +3135,7 @@
          * @return the quick symbol
          */
         private Symbol quickSymbol(final Type type, final String prefix) {
-            final String name = compiler.uniqueName(prefix);
+            final String name = getCurrentFunctionNode().uniqueName(prefix);
             final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null);
 
             symbol.setType(type);
@@ -3166,7 +3177,7 @@
              */
             method.convert(target.getType());
 
-            target.accept(new NodeVisitor(compileUnit, method) {
+            target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
                 @Override
                 protected Node enterDefault(Node node) {
                     throw new AssertionError("Unexpected node " + node + " in store epilogue");
@@ -3228,42 +3239,83 @@
 
     }
 
+    private void newFunctionObject(final FunctionNode functionNode) {
+        final boolean isLazy = functionNode.isLazy();
+        final Class<?>[] cparams = new Class<?>[] { ScriptFunctionData.class, ScriptObject.class, MethodHandle.class };
+
+        new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
+            @Override
+            public void makeObject(final MethodEmitter method) {
+                final String className = isLazy ? SCRIPTFUNCTION_TRAMPOLINE_OBJECT : SCRIPTFUNCTION_IMPL_OBJECT;
+
+                method._new(className).dup();
+                if (isLazy) {
+                    loadConstant(compiler.getCodeInstaller());
+                    loadConstant(functionNode);
+                } else {
+                    final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
+                    method.loadHandle(functionNode.getCompileUnit().getUnitClassName(), functionNode.getName(), signature, EnumSet.of(HANDLE_STATIC)); // function
+                }
+                loadConstant(new ScriptFunctionData(functionNode, makeMap()));
+
+                if (isLazy || functionNode.needsParentScope()) {
+                    method.loadScope();
+                } else {
+                    method.loadNull();
+                }
+
+                method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
+
+                final List<Class<?>> cparamList = new ArrayList<>();
+                if (isLazy) {
+                    cparamList.add(CodeInstaller.class);
+                    cparamList.add(FunctionNode.class);
+                } else {
+                    cparamList.add(MethodHandle.class);
+                }
+                cparamList.addAll(Arrays.asList(cparams));
+
+                method.invoke(constructorNoLookup(className, cparamList.toArray(new Class<?>[cparamList.size()])));
+            }
+        }.makeObject(method);
+    }
+
     /*
      * Globals are special. We cannot refer to any Global (or NativeObject) class by .class, as they are different
      * for different contexts. As far as I can tell, the only NativeObject that we need to deal with like this
      * is from the code pipeline is Global
      */
     private MethodEmitter globalInstance() {
-        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "instance", "()L" + Compiler.GLOBAL_OBJECT + ';');
+        return method.invokeStatic(GLOBAL_OBJECT, "instance", "()L" + GLOBAL_OBJECT + ';');
     }
 
     private MethodEmitter globalObjectPrototype() {
-        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "objectPrototype", methodDescriptor(ScriptObject.class));
+        return method.invokeStatic(GLOBAL_OBJECT, "objectPrototype", methodDescriptor(ScriptObject.class));
     }
 
     private MethodEmitter globalAllocateArguments() {
-        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocateArguments", methodDescriptor(ScriptObject.class, Object[].class, Object.class, int.class));
+        return method.invokeStatic(GLOBAL_OBJECT, "allocateArguments", methodDescriptor(ScriptObject.class, Object[].class, Object.class, int.class));
     }
 
     private MethodEmitter globalNewRegExp() {
-        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "newRegExp", methodDescriptor(Object.class, String.class, String.class));
+        return method.invokeStatic(GLOBAL_OBJECT, "newRegExp", methodDescriptor(Object.class, String.class, String.class));
     }
 
     private MethodEmitter globalRegExpCopy() {
-        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "regExpCopy", methodDescriptor(Object.class, Object.class));
+        return method.invokeStatic(GLOBAL_OBJECT, "regExpCopy", methodDescriptor(Object.class, Object.class));
     }
 
     private MethodEmitter globalAllocateArray(final ArrayType type) {
         //make sure the native array is treated as an array type
-        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocate", "(" + type.getDescriptor() + ")Ljdk/nashorn/internal/objects/NativeArray;");
+        return method.invokeStatic(GLOBAL_OBJECT, "allocate", "(" + type.getDescriptor() + ")Ljdk/nashorn/internal/objects/NativeArray;");
     }
 
     private MethodEmitter globalIsEval() {
-        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class));
+        return method.invokeStatic(GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class));
     }
 
     private MethodEmitter globalDirectEval() {
-        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "directEval",
+        return method.invokeStatic(GLOBAL_OBJECT, "directEval",
                 methodDescriptor(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class));
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Thu Feb 14 13:01:52 2013 +0100
@@ -0,0 +1,366 @@
+package jdk.nashorn.internal.codegen;
+
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.ATTR;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.EMITTED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.FINALIZED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.EnumSet;
+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.Node;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.ir.debug.ASTWriter;
+import jdk.nashorn.internal.ir.debug.PrintVisitor;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ECMAErrors;
+
+/**
+ * A compilation phase is a step in the processes of turning a JavaScript FunctionNode
+ * into bytecode. It has an optional return value.
+ */
+enum CompilationPhase {
+
+    /*
+     * Lazy initialization - tag all function nodes not the script as lazy as
+     * default policy. The will get trampolines and only be generated when
+     * called
+     */
+    LAZY_INITIALIZATION_PHASE(EnumSet.of(FunctionNode.CompilationState.INITIALIZED)) {
+        @Override
+        boolean transform(final Compiler compiler, final FunctionNode fn) {
+
+            /*
+             * For lazy compilation, we might be given a node previously marked as lazy
+             * to compile as the outermost function node in the compiler. Unmark it
+             * so it can be compiled and not cause recursion. Make sure the return type
+             * is unknown so it can be correctly deduced. Return types are always
+             * Objects in Lazy nodes as we haven't got a change to generate code for
+             * them and decude its parameter specialization
+             *
+             * TODO: in the future specializations from a callsite will be passed here
+             * so we can generate a better non-lazy version of a function from a trampoline
+             */
+            //compute the signature from the callsite - todo - now just clone object params
+            final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
+            outermostFunctionNode.setIsLazy(false);
+            outermostFunctionNode.setReturnType(Type.UNKNOWN);
+
+            outermostFunctionNode.accept(new NodeVisitor() {
+                @Override
+                public Node enter(final FunctionNode node) {
+                    assert Compiler.LAZY_JIT;
+                    node.setIsLazy(node != outermostFunctionNode);
+                    return node;
+                }
+            });
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "[Lazy JIT Initialization]";
+        }
+    },
+
+    /*
+     * Constant folding pass
+     *   Simple constant folding that will make elementary constructs go away
+     */
+    CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED), CONSTANT_FOLDED) {
+        @Override
+        boolean transform(final Compiler compiler, final FunctionNode fn) {
+            final Context context = compiler.getContext();
+
+            if (context._print_ast) {
+                context.getErr().println(new ASTWriter(fn));
+            }
+
+            if (context._print_parse) {
+                context.getErr().println(new PrintVisitor(fn));
+            }
+
+            fn.accept(new FoldConstants());
+
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "[Constant Folding]";
+        }
+    },
+
+    /*
+     * Lower (Control flow pass)
+     *   Finalizes the control flow. Clones blocks for finally constructs and
+     *   similar things. Establishes termination criteria for nodes
+     *   Guarantee return instructions to method making sure control flow
+     *   cannot fall off the end. Replacing high level nodes with lower such
+     *   as runtime nodes where applicable.
+     *
+     */
+    LOWERING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED), LOWERED) {
+        @Override
+        boolean transform(final Compiler compiler, final FunctionNode fn) {
+            fn.accept(new Lower());
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "[Control Flow Lowering]";
+        }
+    },
+
+    /*
+     * Attribution
+     *   Assign symbols and types to all nodes.
+     */
+    ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED), ATTR) {
+        @Override
+        boolean transform(final Compiler compiler, final FunctionNode fn) {
+            final Context context = compiler.getContext();
+            try {
+                fn.accept(new Attr(context));
+                return true;
+            } finally {
+                if (context._print_lower_ast) {
+                    context.getErr().println(new ASTWriter(fn));
+                }
+
+                if (context._print_lower_parse) {
+                    context.getErr().println(new PrintVisitor(fn));
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "[Type Attribution]";
+        }
+    },
+
+    /*
+     * Splitter
+     *   Split the AST into several compile units based on a size heuristic
+     *   Splitter needs attributed AST for weight calculations (e.g. is
+     *   a + b a ScriptRuntime.ADD with call overhead or a dadd with much
+     *   less). Split IR can lead to scope information being changed.
+     */
+    SPLITTING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR), SPLIT) {
+        @Override
+        boolean transform(final Compiler compiler, final FunctionNode fn) {
+            final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
+
+            new Splitter(compiler, fn, outermostCompileUnit).split();
+
+            assert fn.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + fn.getCompileUnit() + ") != " + outermostCompileUnit;
+
+            if (fn.isStrictMode()) {
+                assert compiler.getStrictMode();
+                compiler.setStrictMode(true);
+            }
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "[Code Splitting]";
+        }
+    },
+
+    /*
+     * FinalizeTypes
+     *
+     *   This pass finalizes the types for nodes. If Attr created wider types than
+     *   known during the first pass, convert nodes are inserted or access nodes
+     *   are specialized where scope accesses.
+     *
+     *   Runtime nodes may be removed and primitivized or reintroduced depending
+     *   on information that was established in Attr.
+     *
+     * Contract: all variables must have slot assignments and scope assignments
+     * before type finalization.
+     */
+    TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT), FINALIZED) {
+        @Override
+        boolean transform(final Compiler compiler, final FunctionNode fn) {
+            fn.accept(new FinalizeTypes());
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "[Type Finalization]";
+        }
+    },
+
+    /*
+     * Bytecode generation:
+     *
+     *   Generate the byte code class(es) resulting from the compiled FunctionNode
+     */
+    BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED), EMITTED) {
+        @Override
+        boolean transform(final Compiler compiler, final FunctionNode fn) {
+            final Context context = compiler.getContext();
+
+            try {
+                final CodeGenerator codegen = new CodeGenerator(compiler);
+                fn.accept(codegen);
+                codegen.generateScopeCalls();
+
+            } catch (final VerifyError e) {
+                if (context._verify_code || context._print_code) {
+                    context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
+                    if (context._dump_on_error) {
+                        e.printStackTrace(context.getErr());
+                    }
+                } else {
+                    throw e;
+                }
+            }
+
+            for (final CompileUnit compileUnit : compiler.getCompileUnits()) {
+                final ClassEmitter classEmitter = compileUnit.getClassEmitter();
+                classEmitter.end();
+
+                final byte[] bytecode = classEmitter.toByteArray();
+                assert bytecode != null;
+
+                final String className = compileUnit.getUnitClassName();
+
+                compiler.addClass(className, bytecode);
+
+                //should could be printed to stderr for generate class?
+                if (context._print_code) {
+                    final StringBuilder sb = new StringBuilder();
+                    sb.append("class: " + className).
+                        append('\n').
+                        append(ClassEmitter.disassemble(bytecode)).
+                        append("=====");
+                    context.getErr().println(sb);
+                }
+
+                //should we verify the generated code?
+                if (context._verify_code) {
+                    context.verify(bytecode);
+                }
+
+                //should code be dumped to disk - only valid in compile_only mode?
+                if (context._dest_dir != null && context._compile_only) {
+                    final String fileName = className.replace('.', File.separatorChar) + ".class";
+                    final int    index    = fileName.lastIndexOf(File.separatorChar);
+
+                    if (index != -1) {
+                        final File dir = new File(fileName.substring(0, index));
+                        try {
+                            if (!dir.exists() && !dir.mkdirs()) {
+                                throw new IOException();
+                            }
+                            final File file = new File(context._dest_dir, fileName);
+                            try (final FileOutputStream fos = new FileOutputStream(file)) {
+                                fos.write(bytecode);
+                            }
+                        } catch (final IOException e) {
+                            Compiler.LOG.warning("Skipping class dump for " + className + ": " + ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
+                        }
+                    }
+                }
+            }
+
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "[Bytecode Generation]";
+        }
+    };
+
+    private final EnumSet<CompilationState> pre;
+    private final CompilationState post;
+    private long startTime;
+    private long endTime;
+    private boolean isFinished;
+
+    private static final long[] accumulatedTime = new long[CompilationPhase.values().length];
+
+    private CompilationPhase(final EnumSet<CompilationState> pre) {
+        this(pre, null);
+    }
+
+    private CompilationPhase(final EnumSet<CompilationState> pre, final CompilationState post) {
+        this.pre  = pre;
+        this.post = post;
+    }
+
+    boolean isApplicable(final FunctionNode functionNode) {
+        return functionNode.hasState(pre);
+    }
+
+    protected void 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));
+            }
+        }
+
+        startTime = System.currentTimeMillis();
+    }
+
+    protected void end(final FunctionNode functionNode) {
+        endTime = System.currentTimeMillis();
+        accumulatedTime[ordinal()] += (endTime - startTime);
+
+        if (post != null) {
+            functionNode.setState(post);
+        }
+
+        isFinished = true;
+    }
+
+    boolean isFinished() {
+        return isFinished;
+    }
+
+    long getStartTime() {
+        return startTime;
+    }
+
+    long getEndTime() {
+        return endTime;
+    }
+
+    public static long getAccumulatedTime(final CompilationPhase phase) {
+        return accumulatedTime[phase.ordinal()];
+    }
+
+    abstract boolean transform(final Compiler compiler, final FunctionNode functionNode);
+
+    final boolean apply(final Compiler compiler, final FunctionNode functionNode) {
+        try {
+            if (!isApplicable(functionNode)) {
+                return false;
+            }
+            begin(functionNode);
+            transform(compiler, functionNode);
+            return true;
+        } finally {
+            end(functionNode);
+        }
+    }
+
+}
--- a/src/jdk/nashorn/internal/codegen/CompileUnit.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java	Thu Feb 14 13:01:52 2013 +0100
@@ -51,7 +51,7 @@
      * Add weight to this compile unit
      * @param w weight to add
      */
-    public void addWeight(final long w) {
+    void addWeight(final long w) {
         this.weight += w;
     }
 
@@ -59,7 +59,7 @@
      * Get the current weight of the compile unit.
      * @return the unit's weight
      */
-    public long getWeight() {
+    long getWeight() {
         return weight;
     }
 
--- a/src/jdk/nashorn/internal/codegen/Compiler.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Thu Feb 14 13:01:52 2013 +0100
@@ -27,124 +27,377 @@
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
-import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
+import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
 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 java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
+import java.util.Arrays;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.TreeMap;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.Node;
-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.parser.Parser;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.DebugLogger;
-import jdk.nashorn.internal.runtime.ECMAErrors;
-import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.options.Options;
 import org.dynalang.dynalink.support.NameCodec;
 
 /**
  * Responsible for converting JavaScripts to java byte code. Main entry
- * point for code generator
+ * point for code generator. The compiler may also install classes given some
+ * predefined Code installation policy, given to it at construction time.
+ * @see CodeInstaller
  */
 public final class Compiler {
 
-    /** Compiler states available */
-    public enum State {
-        /** compiler is ready */
-        INITIALIZED,
-        /** method has been parsed */
-        PARSED,
-        /** constant folding pass */
-        CONSTANT_FOLDED,
-        /** method has been lowered */
-        LOWERED,
-        /** method hass been attributed */
-        ATTR,
-        /** method has been split */
-        SPLIT,
-        /** method has had its types finalized */
-        FINALIZED,
-        /** method has been emitted to bytecode */
-        EMITTED
-    }
-
-    /** Current context */
-    private final Context context;
-
-    /** Currently compiled source */
-    private final Source source;
-
-    /** Current error manager */
-    private final ErrorManager errors;
-
-    /** Names uniqueName for this compile. */
-    private final Namespace namespace;
-
-    /** Current function node, or null if compiling from source until parsed */
-    private FunctionNode functionNode;
-
-    /** Current compiler state */
-    private final EnumSet<State> state;
-
     /** Name of the scripts package */
     public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts";
 
     /** Name of the objects package */
     public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
 
-    /** Name of the runtime package */
-    public static final String RUNTIME_PACKAGE = "jdk/nashorn/internal/runtime";
-
-    /** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */
-    public static final String GLOBAL_OBJECT = OBJECTS_PACKAGE + '/' + "Global";
+    static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy");
 
-    /** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */
-    public static final String SCRIPTFUNCTION_IMPL_OBJECT = OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl";
+    static final boolean TIME_COMPILATION = Options.getBooleanProperty("nashorn.compiler.time");
 
-    /** Name of the Trampoline, cannot be referred to as .class @see FunctionObjectCreator */
-    public static final String TRAMPOLINE_OBJECT = OBJECTS_PACKAGE + '/' + "Trampoline";
+    private final Map<String, byte[]> bytecode;
 
-    /** Compile unit (class) table. */
     private final Set<CompileUnit> compileUnits;
 
-    /** All the "complex" constants used in the code. */
     private final ConstantData constantData;
 
+    private final FunctionNode functionNode;
+
+    private final CompilationSequence sequence;
+
+    private final Context context;
+
+    private final String scriptName;
+
+    private boolean strict;
+
+    private CodeInstaller<Context> installer;
+
     static final DebugLogger LOG = new DebugLogger("compiler");
 
-    /** Script name */
-    private String scriptName;
+    /**
+     * 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
+     * during a compile.
+     */
+    private static String[] RESERVED_NAMES = {
+        SCOPE.tag(),
+        THIS.tag()
+    };
+
+    /**
+     * This class makes it possible to do your own compilation sequence
+     * from the code generation package. There are predefined compilation
+     * sequences already
+     */
+    @SuppressWarnings("serial")
+    static class CompilationSequence extends LinkedList<CompilationPhase> {
+
+        CompilationSequence(final CompilationPhase... phases) {
+            super(Arrays.asList(phases));
+        }
+
+        CompilationSequence(final CompilationSequence sequence) {
+            this(sequence.toArray(new CompilationPhase[sequence.size()]));
+        }
+
+        CompilationSequence insertAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
+            final CompilationSequence newSeq = new CompilationSequence();
+            for (final CompilationPhase elem : this) {
+                newSeq.add(phase);
+                if (elem.equals(phase)) {
+                    newSeq.add(newPhase);
+                }
+            }
+            assert newSeq.contains(newPhase);
+            return newSeq;
+        }
+
+        CompilationSequence insertBefore(final CompilationPhase phase, final CompilationPhase newPhase) {
+            final CompilationSequence newSeq = new CompilationSequence();
+            for (final CompilationPhase elem : this) {
+                if (elem.equals(phase)) {
+                    newSeq.add(newPhase);
+                }
+                newSeq.add(phase);
+            }
+            assert newSeq.contains(newPhase);
+            return newSeq;
+        }
+
+        CompilationSequence insertFirst(final CompilationPhase phase) {
+            final CompilationSequence newSeq = new CompilationSequence(this);
+            newSeq.addFirst(phase);
+            return newSeq;
+        }
+
+        CompilationSequence insertLast(final CompilationPhase phase) {
+            final CompilationSequence newSeq = new CompilationSequence(this);
+            newSeq.addLast(phase);
+            return newSeq;
+        }
+    }
+
+    /**
+     * 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 CompilationSequence SEQUENCE_NORMAL = new CompilationSequence(
+        CompilationPhase.CONSTANT_FOLDING_PHASE,
+        CompilationPhase.LOWERING_PHASE,
+        CompilationPhase.ATTRIBUTION_PHASE,
+        CompilationPhase.SPLITTING_PHASE,
+        CompilationPhase.TYPE_FINALIZATION_PHASE,
+        CompilationPhase.BYTECODE_GENERATION_PHASE);
 
-    /** Should we dump classes to disk and compile only? */
-    private final boolean dumpClass;
+    final static CompilationSequence SEQUENCE_LAZY =
+        SEQUENCE_NORMAL.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
+
+    final static CompilationSequence SEQUENCE_DEFAULT =
+        LAZY_JIT ?
+            SEQUENCE_LAZY :
+            SEQUENCE_NORMAL;
+
+    /**
+     * Constructor
+     *
+     * @param installer    code installer from
+     * @param functionNode function node (in any available {@link CompilationState}) to compile
+     * @param sequence     {@link Compiler#CompilationSequence} of {@link CompilationPhase}s to apply as this compilation
+     * @param strict       should this compilation use strict mode semantics
+     */
+    Compiler(final Context context, final CodeInstaller<Context> installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) {
+        this.context       = context;
+        this.functionNode  = functionNode;
+        this.sequence      = sequence;
+        this.installer     = installer;
+        this.strict        = strict || functionNode.isStrictMode();
+        this.constantData  = new ConstantData();
+        this.compileUnits  = new HashSet<>();
+        this.bytecode      = new HashMap<>();
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag())).
+                append('$').
+                append(safeSourceName(functionNode.getSource())).
+                append(functionNode.isLazy() ? LAZY.tag() : "");
+
+        this.scriptName = sb.toString();
+
+        LOG.info("Initializing compiler for scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'");
+    }
 
-    /** Code map class name -> byte code for all classes generated from this Source or FunctionNode */
-    private Map<String, byte[]> code;
+    /**
+     * Constructor
+     *
+     * @param installer    code installer from context
+     * @param functionNode function node (in any available {@link CompilationState}) to compile
+     * @param strict       should this compilation use strict mode semantics
+     */
+    public Compiler(final CodeInstaller<Context> installer, final FunctionNode functionNode, final boolean strict) {
+        this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, strict);
+    }
+
+    /**
+     * Constructor - compilation will use the same strict semantics as context
+     *
+     * @param installer    code installer from context
+     * @param functionNode function node (in any available {@link CompilationState}) to compile
+     */
+    public Compiler(final CodeInstaller<Context> installer, final FunctionNode functionNode) {
+        this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, installer.getOwner()._strict);
+    }
+
+    /**
+     * Constructor - compilation needs no installer, but uses a context
+     * Used in "compile only" scenarios
+     * @param context a context
+     * @param functionNode functionNode to compile
+     */
+    public Compiler(final Context context, final FunctionNode functionNode) {
+        this(context, null, functionNode, SEQUENCE_DEFAULT, context._strict);
+    }
+
+    /**
+     * Execute the compilation this Compiler was created with
+     * @return true if compilation succeeds.
+     */
+    public boolean compile() {
+        for (final String reservedName : RESERVED_NAMES) {
+            functionNode.uniqueName(reservedName);
+        }
 
-    /** Are we compiling in strict mode? */
-    private boolean strict;
+        for (final CompilationPhase phase : sequence) {
+            LOG.info("Entering compile phase " + phase + " for function '" + functionNode.getName() + "'");
+            if (phase.isApplicable(functionNode)) {
+                if (!phase.apply(this, functionNode)) { //TODO exceptions, error logging
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Install compiled classes into a given loader
+     * @return root script class - if there are several compile units they will also be installed
+     */
+    public Class<?> install() {
+        Class<?> rootClass = null;
+
+        for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
+            final String     className = entry.getKey();
+            LOG.info("Installing class " + className);
+
+            final byte[]     code  = entry.getValue();
+            final Class<?>   clazz = installer.install(Compiler.binaryName(className), code);
+
+            if (rootClass == null && firstCompileUnitName().equals(className)) {
+                rootClass = clazz;
+            }
+
+            try {
+                //use reflection to write source and constants table to installed classes
+                clazz.getField(SOURCE.tag()).set(null, getSource());
+                clazz.getField(CONSTANTS.tag()).set(null, getConstantData().toArray());
+            } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        LOG.info("Root class: " + rootClass);
+        return rootClass;
+    }
+
+    Set<CompileUnit> getCompileUnits() {
+        return compileUnits;
+    }
+
+    boolean getStrictMode() {
+        return strict;
+    }
+
+    void setStrictMode(final boolean strict) {
+        this.strict = strict;
+    }
+
+    FunctionNode getFunctionNode() {
+        return functionNode;
+    }
+
+    ConstantData getConstantData() {
+        return constantData;
+    }
+
+    CodeInstaller<Context> getCodeInstaller() {
+        return installer;
+    }
+
+    Source getSource() {
+        return functionNode.getSource();
+    }
+
+    void addClass(final String name, final byte[] code) {
+        bytecode.put(name, code);
+    }
+
+    Context getContext() {
+        return this.context;
+    }
 
-    /** Is this a lazy compilation - i.e. not from source, but jitting a previously parsed FunctionNode? */
-    private boolean isLazy;
+    private static String safeSourceName(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('-', '_');
+        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);
+    }
 
-    /** Lazy jitting is disabled by default */
-    private static final boolean LAZY_JIT = false;
+    private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
+        final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
+        compileUnits.add(compileUnit);
+        LOG.info("Added compile unit " + compileUnit);
+        return compileUnit;
+    }
+
+    private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
+        final ClassEmitter classEmitter = new ClassEmitter(context, functionNode.getSource().getName(), unitClassName, strict);
+        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('/', '.');
+    }
 
     /**
      * Should we use integers for arithmetic operations as well?
@@ -164,573 +417,27 @@
 
     static {
         USE_INT_ARITH  =  Options.getBooleanProperty("nashorn.compiler.intarithmetic");
-
         assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
     }
 
-    /**
-     * Factory method for compiler that should compile from source to bytecode
-     *
-     * @param source  the source
-     * @param context context
-     *
-     * @return compiler instance
-     */
-    public static Compiler compiler(final Source source, final Context context) {
-        return Compiler.compiler(source, context, context.getErrorManager(), context._strict);
-    }
-
-    /**
-     * Factory method to get a compiler that goes from from source to bytecode
-     *
-     * @param source  source code
-     * @param context context
-     * @param errors  error manager
-     * @param strict  compilation in strict mode?
-     *
-     * @return compiler instance
-     */
-    public static Compiler compiler(final Source source, final Context context, final ErrorManager errors, final boolean strict) {
-        return new Compiler(source, context, errors, strict);
-    }
-
-    /**
-     * Factory method to get a compiler that goes from FunctionNode (parsed) to bytecode
-     * Requires previous compiler for state
-     *
-     * @param compiler primordial compiler
-     * @param functionNode functionNode to compile
-     *
-     * @return compiler
-     */
-    public static Compiler compiler(final Compiler compiler, final FunctionNode functionNode) {
-        assert false : "lazy jit - not implemented";
-        final Compiler newCompiler = new Compiler(compiler);
-        newCompiler.state.add(State.PARSED);
-        newCompiler.functionNode = functionNode;
-        newCompiler.isLazy = true;
-        return compiler;
-    }
-
-    private Compiler(final Compiler compiler) {
-        this(compiler.source, compiler.context, compiler.errors, compiler.strict);
-    }
-
-    /**
-     * Constructor
-     *
-     * @param source  the source to compile
-     * @param context context
-     * @param errors  error manager
-     * @param strict  compile in strict mode
-     */
-    private Compiler(final Source source, final Context context, final ErrorManager errors, final boolean strict) {
-        this.source       = source;
-        this.context      = context;
-        this.errors       = errors;
-        this.strict       = strict;
-        this.namespace    = new Namespace(context.getNamespace());
-        this.compileUnits = new HashSet<>();
-        this.constantData = new ConstantData();
-        this.state        = EnumSet.of(State.INITIALIZED);
-        this.dumpClass    = context._compile_only && context._dest_dir != null;
-    }
-
-    private String scriptsPackageName() {
-        return dumpClass ? "" : (SCRIPTS_PACKAGE + '/');
-    }
-
-    private int nextCompileUnitIndex() {
-        return compileUnits.size() + 1;
-    }
-
-    private String firstCompileUnitName() {
-        return scriptsPackageName() + scriptName;
-    }
-
-    private String nextCompileUnitName() {
-        return firstCompileUnitName() + '$' + nextCompileUnitIndex();
-    }
-
-    private CompileUnit addCompileUnit(final long initialWeight) {
-        return addCompileUnit(nextCompileUnitName(), initialWeight);
-    }
-
-    private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
-        final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
-        compileUnits.add(compileUnit);
-        LOG.info("Added compile unit " + compileUnit);
-        return compileUnit;
-    }
-
-    private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
-        final ClassEmitter classEmitter = new ClassEmitter(this, unitClassName, strict);
-        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;
-    }
-
-    /**
-     * Perform compilation
-     *
-     * @return true if successful, false otherwise - if false check the error manager
-     */
-    public boolean compile() {
-        assert state.contains(State.INITIALIZED);
-
-        /** do we need to parse source? */
-        if (!state.contains(State.PARSED)) {
-            LOG.info("Parsing '" + source + "'");
-
-            assert this.functionNode == null;
-            this.functionNode = new Parser(this, strict).parse(RUN_SCRIPT.tag());
-
-            state.add(State.PARSED);
-            debugPrintParse();
-
-            if (errors.hasErrors() || context._parse_only) {
-                return false;
-            }
-
-            assert !isLazy;
-            //tag lazy nodes for later code generation and trampolines
-            functionNode.accept(new NodeVisitor() {
+    static {
+        if (TIME_COMPILATION) {
+            Runtime.getRuntime().addShutdownHook(new Thread() {
                 @Override
-                public Node enter(final FunctionNode node) {
-                    if (LAZY_JIT) {
-                        node.setIsLazy(!node.isScript());
+                public void run() {
+                    for (final CompilationPhase phase : CompilationPhase.values()) {
+                        final StringBuilder sb = new StringBuilder();
+                        sb.append(phase);
+                        while (sb.length() < 32) {
+                            sb.append(' ');
+                        }
+                        sb.append(CompilationPhase.getAccumulatedTime(phase));
+                        sb.append(' ');
+                        sb.append(" ms");
+                        System.err.println(sb.toString()); //Context err is gone by shutdown TODO
                     }
-                    return node;
-                }
-            });
-        } else {
-            assert isLazy;
-            functionNode.accept(new NodeVisitor() {
-                @Override
-                public Node enter(final FunctionNode node) {
-                    node.setIsLazy(false);
-                    return null; //TODO do we want to do this recursively? then return "node" instead
                 }
             });
         }
-
-        assert functionNode != null;
-        final boolean oldStrict = strict;
-
-        try {
-            strict |= functionNode.isStrictMode();
-
-            /*
-             * These are the compile phases:
-             *
-             * Constant folding pass
-             *   Simple constant folding that will make elementary constructs go away
-             *
-             * Lower (Control flow pass)
-             *   Finalizes the control flow. Clones blocks for finally constructs and
-             *   similar things. Establishes termination criteria for nodes
-             *   Guarantee return instructions to method making sure control flow
-             *   cannot fall off the end. Replacing high level nodes with lower such
-             *   as runtime nodes where applicable.
-             *
-             * Attr
-             *   Assign symbols and types to all nodes.
-             *
-             * Splitter
-             *   Split the AST into several compile units based on a size heuristic
-             *   Splitter needs attributed AST for weight calculations (e.g. is
-             *   a + b a ScriptRuntime.ADD with call overhead or a dadd with much
-             *   less). Split IR can lead to scope information being changed.
-             *
-             * Contract: all variables must have slot assignments and scope assignments
-             * before lowering.
-             *
-             * FinalizeTypes
-             *   This pass finalizes the types for nodes. If Attr created wider types than
-             *   known during the first pass, convert nodes are inserted or access nodes
-             *   are specialized where scope accesses.
-             *
-             *   Runtime nodes may be removed and primitivized or reintroduced depending
-             *   on information that was established in Attr.
-             *
-             * CodeGeneration
-             *   Emit bytecode
-             *
-             */
-
-            debugPrintAST();
-
-            if (!state.contains(State.FINALIZED)) {
-                LOG.info("Folding constants in '" + functionNode.getName() + "'");
-                functionNode.accept(new FoldConstants());
-                state.add(State.CONSTANT_FOLDED);
-
-                LOG.info("Lowering '" + functionNode.getName() + "'");
-                functionNode.accept(new Lower(this));
-                state.add(State.LOWERED);
-                debugPrintAST();
-
-                LOG.info("Attributing types '" + functionNode.getName() + "'");
-                functionNode.accept(new Attr(this));
-                state.add(State.ATTR);
-
-                this.scriptName = computeNames();
-
-                // Main script code always goes to this compile unit. Note that since we start this with zero weight
-                // and add script code last this class may end up slightly larger than others, but reserving one class
-                // just for the main script seems wasteful.
-                final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0L);
-                LOG.info("Splitting '" + functionNode.getName() + "'");
-                new Splitter(this, functionNode, scriptCompileUnit).split();
-                state.add(State.SPLIT);
-                assert functionNode.getCompileUnit() == scriptCompileUnit;
-
-                assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode();
-                if (functionNode.isStrictMode()) {
-                    strict = true;
-                }
-
-                LOG.info("Finalizing types for '" + functionNode.getName() + "'");
-                functionNode.accept(new FinalizeTypes(this));
-                state.add(State.FINALIZED);
-
-                // print ast and parse if --print-lower-ast and/or --print-lower-parse are selected
-                debugPrintAST();
-                debugPrintParse();
-
-                if (errors.hasErrors()) {
-                    return false;
-                }
-            }
-
-            try {
-                LOG.info("Emitting bytecode for '" + functionNode.getName() + "'");
-                final CodeGenerator codegen = new CodeGenerator(this);
-                functionNode.accept(codegen);
-                codegen.generateScopeCalls();
-            } catch (final VerifyError e) {
-                if (context._verify_code || context._print_code) {
-                    context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
-                    if (context._dump_on_error) {
-                        e.printStackTrace(context.getErr());
-                    }
-                } else {
-                    throw e;
-                }
-            }
-
-            state.add(State.EMITTED);
-
-            code = new TreeMap<>();
-            for (final CompileUnit compileUnit : compileUnits) {
-                final ClassEmitter classEmitter = compileUnit.getClassEmitter();
-                classEmitter.end();
-
-                if (!errors.hasErrors()) {
-                    final byte[] bytecode = classEmitter.toByteArray();
-                    if (bytecode != null) {
-                        code.put(compileUnit.getUnitClassName(), bytecode);
-                        debugDisassemble();
-                        debugVerify();
-                    }
-               }
-            }
-
-            if (code.isEmpty()) {
-                return false;
-            }
-
-            try {
-                dumpClassFiles();
-            } catch (final IOException e) {
-                throw new RuntimeException(e);
-            }
-
-            return true;
-        } finally {
-            strict = oldStrict;
-            LOG.info("Done with '" + functionNode.getName() + "'");
-        }
     }
-
-    /**
-     * Install compiled classes into a given loader
-     * @param installer that takes the generated classes and puts them in the system
-     * @return root script class - if there are several compile units they will also be installed
-     */
-    public Class<?> install(final CodeInstaller installer) {
-        assert state.contains(State.EMITTED);
-        assert scriptName != null;
-
-        Class<?> rootClass = null;
-
-        for (final Entry<String, byte[]> entry : code.entrySet()) {
-            final String     className = entry.getKey();
-            LOG.info("Installing class " + className);
-
-            final byte[]     bytecode  = entry.getValue();
-            final Class<?>   clazz     = installer.install(Compiler.binaryName(className), bytecode);
-
-            if (rootClass == null && firstCompileUnitName().equals(className)) {
-                rootClass = clazz;
-            }
-
-            try {
-                //use reflection to write source and constants table to installed classes
-                clazz.getField(SOURCE.tag()).set(null, source);
-                clazz.getField(CONSTANTS.tag()).set(null, constantData.toArray());
-            } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        LOG.info("Root class: " + rootClass);
-
-        return rootClass;
-    }
-
-    /**
-     * Find a unit that will hold a node of the specified weight.
-     *
-     * @param weight Weight of a node
-     * @return Unit to hold node.
-     */
-    CompileUnit findUnit(final long weight) {
-        for (final CompileUnit unit : compileUnits) {
-            if (unit.canHold(weight)) {
-                unit.addWeight(weight);
-                return unit;
-            }
-        }
-
-        return addCompileUnit(weight);
-    }
-
-    /**
-     * Generate a uniqueName name. Public as {@link Parser} is using this to
-     * create symbols in a different package
-     *
-     * @param  name to base unique name on
-     * @return unique name
-     */
-    public String uniqueName(final String name) {
-        return namespace.uniqueName(name);
-    }
-
-    /**
-     * Internal function to compute reserved names and base names for class to
-     * be generated
-     *
-     * @return scriptName
-     */
-    private String computeNames() {
-        // Reserve internally used names.
-        addReservedNames();
-
-        if (dumpClass) {
-            // get source file name and remove ".js"
-            final String baseName = getSource().getName();
-            final int    index    = baseName.lastIndexOf(".js");
-            if (index != -1) {
-                return baseName.substring(0, index);
-            }
-            return baseName;
-        }
-
-        return namespace.getParent().uniqueName(
-            DEFAULT_SCRIPT_NAME.tag() +
-            '$' +
-            safeSourceName(source) +
-            (isLazy ? CompilerConstants.LAZY.tag() : "")
-        );
-    }
-
-    private static String safeSourceName(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('-', '_');
-        final String mangled = NameCodec.encode(baseName);
-
-        baseName = mangled != null ? mangled : baseName;
-        return baseName;
-    }
-
-    static void verify(final Context context, final byte[] code) {
-        context.verify(code);
-    }
-
-    /**
-     * Fill in the namespace with internally reserved names.
-     */
-    private void addReservedNames() {
-        namespace.uniqueName(SCOPE.tag());
-        namespace.uniqueName(THIS.tag());
-    }
-
-    /**
-     * Get the constant data for this Compiler
-     *
-     * @return the constant data
-     */
-    public ConstantData getConstantData() {
-        return constantData;
-    }
-
-    /**
-     * Get the Context used for Compilation
-     * @see Context
-     * @return the context
-     */
-    public Context getContext() {
-        return context;
-    }
-
-    /**
-     * Get the Source being compiled
-     * @see Source
-     * @return the source
-     */
-    public Source getSource() {
-        return source;
-    }
-
-    /**
-     * Get the error manager used for this compiler
-     * @return the error manager
-     */
-    public ErrorManager getErrors() {
-        return errors;
-    }
-
-    /**
-     * Get the namespace used for this Compiler
-     * @see Namespace
-     * @return the namespace
-     */
-    public Namespace getNamespace() {
-        return namespace;
-    }
-
-    /*
-     * Debugging
-     */
-
-    /**
-     * Print the AST before or after lowering, see --print-ast, --print-lower-ast
-     */
-    private void debugPrintAST() {
-        assert functionNode != null;
-        if (context._print_lower_ast && state.contains(State.LOWERED) ||
-            context._print_ast && !state.contains(State.LOWERED)) {
-            context.getErr().println(new ASTWriter(functionNode));
-        }
-    }
-
-    /**
-     * Print the parsed code before or after lowering, see --print-parse, --print-lower-parse
-     */
-    private boolean debugPrintParse() {
-        if (errors.hasErrors()) {
-            return false;
-        }
-
-        assert functionNode != null;
-
-        if (context._print_lower_parse && state.contains(State.LOWERED) ||
-             context._print_parse && !state.contains(State.LOWERED)) {
-            final PrintVisitor pv = new PrintVisitor();
-            functionNode.accept(pv);
-            context.getErr().print(pv);
-            context.getErr().flush();
-        }
-
-        return true;
-    }
-
-    private void debugDisassemble() {
-        assert code != null;
-        if (context._print_code) {
-            for (final Map.Entry<String, byte[]> entry : code.entrySet()) {
-                context.getErr().println("CLASS: " + entry.getKey());
-                context.getErr().println();
-                ClassEmitter.disassemble(context, entry.getValue());
-                context.getErr().println("======");
-            }
-        }
-    }
-
-    private void debugVerify() {
-        if (context._verify_code) {
-            for (final Map.Entry<String, byte[]> entry : code.entrySet()) {
-                Compiler.verify(context, entry.getValue());
-            }
-        }
-    }
-
-    /**
-     * Implements the "-d" option - dump class files from script to specified output directory
-     *
-     * @throws IOException if classes cannot be written
-     */
-    private void dumpClassFiles() throws IOException {
-        if (context._dest_dir == null) {
-            return;
-        }
-
-        assert code != null;
-
-        for (final Entry<String, byte[]> entry : code.entrySet()) {
-            final String className = entry.getKey();
-            final String fileName  = className.replace('.', File.separatorChar) + ".class";
-            final int    index     = fileName.lastIndexOf(File.separatorChar);
-
-            if (index != -1) {
-                final File dir = new File(fileName.substring(0, index));
-                if (!dir.exists() && !dir.mkdirs()) {
-                    throw new IOException(ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
-                }
-            }
-
-            final byte[] bytecode = entry.getValue();
-            final File outFile = new File(context._dest_dir, fileName);
-            try (final FileOutputStream fos = new FileOutputStream(outFile)) {
-                fos.write(bytecode);
-            }
-        }
-    }
-
-    /**
-     * 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('/', '.');
-    }
-
-    /**
-     * Convert a binary name to a package/class name.
-     *
-     * @param name Binary name.
-     * @return Package/class name.
-     */
-    public static String pathName(final String name) {
-        return name.replace('.', '/');
-    }
-
-
 }
--- a/src/jdk/nashorn/internal/codegen/ConstantData.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/ConstantData.java	Thu Feb 14 13:01:52 2013 +0100
@@ -35,7 +35,7 @@
  * Manages constants needed by code generation.  Objects are maintained in an
  * interning maps to remove duplicates.
  */
-public class ConstantData {
+class ConstantData {
     /** Constant table. */
     final List<Object> constants;
 
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Thu Feb 14 13:01:52 2013 +0100
@@ -66,7 +66,6 @@
 import jdk.nashorn.internal.runtime.Debug;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Source;
 
 /**
  * Lower to more primitive operations. After lowering, an AST has symbols and
@@ -83,18 +82,9 @@
 
 final class FinalizeTypes extends NodeOperatorVisitor {
 
-    /** Current source. */
-    private final Source source;
-
     private static final DebugLogger LOG = new DebugLogger("finalize");
 
-    /**
-     * Constructor.
-     *
-     * @param compiler the compiler
-     */
-    FinalizeTypes(final Compiler compiler) {
-        this.source = compiler.getSource();
+    FinalizeTypes() {
     }
 
     @Override
@@ -424,16 +414,20 @@
 
     @Override
     public Node enter(final FunctionNode functionNode) {
+        if (functionNode.isLazy()) {
+            return null;
+        }
+
         // If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
         // this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
         // need for the callee.
-        if(!functionNode.needsCallee()) {
+        if (!functionNode.needsCallee()) {
             functionNode.getCalleeNode().getSymbol().setNeedsSlot(false);
         }
         // Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its
         // own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
         // this phase.
-        if(!(functionNode.needsScope() || functionNode.needsParentScope())) {
+        if (!(functionNode.needsScope() || functionNode.needsParentScope())) {
             functionNode.getScopeNode().getSymbol().setNeedsSlot(false);
         }
 
@@ -443,8 +437,7 @@
 
     @Override
     public Node leave(final IfNode ifNode) {
-        final Node test = convert(ifNode.getTest(), Type.BOOLEAN);
-        ifNode.setTest(test);
+        ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
         return ifNode;
     }
 
@@ -518,6 +511,7 @@
 
     @Override
     public Node leave(final VarNode varNode) {
+
         final Node rhs = varNode.getInit();
         if (rhs != null) {
             Type destType = specialize(varNode);
@@ -823,7 +817,7 @@
                 setTypeOverride(node, to);
                 return resultNode;
             }
-            resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node);
+            resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
         }
 
         LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
@@ -836,11 +830,11 @@
         return resultNode;
     }
 
-    private Node discard(final Node node) {
+    private static Node discard(final Node node) {
         node.setDiscard(true);
 
         if (node.getSymbol() != null) {
-            final Node discard = new UnaryNode(source, Token.recast(node.getToken(), TokenType.DISCARD), node);
+            final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node);
             //discard never has a symbol in the discard node - then it would be a nop
             discard.copyTerminalFlags(node);
             return discard;
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java	Thu Feb 14 13:01:52 2013 +0100
@@ -44,10 +44,13 @@
 /**
  * Simple constant folding pass, executed before IR is starting to be lowered.
  */
-public class FoldConstants extends NodeVisitor {
+final class FoldConstants extends NodeVisitor {
 
     private static final DebugLogger LOG = new DebugLogger("fold");
 
+    FoldConstants() {
+    }
+
     @Override
     public Node leave(final UnaryNode unaryNode) {
         final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
--- a/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Thu Feb 14 13:01:52 2013 +0100
@@ -25,6 +25,8 @@
 
 package jdk.nashorn.internal.codegen;
 
+import java.lang.invoke.MethodType;
+import java.util.ArrayList;
 import java.util.List;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
@@ -32,6 +34,8 @@
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
 
+import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
+
 /**
  * Class that generates function signatures for dynamic calls
  */
@@ -46,6 +50,9 @@
     /** valid Java descriptor string for function */
     private final String descriptor;
 
+    /** {@link MethodType} for function */
+    private final MethodType methodType;
+
     /**
      * Constructor
      *
@@ -126,8 +133,31 @@
             assert false : "isVarArgs cannot be false when argTypes are null";
         }
 
-        returnType = retType;
-        descriptor = Type.getMethodDescriptor(returnType, paramTypes);
+        this.returnType = retType;
+        this.descriptor = Type.getMethodDescriptor(returnType, paramTypes);
+
+        final List<Class<?>> paramTypeList = new ArrayList<>();
+        for (final Type paramType : paramTypes) {
+            paramTypeList.add(paramType.getTypeClass());
+        }
+
+        this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class[paramTypes.length]));
+    }
+
+    /**
+     * Create a function signature given a function node, using as much
+     * type information for parameters and return types that is availabe
+     *
+     * @param functionNode the function node
+     */
+    public FunctionSignature(final FunctionNode functionNode) {
+        this(
+            true,
+            functionNode.needsCallee(),
+            functionNode.getReturnType(),
+            (functionNode.isVarArg() && !functionNode.isScript()) ?
+                null :
+                functionNode.getParameters());
     }
 
     /**
@@ -165,21 +195,14 @@
     }
 
     /**
-     * Returns the generic signature of the function being compiled.
-     *
-     * @param functionNode function being compiled.
-     * @return function signature.
+     * Return the {@link MethodType} for this function signature
+     * @return the method type
      */
-    public static String functionSignature(final FunctionNode functionNode) {
-        return new FunctionSignature(
-            true,
-            functionNode.needsCallee(),
-            functionNode.getReturnType(),
-            (functionNode.isVarArg() && !functionNode.isScript()) ?
-                null :
-                functionNode.getParameters()).toString();
+    public MethodType getMethodType() {
+        return methodType;
     }
 
+
     private static Type[] objectArgs(final int nArgs) {
         final Type[] array = new Type[nArgs];
         for (int i = 0; i < nArgs; i++) {
--- a/src/jdk/nashorn/internal/codegen/Lower.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/Lower.java	Thu Feb 14 13:01:52 2013 +0100
@@ -89,10 +89,6 @@
 
 final class Lower extends NodeOperatorVisitor {
 
-    private final Compiler compiler;
-
-    private final Source source;
-
     /**
      * Nesting level stack. Currently just used for loops to avoid the problem
      * with terminal bodies that end with throw/return but still do continues to
@@ -111,9 +107,7 @@
      *
      * @param compiler the compiler
      */
-    Lower(final Compiler compiler) {
-        this.compiler   = compiler;
-        this.source     = compiler.getSource();
+    Lower() {
         this.nesting    = new ArrayDeque<>();
         this.statements = new ArrayList<>();
     }
@@ -204,7 +198,7 @@
         if (getCurrentFunctionNode().isScript()) {
             if (!(expr instanceof Block)) {
                 if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
-                    executeNode.setExpression(new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN),
+                    executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN),
                             getCurrentFunctionNode().getResultNode(),
                             expr));
                 }
@@ -254,6 +248,11 @@
     public Node enter(final FunctionNode functionNode) {
         LOG.info("START FunctionNode: " + functionNode.getName());
 
+        if (functionNode.isLazy()) {
+            LOG.info("LAZY: " + functionNode.getName());
+            return null;
+        }
+
         initFunctionNode(functionNode);
 
         Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
@@ -299,7 +298,7 @@
             }
 
             if (functionNode.isScript()) {
-                new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
+                new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
             }
 
             //do the statements - this fills the block with code
@@ -379,6 +378,8 @@
         if (tryNode != null) {
             //we are inside a try block - we don't necessarily have a result node yet. attr will do that.
             if (expr != null) {
+                final Source source = getCurrentFunctionNode().getSource();
+
                 //we need to evaluate the result of the return in case it is complex while
                 //still in the try block, store it in a result value and return it afterwards
                 final long token        = returnNode.getToken();
@@ -518,6 +519,7 @@
          * finally_body_inlined marked (*) will fix it before rethrowing
          * whatever problem there was for identical semantic.
          */
+        final Source source = getCurrentFunctionNode().getSource();
 
         // if try node does not contain a catch we can skip creation of a new
         // try node and just append our synthetic catch to the existing try node.
@@ -559,7 +561,7 @@
         final CatchNode catchAllNode;
         final IdentNode exception;
 
-        exception    = new IdentNode(source, token, finish, compiler.uniqueName("catch_all"));
+        exception    = new IdentNode(source, token, finish, getCurrentFunctionNode().uniqueName("catch_all"));
         catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
         catchAllNode.setIsSyntheticRethrow();
 
@@ -632,7 +634,7 @@
             if (whileNode instanceof DoWhileNode) {
                 setTerminal(whileNode, true);
             } else if (conservativeAlwaysTrue(test)) {
-                node = new ForNode(source, whileNode.getToken(), whileNode.getFinish());
+                node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
                 ((ForNode)node).setBody(body);
                 ((ForNode)node).accept(this);
                 setTerminal(node, !escapes);
@@ -799,7 +801,7 @@
         }
 
         //create a return statement
-        final Node returnNode = new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
+        final Node returnNode = new ReturnNode(functionNode.getSource(), functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
         returnNode.accept(this);
     }
 
@@ -897,7 +899,7 @@
             finallyBody = (Block)finallyBody.clone();
             final boolean hasTerminalFlags = finallyBody.hasTerminalFlags();
 
-            new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
+            new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
 
             if (hasTerminalFlags) {
                 getCurrentBlock().copyTerminalFlags(finallyBody);
@@ -951,17 +953,18 @@
      * TODO : only create those that are needed.
      * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant
      */
-    private void initFunctionNode(final FunctionNode functionNode) {
-        final long token  = functionNode.getToken();
-        final int  finish = functionNode.getFinish();
+    private static void initFunctionNode(final FunctionNode functionNode) {
+        final Source source = functionNode.getSource();
+        final long token    = functionNode.getToken();
+        final int  finish   = functionNode.getFinish();
 
         functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
         functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
         functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
         functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
-        if(functionNode.isVarArg()) {
+        if (functionNode.isVarArg()) {
             functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
-            if(functionNode.needsArguments()) {
+            if (functionNode.needsArguments()) {
                 functionNode.setArgumentsNode(new IdentNode(source, token, finish, ARGUMENTS.tag()));
             }
         }
--- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java	Thu Feb 14 13:01:52 2013 +0100
@@ -106,11 +106,11 @@
     /**
      * Set the compile unit and method name.
      * @param compileUnit the compile unit
-     * @param compiler the compiler to generate a unique method name
+     * @param methodName the method name
      */
-    protected void setClassAndName(final CompileUnit compileUnit, final Compiler compiler) {
+    protected void setClassAndName(final CompileUnit compileUnit, final String methodName) {
         this.compileUnit = compileUnit;
-        this.methodName = compiler.uniqueName("scopeCall");
+        this.methodName  = methodName;
     }
 
     /**
--- a/src/jdk/nashorn/internal/codegen/Splitter.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/Splitter.java	Thu Feb 14 13:01:52 2013 +0100
@@ -49,13 +49,14 @@
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.WhileNode;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * Split the IR into smaller compile units.
  */
-public class Splitter extends NodeVisitor {
+final class Splitter extends NodeVisitor {
     /** Current compiler. */
     private final Compiler compiler;
 
@@ -63,7 +64,7 @@
     private final FunctionNode functionNode;
 
     /** Compile unit for the main script. */
-    private final CompileUnit scriptCompileUnit;
+    private final CompileUnit outermostCompileUnit;
 
     /** Cache for calculated block weights. */
     private final Map<Node, Long> weightCache = new HashMap<>();
@@ -71,27 +72,35 @@
     /** Weight threshold for when to start a split. */
     public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
 
+    private static final DebugLogger LOG = Compiler.LOG;
+
     /**
      * Constructor.
      *
-     * @param compiler           the compiler
-     * @param functionNode       function node to split
-     * @param scriptCompileUnit  script compile unit
+     * @param compiler              the compiler
+     * @param functionNode          function node to split
+     * @param outermostCompileUnit  compile unit for outermost function, if non-lazy this is the script's compile unit
      */
-    public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit scriptCompileUnit) {
-        this.compiler     = compiler;
-        this.functionNode = functionNode;
-        this.scriptCompileUnit = scriptCompileUnit;
+    public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
+        this.compiler             = compiler;
+        this.functionNode         = functionNode;
+        this.outermostCompileUnit = outermostCompileUnit;
     }
 
     /**
      * Execute the split
      */
     void split() {
+        if (functionNode.isLazy()) {
+            LOG.info("Postponing split of '" + functionNode.getName() + "' as it's lazy");
+            return;
+        }
+        LOG.info("Initiating split of '" + functionNode.getName() + "'");
+
         long weight = WeighNodes.weigh(functionNode);
 
         if (weight >= SPLIT_THRESHOLD) {
-            Compiler.LOG.info("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
+            LOG.info("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
 
             functionNode.accept(this);
 
@@ -111,20 +120,18 @@
 
         assert functionNode.getCompileUnit() == null : "compile unit already set";
 
-        if (functionNode.isScript()) {
-            assert scriptCompileUnit != null : "script compile unit is null";
+        if (compiler.getFunctionNode() == functionNode) { //functionNode.isScript()) {
+            assert outermostCompileUnit != null : "outermost compile unit is null";
 
-            functionNode.setCompileUnit(scriptCompileUnit);
-            scriptCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
+            functionNode.setCompileUnit(outermostCompileUnit);
+            outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
         } else {
             functionNode.setCompileUnit(findUnit(weight));
         }
 
         // Recursively split nested functions
-        final List<FunctionNode> functions = functionNode.getFunctions();
-
-        for (final FunctionNode function : functions) {
-            new Splitter(compiler, function, scriptCompileUnit).split();
+        for (final FunctionNode function : functionNode.getFunctions()) {
+            new Splitter(compiler, function, outermostCompileUnit).split();
         }
     }
 
@@ -192,7 +199,7 @@
         final Source source = parent.getSource();
         final long   token  = parent.getToken();
         final int    finish = parent.getFinish();
-        final String name   = compiler.uniqueName(SPLIT_PREFIX.tag());
+        final String name   = parent.getFunction().uniqueName(SPLIT_PREFIX.tag());
 
         final Block newBlock = new Block(source, token, finish, parent, functionNode);
         newBlock.setFrame(new Frame(parent.getFrame()));
@@ -284,6 +291,10 @@
 
     @Override
     public Node enter(final FunctionNode node) {
+        if (node.isLazy()) {
+            return null;
+        }
+
         final List<Node> statements = node.getStatements();
 
         for (final Node statement : statements) {
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java	Thu Feb 14 13:01:52 2013 +0100
@@ -66,7 +66,7 @@
  * Computes the "byte code" weight of an AST segment. This is used
  * for Splitting too large class files
  */
-public class WeighNodes extends NodeOperatorVisitor {
+final class WeighNodes extends NodeOperatorVisitor {
     /*
      * Weight constants.
      */
--- a/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Thu Feb 14 14:16:58 2013 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +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.objects;
-
-import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
-import static jdk.nashorn.internal.codegen.Compiler.SCRIPTFUNCTION_IMPL_OBJECT;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
-import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
-
-import java.lang.invoke.MethodHandle;
-import java.util.ArrayList;
-import java.util.EnumSet;
-
-import jdk.nashorn.internal.codegen.CodeGenerator;
-import jdk.nashorn.internal.codegen.FunctionSignature;
-import jdk.nashorn.internal.codegen.MethodEmitter;
-import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptFunctionData;
-import jdk.nashorn.internal.runtime.ScriptObject;
-
-/**
- * Analyze a function object's characteristics for appropriate code
- * generation. This generates code for the instantiation of ScriptFunctions.
- */
-public class FunctionObjectCreator extends ObjectCreator {
-
-    private final FunctionNode functionNode;
-
-    /**
-     * Constructor
-     *
-     * @param codegen      the code generator
-     * @param functionNode the function node to turn into a ScriptFunction implementation
-     */
-    public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode) {
-        super(codegen, new ArrayList<String>(), new ArrayList<Symbol>(), false, false);
-        this.functionNode = functionNode;
-    }
-
-    private void loadHandle(final MethodEmitter method, final String signature) {
-        method.loadHandle(functionNode.getCompileUnit().getUnitClassName(), functionNode.getName(), signature, EnumSet.of(HANDLE_STATIC)); // function
-    }
-
-    /**
-     * Emit code for creating the object
-     *
-     * @param method the method emitter
-     */
-    @Override
-    public void makeObject(final MethodEmitter method) {
-
-        final PropertyMap map = makeMap();
-        final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
-        final ScriptFunctionData scriptFunctionData = new ScriptFunctionData(functionNode, map);
-
-        /*
-         * Instantiate the function object
-         */
-        method._new(SCRIPTFUNCTION_IMPL_OBJECT).dup();
-        codegen.loadConstant(scriptFunctionData);
-        loadHandle(method, signature);
-        if(functionNode.needsParentScope()) {
-            method.loadScope();
-        } else {
-            method.loadNull();
-        }
-        method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
-
-        /*
-         * Invoke the constructor
-         */
-        method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_OBJECT, ScriptFunctionData.class, MethodHandle.class, ScriptObject.class, MethodHandle.class));
-
-    }
-}
--- a/src/jdk/nashorn/internal/codegen/objects/ObjectClassGenerator.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/codegen/objects/ObjectClassGenerator.java	Thu Feb 14 13:01:52 2013 +0100
@@ -472,11 +472,11 @@
         final byte[] code = classEmitter.toByteArray();
 
         if (context != null && context._print_code) {
-            ClassEmitter.disassemble(context, code);
+            Context.getCurrentErr().println(ClassEmitter.disassemble(code));
         }
 
         if (context != null && context._verify_code) {
-            ClassEmitter.verify(context, code);
+            context.verify(code);
         }
 
         return code;
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Feb 14 13:01:52 2013 +0100
@@ -32,15 +32,16 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Stack;
+
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.Frame;
 import jdk.nashorn.internal.codegen.MethodEmitter;
 import jdk.nashorn.internal.codegen.Namespace;
-import jdk.nashorn.internal.codegen.Splitter;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -67,6 +68,28 @@
         SETTER
     }
 
+    /** Compilation states available */
+    public enum CompilationState {
+        /** compiler is ready */
+        INITIALIZED,
+        /** method has been parsed */
+        PARSED,
+        /** method has been parsed */
+        PARSE_ERROR,
+        /** constant folding pass */
+        CONSTANT_FOLDED,
+        /** method has been lowered */
+        LOWERED,
+        /** method hass been attributed */
+        ATTR,
+        /** method has been split */
+        SPLIT,
+        /** method has had its types finalized */
+        FINALIZED,
+        /** method has been emitted to bytecode */
+        EMITTED
+    }
+
     /** External function identifier. */
     @Ignore
     private IdentNode ident;
@@ -147,6 +170,10 @@
     @Ignore
     private Node selfSymbolInit;
 
+    /** Current compilation state */
+    @Ignore
+    private final EnumSet<CompilationState> compilationState;
+
     /** Function flags. */
     private int flags;
 
@@ -200,16 +227,16 @@
     /**
      * Constructor
      *
-     * @param source   the source
-     * @param token    token
-     * @param finish   finish
-     * @param compiler the compiler
-     * @param parent   the parent block
-     * @param ident    the identifier
-     * @param name     the name of the function
+     * @param source    the source
+     * @param token     token
+     * @param finish    finish
+     * @param namespace the namespace
+     * @param parent    the parent block
+     * @param ident     the identifier
+     * @param name      the name of the function
      */
     @SuppressWarnings("LeakingThisInConstructor")
-    public FunctionNode(final Source source, final long token, final int finish, final Compiler compiler, final Block parent, final IdentNode ident, final String name) {
+    public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final Block parent, final IdentNode ident, final String name) {
         super(source, token, finish, parent, null);
 
         this.ident             = ident;
@@ -219,7 +246,7 @@
         this.functions         = new ArrayList<>();
         this.firstToken        = token;
         this.lastToken         = token;
-        this.namespace         = new Namespace(compiler.getNamespace().getParent());
+        this.namespace         = namespace;
         this.labelStack        = new Stack<>();
         this.controlStack      = new Stack<>();
         this.declarations      = new ArrayList<>();
@@ -227,6 +254,7 @@
         // it as such a leak - this is a false positive as we're setting this into a field of the object being
         // constructed, so it can't be seen from other threads.
         this.function          = this;
+        this.compilationState  = EnumSet.of(CompilationState.INITIALIZED);
     }
 
     @SuppressWarnings("LeakingThisInConstructor")
@@ -269,6 +297,8 @@
         // it as such a leak - this is a false positive as we're setting this into a field of the object being
         // constructed, so it can't be seen from other threads.
         this.function = this;
+
+        this.compilationState = EnumSet.copyOf(functionNode.compilationState);
     }
 
     @Override
@@ -344,6 +374,41 @@
         return super.needsScope() || isScript();
     }
 
+    /**
+     * Check whether this FunctionNode has reached a give CompilationState.
+     *
+     * @param state the state to check for
+     * @return true of the node is in the given state
+     */
+    public boolean hasState(final EnumSet<CompilationState> state) {
+        return compilationState.equals(state);
+    }
+
+    /**
+     * 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.
+     * To check for an exact state, use {FunctionNode{@link #hasState(EnumSet)}
+     *
+     * @param state state to check for
+     * @return true if state is present in the total compilation state of this FunctionNode
+     */
+    public boolean hasState(final CompilationState state) {
+        return compilationState.contains(state);
+    }
+
+    /**
+     * Add a state to the total CompilationState of this node, e.g. if
+     * FunctionNode has been lowered, the compiler will add
+     * {@code CompilationState#LOWERED} to the state vector
+     *
+     * @param state {@link CompilationState} to add
+     */
+    public void setState(final CompilationState state) {
+        compilationState.add(state);
+    }
+
     /*
      * Frame management.
      */
@@ -366,17 +431,6 @@
     }
 
     /**
-     * return a unique name in the scope of the function.
-     *
-     * @param base Base string.
-     *
-     * @return Unique name.
-     */
-    public String uniqueName(final String base) {
-        return namespace.uniqueName(base);
-    }
-
-    /**
      * Create a temporary variable to the current frame.
      *
      * @param currentFrame Frame to add to - defaults to current function frame
@@ -408,6 +462,15 @@
     }
 
     /**
+     * Create a unique name in the namespace of this FunctionNode
+     * @param base prefix for name
+     * @return base if no collision exists, otherwise a name prefix with base
+     */
+    public String uniqueName(final String base) {
+        return namespace.uniqueName(base);
+    }
+
+    /**
      * Add a new temporary variable to the current frame
      *
      * @param type Strong type of symbol
@@ -428,11 +491,11 @@
      */
     public Symbol newLiteral(final LiteralNode<?> literalNode) {
         final String uname = uniqueName(LITERAL_PREFIX.tag());
-        final Symbol sym = new Symbol(uname, IS_CONSTANT, literalNode.getType());
-        sym.setNode(literalNode);
-        literalNode.setSymbol(sym);
+        final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType());
+        symbol.setNode(literalNode);
+        literalNode.setSymbol(symbol);
 
-        return sym;
+        return symbol;
     }
 
     @Override
@@ -806,7 +869,6 @@
 
     /**
      * Checks if this function is a sub-function generated by splitting a larger one
-     * @see Splitter
      *
      * @return true if this function is split from a larger one
      */
@@ -816,7 +878,6 @@
 
     /**
      * Flag this function node as being a sub-function generated by the splitter
-     * @see Splitter
      */
     public void setIsSplit() {
         this.flags |= IS_SPLIT;
@@ -1149,7 +1210,6 @@
     /**
      * Get the compile unit used to compile this function
      * @see Compiler
-     * @see Splitter
      * @return the compile unit
      */
     public CompileUnit getCompileUnit() {
@@ -1159,7 +1219,6 @@
     /**
      * Reset the compile unit used to compile this function
      * @see Compiler
-     * @see Splitter
      * @param compileUnit the compile unit
      */
     public void setCompileUnit(final CompileUnit compileUnit) {
--- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Thu Feb 14 13:01:52 2013 +0100
@@ -25,11 +25,8 @@
 
 package jdk.nashorn.internal.ir.debug;
 
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import java.util.Arrays;
 import java.util.List;
-import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BinaryNode;
@@ -87,8 +84,7 @@
      * @return JSON string representation of AST of the supplied code
      */
     public static String parse(final Context context, final String code, final String name, final boolean includeLoc) {
-        final Compiler     compiler   = Compiler.compiler(new Source(name, code), context, new Context.ThrowErrorManager(), context._strict);
-        final Parser       parser     = new Parser(compiler, context._strict);
+        final Parser       parser     = new Parser(context, new Source(name, code), new Context.ThrowErrorManager(), context._strict);
         final JSONWriter   jsonWriter = new JSONWriter(includeLoc);
         try {
             final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.tag());
--- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java	Thu Feb 14 13:01:52 2013 +0100
@@ -70,7 +70,7 @@
     private FunctionNode currentFunctionNode;
 
     /** Current compile unit used for class generation. */
-    protected CompileUnit compileUnit;
+    private CompileUnit compileUnit;
 
     /**
      * Current method visitor used for method generation.
--- a/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java	Thu Feb 14 13:01:52 2013 +0100
@@ -32,7 +32,7 @@
 
 /**
  * A {@code ScriptFunctionImpl} subclass for functions created using {@code Function.prototype.bind}. Such functions
- * must track their {@code [[TargetFunction]] property for purposes of correctly implementing {@code [[HasInstance]]};
+ * must track their {@code [[TargetFunction]]} property for purposes of correctly implementing {@code [[HasInstance]]};
  * see {@link ScriptFunction#isInstance(ScriptObject)}.
  */
 class BoundScriptFunctionImpl extends ScriptFunctionImpl {
--- a/src/jdk/nashorn/internal/objects/Global.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/objects/Global.java	Thu Feb 14 13:01:52 2013 +0100
@@ -351,6 +351,8 @@
 
     /**
      * Constructor
+     *
+     * @param context the context
      */
     public Global(final Context context) {
         this.context = context;
--- a/src/jdk/nashorn/internal/objects/NativeJava.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/objects/NativeJava.java	Thu Feb 14 13:01:52 2013 +0100
@@ -385,6 +385,7 @@
     public static Object extend(final Object self, final Object... types) {
         if(types == null || types.length == 0) {
             typeError("extend.expects.at.least.one.argument");
+            throw new AssertionError(); //circumvent warning for types == null below
         }
         final Class<?>[] stypes = new Class<?>[types.length];
         try {
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Thu Feb 14 13:01:52 2013 +0100
@@ -28,7 +28,7 @@
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
 import java.lang.invoke.MethodHandle;
-import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
+
 import jdk.nashorn.internal.runtime.GlobalFunctions;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
@@ -94,14 +94,14 @@
 
     /**
      * Constructor called by (compiler) generated code for {@link ScriptObject}s.
-     * Code is generated by {@link FunctionObjectCreator}
+     * Code is generated by {@link jdk.nashorn.internal.codegen.CodeGenerator#newFunctionObject}
      *
      * @param data static function data
      * @param methodHandle handle for invocation
      * @param scope scope object
      * @param allocator instance constructor for function
      */
-    public ScriptFunctionImpl(final ScriptFunctionData data, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle allocator) {
+    public ScriptFunctionImpl(final MethodHandle methodHandle, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) {
         super(data, getMap(data.isStrict()), scope);
         // Set method handles in script data
         data.setMethodHandles(methodHandle, allocator);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java	Thu Feb 14 13:01:52 2013 +0100
@@ -0,0 +1,122 @@
+package jdk.nashorn.internal.objects;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+
+import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
+
+import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.runtime.CodeInstaller;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptFunctionData;
+import jdk.nashorn.internal.runtime.ScriptObject;
+
+/**
+ * A trampoline is a promise to compile a {@link ScriptFunction} later. It just looks like
+ * the call to the script function, but when invoked it will compile the script function
+ * (in a new compile unit) and invoke it
+ */
+public final class ScriptFunctionTrampolineImpl extends ScriptFunctionImpl {
+
+    private CodeInstaller<Context> installer;
+
+    /** Function node to lazily recompile when trampoline is hit */
+    private FunctionNode functionNode;
+
+    /**
+     * Constructor
+     *
+     * @param installer    opaque code installer from context
+     * @param functionNode function node to lazily compile when trampoline is hit
+     * @param data         {@link ScriptFunctionData} for function
+     * @param scope        scope
+     * @param allocator    allocator
+     */
+    public ScriptFunctionTrampolineImpl(final CodeInstaller<Context> installer, final FunctionNode functionNode, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) {
+        super(null, data, scope, allocator);
+
+        this.installer    = installer;
+        this.functionNode = functionNode;
+
+        data.setMethodHandles(makeTrampoline(), allocator);
+    }
+
+    private final MethodHandle makeTrampoline() {
+        final MethodType mt =
+            new FunctionSignature(
+                true,
+                functionNode.needsCallee(),
+                Type.OBJECT,
+                functionNode.getParameters().size()).
+            getMethodType();
+
+        return
+            MH.bindTo(
+                MH.asCollector(
+                    findOwnMH(
+                        "trampoline",
+                        Object.class,
+                        Object[].class),
+                    Object[].class,
+                    mt.parameterCount()),
+                this);
+    }
+
+    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findVirtual(MethodHandles.lookup(), ScriptFunctionTrampolineImpl.class, name, MH.type(rtype, types));
+    }
+
+    @Override
+    protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) {
+        //prevent trampoline recompilation cycle if a function is bound before use
+        compile();
+        return super.makeBoundFunction(data);
+    }
+
+    private MethodHandle compile() {
+        final Compiler compiler = new Compiler(installer, functionNode);
+        if (!compiler.compile()) {
+            assert false : "compilation error in trampoline for " + functionNode.getName();
+            return null;
+        }
+
+        final Class<?> clazz = compiler.install();
+        /* compute function signature for lazy method. this can be done first after compilation, as only then do we know
+         * the final state about callees, scopes and specialized parameter types */
+        final FunctionSignature signature = new FunctionSignature(true, functionNode.needsCallee(), Type.OBJECT, functionNode.getParameters().size());
+        final MethodType        mt        = signature.getMethodType();
+
+        MethodHandle mh = MH.findStatic(MethodHandles.publicLookup(), clazz, functionNode.getName(), mt);
+        mh = MH.bindTo(mh, this);
+
+        // now the invoker method looks like the one our superclass is expecting
+        resetInvoker(mh);
+
+        return mh;
+    }
+
+    @SuppressWarnings("unused")
+    private Object trampoline(final Object... args) {
+        /** Create a new compiler for the lazy node, using the same installation policy as the old one */
+
+        MethodHandle mh = compile();
+
+        // spread the array to invididual args of the correct type
+        mh = MH.asSpreader(mh, Object[].class, mh.type().parameterCount());
+
+        try {
+            //invoke the real method the trampoline points to. this only happens once
+            return mh.invoke(args);
+        } catch (final RuntimeException | Error e) {
+            throw e;
+        } catch (final Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+}
--- a/src/jdk/nashorn/internal/parser/Lexer.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/parser/Lexer.java	Thu Feb 14 13:01:52 2013 +0100
@@ -289,6 +289,11 @@
         add(type, start, position);
     }
 
+    /**
+     * Return the String of valid whitespace characters for regular
+     * expressions in JavaScript
+     * @return regexp whitespace string
+     */
     public static String getWhitespaceRegExp() {
         return JAVASCRIPT_WHITESPACE_IN_REGEXP;
     }
@@ -959,6 +964,8 @@
                     // Add string token without editing.
                     add(type, stringState.position, stringState.limit);
                     break;
+                default:
+                    break;
                 }
             } else {
                 /// Add string token without editing.
--- a/src/jdk/nashorn/internal/parser/Parser.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/parser/Parser.java	Thu Feb 14 13:01:52 2013 +0100
@@ -39,7 +39,6 @@
 import static jdk.nashorn.internal.parser.TokenType.ELSE;
 import static jdk.nashorn.internal.parser.TokenType.EOF;
 import static jdk.nashorn.internal.parser.TokenType.EOL;
-import static jdk.nashorn.internal.parser.TokenType.EXECSTRING;
 import static jdk.nashorn.internal.parser.TokenType.FINALLY;
 import static jdk.nashorn.internal.parser.TokenType.FUNCTION;
 import static jdk.nashorn.internal.parser.TokenType.IDENT;
@@ -60,7 +59,9 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Stack;
-import jdk.nashorn.internal.codegen.Compiler;
+
+import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.Namespace;
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Block;
@@ -97,8 +98,10 @@
 import jdk.nashorn.internal.ir.WhileNode;
 import jdk.nashorn.internal.ir.WithNode;
 import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.JSErrorType;
 import jdk.nashorn.internal.runtime.ParserException;
+import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.ScriptingFunctions;
 
 /**
@@ -106,49 +109,72 @@
  *
  */
 public class Parser extends AbstractParser {
-    /** Code generator. */
-    private final Compiler compiler;
-
     /** Current context. */
     private final Context context;
 
     /** Is scripting mode. */
     private final boolean scripting;
 
-    /** Top level script being compiled. */
+    /** Top level script being parsed. */
     private FunctionNode script;
 
-    /** Current function being compiled. */
+    /** Current function being parsed. */
     private FunctionNode function;
 
     /** Current parsing block. */
     private Block block;
 
+    /** Namespace for function names where not explicitly given */
+    private final Namespace namespace;
+
     /**
-     * Construct a parser.
-     * @param compiler Compiler state used to parse.
+     * Constructor
+     *
+     * @param context parser context
+     * @param source  source to parse
+     * @param errors  error manager
      */
-    public Parser(final Compiler compiler) {
-        this(compiler, compiler.getContext()._strict);
+    public Parser(final Context context, final Source source, final ErrorManager errors) {
+        this(context, source, errors, context._strict);
     }
 
     /**
      * Construct a parser.
-     * @param compiler Compiler state used to parse.
-     * @param strict parser created with strict mode enabled.
+     *
+     * @param context parser context
+     * @param source  source to parse
+     * @param errors  error manager
+     * @param strict  parser created with strict mode enabled.
      */
-    public Parser(final Compiler compiler, final boolean strict) {
-        super(compiler.getSource(), compiler.getErrors(), strict);
-
-        this.compiler = compiler;
-        this.context  = compiler.getContext();
-        this.scripting = this.context._scripting;
+    public Parser(final Context context, final Source source, final ErrorManager errors, final boolean strict) {
+        super(source, errors, strict);
+        this.context   = context;
+        this.namespace = new Namespace(context.getNamespace());
+        this.scripting = context._scripting;
     }
 
     /**
-     * Parse source content.
-     * @param scriptName file name for script
-     * @return Top level function (script).
+     * Execute parse and return the resulting function node.
+     * Errors will be thrown and the error manager will contain information
+     * if parsing should fail
+     *
+     * This is the default parse call, which will name the function node
+     * "runScript" {@link CompilerConstants#RUN_SCRIPT}
+     *
+     * @return function node resulting from successful parse
+     */
+    public FunctionNode parse() {
+        return parse(RUN_SCRIPT.tag());
+    }
+
+    /**
+     * 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) {
         try {
@@ -257,11 +283,11 @@
         }
 
         sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.tag());
-        final String name = compiler.uniqueName(sb.toString());
+        final String name = namespace.uniqueName(sb.toString());
         assert function != null || name.equals(RUN_SCRIPT.tag())  : "name = " + name;// must not rename runScript().
 
         // Start new block.
-        final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), compiler, block, ident, name);
+        final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, block, ident, name);
         block = function = functionBlock;
         function.setStrictMode(isStrictMode);
 
--- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java	Thu Feb 14 13:01:52 2013 +0100
@@ -34,14 +34,21 @@
  * The compiler still retains most of the state around code emission
  * and management internally, so this is to avoid passing around any
  * logic that isn't directly related to installing a class
+ * @param <T> owner class type for this code installer
  *
  */
-public interface CodeInstaller {
+public interface CodeInstaller<T> {
+    /**
+     * Return the owner for the CodeInstaller, e.g. a {@link Context}
+     * @return owner
+     */
+    public T getOwner();
+
     /**
      * Install a class
      * @param className name of the class with / separation
-      * @param bytecode  bytecode
-      * @return the installed class
+     * @param bytecode  bytecode
+     * @return the installed class
      */
     public Class<?> install(final String className, final byte[] bytecode);
 }
--- a/src/jdk/nashorn/internal/runtime/Context.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Thu Feb 14 13:01:52 2013 +0100
@@ -45,12 +45,16 @@
 import java.security.PrivilegedAction;
 import java.util.Locale;
 import java.util.TimeZone;
+
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
 import jdk.nashorn.internal.codegen.ClassEmitter;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.Namespace;
 import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.debug.PrintVisitor;
+import jdk.nashorn.internal.parser.Parser;
 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 import jdk.nashorn.internal.runtime.options.KeyValueOption;
@@ -63,6 +67,36 @@
  */
 public final class Context {
 
+    /**
+     * ContextCodeInstaller that has the privilege of installing classes in the Context.
+     * Can only be instantiated from inside the context and is opaque to other classes
+     */
+    public static class ContextCodeInstaller implements CodeInstaller<Context> {
+        private final Context      context;
+        private final ScriptLoader loader;
+        private final CodeSource   codeSource;
+
+        private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) {
+            this.context    = context;
+            this.loader     = loader;
+            this.codeSource = codeSource;
+        }
+
+        /**
+         * Return the context for this installer
+         * @return context
+         */
+        @Override
+        public Context getOwner() {
+            return context;
+        }
+
+        @Override
+        public Class<?> install(final String className, final byte[] bytecode) {
+            return loader.installClass(className, bytecode, codeSource);
+        }
+    }
+
     /** Is Context global debug mode enabled ? */
     public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
 
@@ -751,6 +785,7 @@
     /**
      * Initialize given global scope object.
      *
+     * @param global the global
      * @return the initialized global scope object.
      */
     public ScriptObject initGlobal(final ScriptObject global) {
@@ -877,22 +912,24 @@
             }
         }
 
-        final Compiler compiler = Compiler.compiler(source, this, errMan, strict);
-
-        if (!compiler.compile()) {
+        final FunctionNode functionNode = new Parser(this, source, errMan, strict).parse();
+        if (errors.hasErrors() || _parse_only) {
             return null;
         }
 
-        final URL url = source.getURL();
+        if (_print_lower_parse) {
+            getErr().println(new PrintVisitor(functionNode));
+        }
+
+        final URL          url    = source.getURL();
         final ScriptLoader loader = _loader_per_compile ? createNewLoader() : scriptLoader;
         final CodeSource   cs     = url == null ? null : new CodeSource(url, (CodeSigner[])null);
+        final CodeInstaller<Context> installer = new ContextCodeInstaller(this, loader, cs);
 
-        script = compiler.install(new CodeInstaller() {
-            @Override
-            public Class<?> install(final String className, final byte[] bytecode) {
-                return loader.installClass(className, bytecode, cs);
-            }
-        });
+        final Compiler compiler = new Compiler(installer, functionNode, strict);
+
+        compiler.compile();
+        script = compiler.install();
 
         if (global != null) {
             global.cacheClass(source, script);
@@ -917,15 +954,14 @@
     private ScriptObject newGlobalTrusted() {
         try {
             final Class<?> clazz = Class.forName("jdk.nashorn.internal.objects.Global", true, scriptLoader);
-            final Constructor cstr = clazz.getConstructor(Context.class);
+            final Constructor<?> cstr = clazz.getConstructor(Context.class);
             return (ScriptObject) cstr.newInstance(this);
         } catch (final Exception e) {
             printStackTrace(e);
             if (e instanceof RuntimeException) {
                 throw (RuntimeException)e;
-            } else {
-                throw new RuntimeException(e);
             }
+            throw new RuntimeException(e);
         }
     }
 }
--- a/src/jdk/nashorn/internal/runtime/DebugLogger.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/runtime/DebugLogger.java	Thu Feb 14 13:01:52 2013 +0100
@@ -73,8 +73,11 @@
      * Get the output writer for the logger. Loggers always default to
      * stderr for output as they are used mainly to output debug info
      *
+     * Can be inherited so this should not be static.
+     *
      * @return print writer for log output.
      */
+    @SuppressWarnings("static-method")
     public PrintWriter getOutputStream() {
         return Context.getCurrentErr();
     }
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/runtime/FindProperty.java	Thu Feb 14 13:01:52 2013 +0100
@@ -36,7 +36,7 @@
 public final class FindProperty {
     /** Object where search began. */
     private final ScriptObject self;
-    ;
+
     /** Object where search finish. */
     private final ScriptObject prototype;
 
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Thu Feb 14 13:01:52 2013 +0100
@@ -78,12 +78,14 @@
     /**
      * Constructor
      *
-     * @param name         function name
-     * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
-     * @param map          property map
-     * @param scope        scope
-     * @param specs        specialized version of this function - other method handles
-     *
+     * @param name          function name
+     * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
+     * @param map           property map
+     * @param scope         scope
+     * @param specs         specialized version of this function - other method handles
+     * @param strict        is this a strict mode function?
+     * @param builtin       is this a built in function?
+     * @param isConstructor is this a constructor?
      */
     protected ScriptFunction(
             final String name,
@@ -240,10 +242,17 @@
      * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
      * @return a function with the specified self and parameters bound.
      */
-    protected ScriptFunction makeBoundFunction(Object self, Object[] args) {
+    protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
         return makeBoundFunction(data.makeBoundFunctionData(this, self, args));
     }
 
+    /**
+     * Create a version of this function as in {@link ScriptFunction#makeBoundFunction(Object, Object[])},
+     * but using a {@link ScriptFunctionData} for the bound data.
+     *
+     * @param boundData ScriptFuntionData for the bound function
+     * @return a function with the bindings performed according to the given data
+     */
     protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData);
 
     @Override
@@ -350,6 +359,15 @@
     }
 
     /**
+     * Reset the invoker handle. This is used by trampolines for
+     * lazy code generation
+     * @param invoker new invoker
+     */
+    protected void resetInvoker(final MethodHandle invoker) {
+        data.resetInvoker(invoker);
+    }
+
+    /**
      * Prototype getter for this ScriptFunction - follows the naming convention
      * used by Nasgen and the code generator
      *
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Thu Feb 14 13:01:52 2013 +0100
@@ -101,11 +101,13 @@
 
     /**
      * Constructor
+     *
      * @param name the function name
      * @param methodHandle the method handle
      * @param specs array of specialized method handles
      * @param strict strict flag
      * @param builtin builtin flag
+     * @param isConstructor constructor flags
      */
     public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) {
         this(name, null, 0L, methodHandle, specs, strict, builtin, isConstructor);
@@ -432,7 +434,7 @@
      * @param invoker the invoker handle
      * @param allocator the allocator handle
      */
-    public void setMethodHandles(MethodHandle invoker, MethodHandle allocator) {
+    public void setMethodHandles(final MethodHandle invoker, final MethodHandle allocator) {
         // We can't make method handle fields final because they're not available during codegen
         // and they're set when first called, so we enforce set-once here.
         if (this.invoker == null) {
@@ -443,6 +445,16 @@
     }
 
     /**
+     * Used by the trampoline. Must not be any wider than package
+     * private
+     * @param invoker new invoker
+     */
+    void resetInvoker(final MethodHandle invoker) {
+        this.invoker     = invoker;
+        this.constructor = null; //delay constructor composition
+    }
+
+    /**
      * Allocates an object using this function's allocator.
      * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
      */
--- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Thu Feb 14 13:01:52 2013 +0100
@@ -54,14 +54,21 @@
     /** Handle to implementation of {@link ScriptingFunctions#exec} - Nashorn extension */
     public static final MethodHandle EXEC = findOwnMH("exec",     Object.class, Object.class, Object.class, Object.class);
 
-    /** Names of special properties used by $EXEC API. */
-    public  static final String EXEC_NAME = "$EXEC";
-    public  static final String OUT_NAME  = "$OUT";
-    public  static final String ERR_NAME  = "$ERR";
-    public  static final String EXIT_NAME = "$EXIT";
+    /** EXEC name - special property used by $EXEC API. */
+    public static final String EXEC_NAME = "$EXEC";
+
+    /** OUT name - special property used by $EXEC API. */
+    public static final String OUT_NAME  = "$OUT";
+
+    /** ERR name - special property used by $EXEC API. */
+    public static final String ERR_NAME  = "$ERR";
+
+    /** EXIT name - special property used by $EXEC API. */
+    public static final String EXIT_NAME = "$EXIT";
 
     /** Names of special properties used by $ENV API. */
     public  static final String ENV_NAME  = "$ENV";
+
     private static final String PWD_NAME  = "PWD";
 
     private ScriptingFunctions() {
@@ -114,8 +121,11 @@
      *
      * @param self   self reference
      * @param string string to execute
+     * @param input
      *
      * @return output string from the request
+     * @throws IOException
+     * @throws InterruptedException
      */
     public static Object exec(final Object self, final Object string, final Object input) throws IOException, InterruptedException {
         // Current global is need to fetch additional inputs and for additional results.
--- a/src/jdk/nashorn/tools/Shell.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/src/jdk/nashorn/tools/Shell.java	Thu Feb 14 13:01:52 2013 +0100
@@ -41,6 +41,8 @@
 import java.util.ResourceBundle;
 import jdk.nashorn.api.scripting.NashornException;
 import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.parser.Parser;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.ScriptFunction;
@@ -241,13 +243,14 @@
 
             // For each file on the command line.
             for (final String fileName : files) {
-                final File file = new File(fileName);
-                final Source source = new Source(fileName, file);
-                final Compiler compiler = Compiler.compiler(source, context);
-                compiler.compile();
+                final FunctionNode functionNode = new Parser(context, new Source(fileName, new File(fileName)), errors).parse();
+
                 if (errors.getNumberOfErrors() != 0) {
                     return COMPILATION_ERROR;
                 }
+
+                //null - pass no code installer - this is compile only
+                new Compiler(context, functionNode).compile();
             }
         } finally {
             context.getOut().flush();
@@ -282,7 +285,7 @@
             // For each file on the command line.
             for (final String fileName : files) {
                 final File file = new File(fileName);
-                ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
+                final ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
                 if (script == null || errors.getNumberOfErrors() != 0) {
                     return COMPILATION_ERROR;
                 }
--- a/test/script/trusted/JDK-8006529.js	Thu Feb 14 14:16:58 2013 +0530
+++ b/test/script/trusted/JDK-8006529.js	Thu Feb 14 13:01:52 2013 +0100
@@ -39,17 +39,15 @@
  * and FunctionNode because of package-access check and so reflective calls.
  */
 
+var Parser         = Java.type("jdk.nashorn.internal.parser.Parser")
 var Compiler       = Java.type("jdk.nashorn.internal.codegen.Compiler")
 var Context        = Java.type("jdk.nashorn.internal.runtime.Context")
 var Source         = Java.type("jdk.nashorn.internal.runtime.Source")
 var FunctionNode   = Java.type("jdk.nashorn.internal.ir.FunctionNode")
 
 // Compiler class methods and fields
-
-// Compiler.compile(Source, Context)
-var compilerMethod = Compiler.class.getMethod("compiler", Source.class, Context.class);
-// Compiler.compile()
-var compileMethod  = Compiler.class.getMethod("compile");
+var parseMethod = Parser.class.getMethod("parse");
+var compileMethod = Compiler.class.getMethod("compile");
 
 // NOTE: private field. But this is a trusted test!
 // Compiler.functionNode
@@ -90,10 +88,14 @@
 // source code, returns a jdk.nashorn.internal.ir.FunctionNode object 
 // representing it.
 function compile(source) {
-   var compiler = compilerMethod.invoke(null,
-       new Source("<no name>", source), Context.getContext())
-   compileMethod.invoke(compiler);
-   return getScriptNode(compiler)
+    var source   = new Source("<no name>", source);
+    var parser   = new Parser(Context.getContext(), source, null);
+    var func     = parseMethod.invoke(parser);
+    var compiler = new Compiler(Context.getContext(), func);
+
+    compileMethod.invoke(compiler);
+
+    return getScriptNode(compiler);
 };
 
 var allAssertions = (function() {
--- a/test/src/jdk/nashorn/internal/parser/ParserTest.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/test/src/jdk/nashorn/internal/parser/ParserTest.java	Thu Feb 14 13:01:52 2013 +0100
@@ -156,10 +156,7 @@
                 Context.setGlobal(global);
             }
             final Source   source   = new Source(file.getAbsolutePath(), buffer);
-            final Compiler compiler = Compiler.compiler(source, context, errors, context._strict);
-
-            final Parser parser = new Parser(compiler);
-            parser.parse(CompilerConstants.RUN_SCRIPT.tag());
+            new Parser(context, source, errors).parse();
             if (errors.getNumberOfErrors() > 0) {
                 log("Parse failed: " + file.getAbsolutePath());
                 failed++;
--- a/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java	Thu Feb 14 14:16:58 2013 +0530
+++ b/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java	Thu Feb 14 13:01:52 2013 +0100
@@ -126,6 +126,7 @@
                 }
                 final File file = new File(fileName);
                 ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
+
                 if (script == null || errors.getNumberOfErrors() != 0) {
                     return COMPILATION_ERROR;
                 }