8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change.
authorlagergren
Tue, 12 Mar 2013 15:30:53 +0100
changeset 16523 af8b30edebce
parent 16522 d643e3ee819c
child 16524 859859da57fe
8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change. Reviewed-by: attila, jlaskey
nashorn/src/jdk/nashorn/internal/codegen/Attr.java
nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java
nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java
nashorn/src/jdk/nashorn/internal/codegen/Compiler.java
nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java
nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java
nashorn/src/jdk/nashorn/internal/codegen/Lower.java
nashorn/src/jdk/nashorn/internal/codegen/Splitter.java
nashorn/src/jdk/nashorn/internal/codegen/types/Type.java
nashorn/src/jdk/nashorn/internal/ir/Block.java
nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java
nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java
nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java
nashorn/src/jdk/nashorn/internal/ir/annotations/ChildNode.java
nashorn/src/jdk/nashorn/internal/ir/annotations/ParentNode.java
nashorn/src/jdk/nashorn/internal/ir/annotations/Reference.java
nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java
nashorn/src/jdk/nashorn/internal/objects/NativeArray.java
nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java
nashorn/src/jdk/nashorn/internal/objects/NativeError.java
nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java
nashorn/src/jdk/nashorn/internal/parser/JSONParser.java
nashorn/src/jdk/nashorn/internal/parser/Parser.java
nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java
nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java
nashorn/src/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk/nashorn/internal/runtime/ECMAException.java
nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java
nashorn/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java
nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties
nashorn/test/script/currently-failing/JDK-8006529.js
nashorn/test/script/currently-failing/clone_ir.js
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Tue Mar 12 15:30:53 2013 +0100
@@ -42,6 +42,7 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -51,6 +52,7 @@
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.CallNode;
 import jdk.nashorn.internal.ir.CallNode.EvalArgs;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.CaseNode;
 import jdk.nashorn.internal.ir.CatchNode;
 import jdk.nashorn.internal.ir.ForNode;
@@ -332,8 +334,14 @@
             functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this));
         }
 
+        if (functionNode.hasLazyChildren()) {
+            objectifySymbols(functionNode);
+        }
+
         functionNode.popFrame();
 
+        functionNode.setState(CompilationState.ATTR);
+
         end(functionNode, false);
 
         return null;
@@ -957,20 +965,17 @@
 
     @Override
     public Node leaveBIT_AND(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveBIT_OR(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveBIT_XOR(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
@@ -1002,14 +1007,28 @@
         return binaryNode;
     }
 
+    private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) {
+        // TODO we currently don't support changing inferred type based on uses, only on
+        // definitions. we would need some additional logic. We probably want to do that
+        // in the future, if e.g. a specialized method gets parameter that is only used
+        // as, say, an int : function(x) { return x & 4711 }, and x is not defined in
+        // the function. to make this work, uncomment the following two type inferences
+        // and debug.
+
+        //newType(binaryNode.lhs().getSymbol(), operandType);
+        //newType(binaryNode.rhs().getSymbol(), operandType);
+        newTemporary(destType, binaryNode);
+        return binaryNode;
+    }
+
+    private Node coerce(final BinaryNode binaryNode, final Type type) {
+        return coerce(binaryNode, type, type);
+    }
+
     //leave a binary node and inherit the widest type of lhs , rhs
     private Node leaveBinaryArithmetic(final BinaryNode binaryNode) {
-        if (!Compiler.shouldUseIntegerArithmetic()) {
-            newTemporary(Type.NUMBER, binaryNode);
-            return binaryNode;
-        }
-        newTemporary(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType(), Type.NUMBER), binaryNode);
-        return binaryNode;
+        assert !Compiler.shouldUseIntegerArithmetic();
+        return end(coerce(binaryNode, Type.NUMBER));
     }
 
     @Override
@@ -1089,23 +1108,17 @@
 
     @Override
     public Node leaveSAR(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        end(binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveSHL(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        end(binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveSHR(final BinaryNode binaryNode) {
-        newTemporary(Type.LONG, binaryNode);
-        end(binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.LONG));
     }
 
     @Override
@@ -1211,11 +1224,17 @@
         // type or its parameters with the widest (OBJECT) type for safety.
         functionNode.setReturnType(Type.UNKNOWN);
 
-        for (final IdentNode ident : functionNode.getParameters()) {
-            addLocalDef(ident.getName());
-            final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident);
+        for (final IdentNode param : functionNode.getParameters()) {
+            addLocalDef(param.getName());
+            final Symbol paramSymbol = functionNode.defineSymbol(param.getName(), IS_PARAM, param);
             if (paramSymbol != null) {
-                newType(paramSymbol, Type.UNKNOWN);
+                final Type callSiteParamType = functionNode.getSpecializedType(param);
+                if (callSiteParamType != null) {
+                    LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that.");
+
+                    System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that.");
+                }
+                newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
             }
 
             LOG.info("Initialized param " + paramSymbol);
@@ -1229,36 +1248,29 @@
      * @param functionNode functionNode
      */
     private static void finalizeParameters(final FunctionNode functionNode) {
-        boolean nonObjectParams = false;
-        List<Type> paramSpecializations = new ArrayList<>();
+        final boolean isVarArg = functionNode.isVarArg();
 
         for (final IdentNode ident : functionNode.getParameters()) {
             final Symbol paramSymbol = ident.getSymbol();
-            if (paramSymbol != null) {
-                Type type = paramSymbol.getSymbolType();
-                if (type.isUnknown()) {
-                    type = Type.OBJECT;
-                }
-                paramSpecializations.add(type);
-                if (!type.isObject()) {
-                    nonObjectParams = true;
-                }
-                newType(paramSymbol, Type.OBJECT);
+
+            assert paramSymbol != null;
+            Type type = functionNode.getSpecializedType(ident);
+            if (type == null) {
+                type = Type.OBJECT;
             }
-        }
 
-        if (!nonObjectParams) {
-            paramSpecializations = null;
-            // Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters
-            // here. If the callee has parameter specializations, we can regenerate it with those particular types for speed.
-        } else {
-            LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations);
-        }
+            // if we know that a parameter is only used as a certain type throughout
+            // this function, we can tell the runtime system that no matter what the
+            // call site is, use this information. TODO
+            if (!paramSymbol.getSymbolType().isObject()) {
+                LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType());
+            }
 
-        // parameters should not be slots for a function that uses variable arity signature
-        if (functionNode.isVarArg()) {
-            for (final IdentNode param : functionNode.getParameters()) {
-                param.getSymbol().setNeedsSlot(false);
+            newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
+
+            // parameters should not be slots for a function that uses variable arity signature
+            if (isVarArg) {
+                paramSymbol.setNeedsSlot(false);
             }
         }
     }
@@ -1548,6 +1560,39 @@
         localUses.add(name);
     }
 
+    /**
+     * Pessimistically promote all symbols in current function node to Object types
+     * This is done when the function contains unevaluated black boxes such as
+     * lazy sub-function nodes that have not been compiled.
+     *
+     * @param functionNode function node in whose scope symbols should conservatively be made objects
+     */
+    private static void objectifySymbols(final FunctionNode functionNode) {
+        functionNode.accept(new NodeVisitor() {
+            private void toObject(final Block block) {
+                for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) {
+                    final Symbol symbol = iter.next();
+                    newType(symbol, Type.OBJECT);
+                }
+            }
+
+            @Override
+            public Node enter(final Block block) {
+                toObject(block);
+                return block;
+            }
+
+            @Override
+            public Node enter(final FunctionNode node) {
+                toObject(node);
+                if (node.isLazy()) {
+                    return null;
+                }
+                return node;
+            }
+        });
+    }
+
     private static String name(final Node node) {
         final String cn = node.getClass().getName();
         int lastDot = cn.lastIndexOf('.');
--- a/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Tue Mar 12 15:30:53 2013 +0100
@@ -32,7 +32,6 @@
 import static jdk.nashorn.internal.codegen.Condition.LT;
 import static jdk.nashorn.internal.codegen.Condition.NE;
 
-import jdk.nashorn.internal.codegen.Label;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Node;
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Mar 12 15:30:53 2013 +0100
@@ -25,10 +25,8 @@
 
 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;
@@ -50,7 +48,6 @@
 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;
@@ -84,6 +81,7 @@
 import jdk.nashorn.internal.ir.IndexNode;
 import jdk.nashorn.internal.ir.LineNumberNode;
 import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
 import jdk.nashorn.internal.ir.Node;
@@ -108,14 +106,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.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 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;
@@ -149,8 +146,6 @@
     /** 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;
@@ -982,6 +977,7 @@
         method.label(functionNode.getEntryLabel());
 
         initLocals(functionNode);
+        functionNode.setState(CompilationState.EMITTED);
 
         return functionNode;
     }
@@ -3234,42 +3230,23 @@
     }
 
     private void newFunctionObject(final FunctionNode functionNode) {
-        final boolean isLazy = functionNode.isLazy();
-        final Class<?>[] cparams = new Class<?>[] { ScriptFunctionData.class, ScriptObject.class, MethodHandle.class };
+        final boolean    isLazy  = functionNode.isLazy();
+        final Class<?>[] cparams = new Class<?>[] { RecompilableScriptFunctionData.class, ScriptObject.class };
 
         new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
             @Override
-            protected 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()));
+            protected void makeObject(final MethodEmitter m) {
+                final String className = SCRIPTFUNCTION_IMPL_OBJECT;
+
+                m._new(className).dup();
+                loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
 
                 if (isLazy || functionNode.needsParentScope()) {
-                    method.loadScope();
+                    m.loadScope();
                 } else {
-                    method.loadNull();
+                    m.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()])));
+                m.invoke(constructorNoLookup(className, cparams));
             }
         }.makeObject(method);
     }
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Tue Mar 12 15:30:53 2013 +0100
@@ -2,10 +2,10 @@
 
 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.PARSED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
 
 import java.io.File;
@@ -39,7 +39,7 @@
      * default policy. The will get trampolines and only be generated when
      * called
      */
-    LAZY_INITIALIZATION_PHASE(EnumSet.of(FunctionNode.CompilationState.INITIALIZED)) {
+    LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
 
@@ -79,9 +79,11 @@
                     if (node == outermostFunctionNode) {
                         return node;
                     }
-                    assert Compiler.LAZY_JIT;
+                    assert compiler.isLazy();
                     lazy.add(node);
 
+                    //also needs scope, potentially needs arguments etc etc
+
                     return node;
                 }
             });
@@ -113,7 +115,7 @@
      * Constant folding pass
      *   Simple constant folding that will make elementary constructs go away
      */
-    CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED), CONSTANT_FOLDED) {
+    CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
             fn.accept(new FoldConstants());
@@ -134,7 +136,7 @@
      *   as runtime nodes where applicable.
      *
      */
-    LOWERING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED), LOWERED) {
+    LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
             fn.accept(new Lower());
@@ -150,19 +152,10 @@
      * Attribution
      *   Assign symbols and types to all nodes.
      */
-    ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED), ATTR) {
+    ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
-            final ScriptEnvironment env = compiler.getEnv();
-
             fn.accept(new Attr());
-            if (env._print_lower_ast) {
-                env.getErr().println(new ASTWriter(fn));
-            }
-
-            if (env._print_lower_parse) {
-                env.getErr().println(new PrintVisitor(fn));
-           }
         }
 
         @Override
@@ -178,7 +171,7 @@
      *   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) {
+    SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
             final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
@@ -212,10 +205,20 @@
      * 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) {
+    TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
+            final ScriptEnvironment env = compiler.getEnv();
+
             fn.accept(new FinalizeTypes());
+
+            if (env._print_lower_ast) {
+                env.getErr().println(new ASTWriter(fn));
+            }
+
+            if (env._print_lower_parse) {
+                env.getErr().println(new PrintVisitor(fn));
+           }
         }
 
         @Override
@@ -229,7 +232,7 @@
      *
      *   Generate the byte code class(es) resulting from the compiled FunctionNode
      */
-    BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED), EMITTED) {
+    BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
             final ScriptEnvironment env = compiler.getEnv();
@@ -306,18 +309,12 @@
     };
 
     private final EnumSet<CompilationState> pre;
-    private final CompilationState post;
     private long startTime;
     private long endTime;
     private boolean isFinished;
 
     private CompilationPhase(final EnumSet<CompilationState> pre) {
-        this(pre, null);
-    }
-
-    private CompilationPhase(final EnumSet<CompilationState> pre, final CompilationState post) {
-        this.pre  = pre;
-        this.post = post;
+        this.pre = pre;
     }
 
     boolean isApplicable(final FunctionNode functionNode) {
@@ -343,10 +340,6 @@
         endTime = System.currentTimeMillis();
         Timing.accumulateTime(toString(), endTime - startTime);
 
-        if (post != null) {
-            functionNode.setState(post);
-        }
-
         isFinished = true;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java	Tue Mar 12 15:30:53 2013 +0100
@@ -37,6 +37,8 @@
 
     private long weight;
 
+    private Class<?> clazz;
+
     CompileUnit(final String className, final ClassEmitter classEmitter) {
         this(className, classEmitter, 0L);
     }
@@ -48,6 +50,24 @@
     }
 
     /**
+     * Return the class that contains the code for this unit, null if not
+     * generated yet
+     *
+     * @return class with compile unit code
+     */
+    public Class<?> getCode() {
+        return clazz;
+    }
+
+    /**
+     * Set class when it exists. Only accessible from compiler
+     * @param clazz class with code for this compile unit
+     */
+    void setCode(final Class<?> clazz) {
+        this.clazz = clazz;
+    }
+
+    /**
      * Add weight to this compile unit
      * @param w weight to add
      */
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Tue Mar 12 15:30:53 2013 +0100
@@ -45,11 +45,15 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.logging.Level;
+
 import jdk.internal.dynalink.support.NameCodec;
 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.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
@@ -71,8 +75,6 @@
     /** Name of the objects package */
     public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
 
-    static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy");
-
     private final Map<String, byte[]> bytecode;
 
     private final Set<CompileUnit> compileUnits;
@@ -164,7 +166,7 @@
      * and JIT it at once. This can lead to long startup time and fewer type
      * specializations
      */
-    final static CompilationSequence SEQUENCE_NORMAL = new CompilationSequence(
+    final static CompilationSequence SEQUENCE_EAGER = new CompilationSequence(
         CompilationPhase.CONSTANT_FOLDING_PHASE,
         CompilationPhase.LOWERING_PHASE,
         CompilationPhase.ATTRIBUTION_PHASE,
@@ -173,12 +175,15 @@
         CompilationPhase.BYTECODE_GENERATION_PHASE);
 
     final static CompilationSequence SEQUENCE_LAZY =
-        SEQUENCE_NORMAL.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
+        SEQUENCE_EAGER.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
 
-    final static CompilationSequence SEQUENCE_DEFAULT =
-        LAZY_JIT ?
-            SEQUENCE_LAZY :
-            SEQUENCE_NORMAL;
+    private static CompilationSequence sequence(final boolean lazy) {
+        return lazy ? SEQUENCE_LAZY : SEQUENCE_EAGER;
+    }
+
+    boolean isLazy() {
+        return sequence == SEQUENCE_LAZY;
+    }
 
     private static String lazyTag(final FunctionNode functionNode) {
         if (functionNode.isLazy()) {
@@ -213,10 +218,7 @@
 
         this.scriptName = sb.toString();
 
-        LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'");
-        if (functionNode.isLazy()) {
-            LOG.info(">>> This is a lazy recompilation triggered by a trampoline");
-        }
+        LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "' lazy=" + functionNode.isLazy());
     }
 
     /**
@@ -227,7 +229,7 @@
      * @param strict       should this compilation use strict mode semantics
      */
     public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final boolean strict) {
-        this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, strict);
+        this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), strict);
     }
 
     /**
@@ -237,7 +239,7 @@
      * @param functionNode function node (in any available {@link CompilationState}) to compile
      */
     public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode) {
-        this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, installer.getOwner()._strict);
+        this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
     }
 
     /**
@@ -247,28 +249,104 @@
      * @param functionNode functionNode to compile
      */
     public Compiler(final ScriptEnvironment env, final FunctionNode functionNode) {
-        this(env, null, functionNode, SEQUENCE_DEFAULT, env._strict);
+        this(env, null, functionNode, sequence(env._lazy_compilation), env._strict);
+    }
+
+    /**
+     * Execute the compilation this Compiler was created with
+     * @params param types if known, for specialization
+     * @throws CompilationException if something goes wrong
+     * @return this compiler, for possible chaining
+     */
+    public Compiler compile() throws CompilationException {
+        return compile(null);
     }
 
     /**
      * Execute the compilation this Compiler was created with
+     * @param paramTypes param types if known, for specialization
      * @throws CompilationException if something goes wrong
+     * @return this compiler, for possible chaining
      */
-    public void compile() throws CompilationException {
+    public Compiler compile(final Class<?> paramTypes) throws CompilationException {
         for (final String reservedName : RESERVED_NAMES) {
             functionNode.uniqueName(reservedName);
         }
 
+        final boolean fine = !LOG.levelAbove(Level.FINE);
+        final boolean info = !LOG.levelAbove(Level.INFO);
+
+        long time = 0L;
+
         for (final CompilationPhase phase : sequence) {
             phase.apply(this, functionNode);
-            final String end = phase.toString() + " done for function '" + functionNode.getName() + "'";
-            if (Timing.isEnabled()) {
-                final long duration = phase.getEndTime() - phase.getStartTime();
-                LOG.info(end + " in " + duration + " ms");
-            } else {
-                LOG.info(end);
+
+            final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
+            time += duration;
+
+            if (fine) {
+                final StringBuilder sb = new StringBuilder();
+
+                sb.append(phase.toString()).
+                    append(" done for function '").
+                    append(functionNode.getName()).
+                    append('\'');
+
+                if (duration > 0L) {
+                    sb.append(" in ").
+                        append(duration).
+                        append(" ms ");
+                }
+
+                LOG.fine(sb.toString());
             }
         }
+
+        if (info) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("Compile job for '").
+                append(functionNode.getName()).
+                append("' finished");
+
+            if (time > 0L) {
+                sb.append(" in ").
+                    append(time).
+                    append(" ms");
+            }
+
+            LOG.info(sb.toString());
+        }
+
+        return this;
+    }
+
+    private Class<?> install(final String className, final byte[] code) {
+        LOG.fine("Installing class " + className);
+
+        final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
+
+        try {
+            final Source   source    = getSource();
+            final Object[] constants = getConstantData().toArray();
+            // Need doPrivileged because these fields are private
+            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+                @Override
+                public Void run() throws Exception {
+                    //use reflection to write source and constants table to installed classes
+                    final Field sourceField    = clazz.getDeclaredField(SOURCE.tag());
+                    final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
+                    sourceField.setAccessible(true);
+                    constantsField.setAccessible(true);
+                    sourceField.set(null, source);
+                    constantsField.set(null, constants);
+                    return null;
+                }
+            });
+        } catch (final PrivilegedActionException e) {
+            throw new RuntimeException(e);
+        }
+
+        return clazz;
     }
 
     /**
@@ -280,42 +358,38 @@
 
         assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
 
-        Class<?> rootClass = null;
+        final Map<String, Class<?>> installedClasses = new HashMap<>();
+
+        final String   rootClassName = firstCompileUnitName();
+        final Class<?> rootClass     = install(rootClassName, bytecode.get(rootClassName));
+
+        installedClasses.put(rootClassName, rootClass);
 
         for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
-            final String     className = entry.getKey();
-            LOG.fine("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;
+            final String className = entry.getKey();
+            if (className.equals(rootClassName)) {
+                continue;
             }
+            installedClasses.put(className, install(className, entry.getValue()));
+        }
 
-            try {
-                final Source source = getSource();
-                final Object[] constants = getConstantData().toArray();
-                // Need doPrivileged because these fields are private
-                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
-                    @Override
-                    public Void run() throws Exception {
-                        //use reflection to write source and constants table to installed classes
-                        final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
-                        final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
-                        sourceField.setAccessible(true);
-                        constantsField.setAccessible(true);
-                        sourceField.set(null, source);
-                        constantsField.set(null, constants);
-                        return null;
-                    }
-                });
-            } catch (final PrivilegedActionException e) {
-                throw new RuntimeException(e);
-            }
+        for (final CompileUnit unit : compileUnits) {
+            unit.setCode(installedClasses.get(unit.getUnitClassName()));
         }
 
+        functionNode.accept(new NodeVisitor() {
+            @Override
+            public Node enter(final FunctionNode node) {
+                if (node.isLazy()) {
+                    return null;
+                }
+                node.setState(CompilationState.INSTALLED);
+                return node;
+            }
+        });
+
         LOG.info("Installed root class: " + rootClass + " and " + bytecode.size() + " compile unit classes");
+
         if (Timing.isEnabled()) {
             final long duration = System.currentTimeMillis() - t0;
             Timing.accumulateTime("[Code Installation]", duration);
@@ -444,8 +518,6 @@
      * TODO: We currently generate no overflow checks so this is
      * disabled
      *
-     * @see #shouldUseIntegers()
-     *
      * @return true if arithmetic operations should not widen integer
      *   operands by default.
      */
@@ -460,4 +532,5 @@
         assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
     }
 
+
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue Mar 12 15:30:53 2013 +0100
@@ -34,6 +34,7 @@
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.CallNode;
 import jdk.nashorn.internal.ir.CallNode.EvalArgs;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.CaseNode;
 import jdk.nashorn.internal.ir.CatchNode;
 import jdk.nashorn.internal.ir.DoWhileNode;
@@ -235,19 +236,19 @@
 
     @Override
     public Node leaveBIT_AND(BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : binaryNode.getSymbol();
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
     @Override
     public Node leaveBIT_OR(BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
     @Override
     public Node leaveBIT_XOR(BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
@@ -255,7 +256,7 @@
     public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
         assert binaryNode.getSymbol() != null;
         binaryNode.setRHS(discard(binaryNode.rhs()));
-        // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
+        // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
         // in that case, update the node type as well
         propagateType(binaryNode, binaryNode.lhs().getType());
         return binaryNode;
@@ -344,7 +345,7 @@
 
     @Override
     public Node leaveSHR(final BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong();
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong() : "long coercion expected: " + binaryNode.getSymbol();
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
@@ -432,6 +433,8 @@
         }
 
         updateSymbols(functionNode);
+        functionNode.setState(CompilationState.FINALIZED);
+
         return functionNode;
     }
 
@@ -511,7 +514,6 @@
 
     @Override
     public Node leave(final VarNode varNode) {
-
         final Node rhs = varNode.getInit();
         if (rhs != null) {
             Type destType = specialize(varNode);
--- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java	Tue Mar 12 15:30:53 2013 +0100
@@ -30,11 +30,13 @@
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.EmptyNode;
 import jdk.nashorn.internal.ir.ExecuteNode;
+import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.IfNode;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.TernaryNode;
 import jdk.nashorn.internal.ir.UnaryNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.JSType;
@@ -72,6 +74,20 @@
     }
 
     @Override
+    public Node enter(final FunctionNode functionNode) {
+        if (functionNode.isLazy()) {
+            return null;
+        }
+        return functionNode;
+    }
+
+    @Override
+    public Node leave(final FunctionNode functionNode) {
+        functionNode.setState(CompilationState.CONSTANT_FOLDED);
+        return functionNode;
+    }
+
+    @Override
     public Node leave(final IfNode ifNode) {
         final Node test = ifNode.getTest();
         if (test instanceof LiteralNode) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Tue Mar 12 15:30:53 2013 +0100
@@ -146,7 +146,7 @@
 
     /**
      * Create a function signature given a function node, using as much
-     * type information for parameters and return types that is availabe
+     * type information for parameters and return types that is available
      *
      * @param functionNode the function node
      */
@@ -202,6 +202,14 @@
         return methodType;
     }
 
+    /**
+     * Return the return type for this function signature
+     * @return the return type
+     */
+    public Type getReturnType() {
+        return returnType;
+    }
+
     private static Type[] objectArgs(final int nArgs) {
         final Type[] array = new Type[nArgs];
         for (int i = 0; i < nArgs; i++) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Tue Mar 12 15:30:53 2013 +0100
@@ -69,6 +69,7 @@
 import jdk.nashorn.internal.ir.VarNode;
 import jdk.nashorn.internal.ir.WhileNode;
 import jdk.nashorn.internal.ir.WithNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Token;
@@ -332,6 +333,8 @@
         LOG.info("END FunctionNode: " + functionNode.getName());
         unnest(functionNode);
 
+        functionNode.setState(CompilationState.LOWERED);
+
         return null;
     }
 
@@ -636,7 +639,7 @@
             } else if (conservativeAlwaysTrue(test)) {
                 node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
                 ((ForNode)node).setBody(body);
-                ((ForNode)node).accept(this);
+                node.accept(this);
                 setTerminal(node, !escapes);
             }
         }
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java	Tue Mar 12 15:30:53 2013 +0100
@@ -39,6 +39,7 @@
 import jdk.nashorn.internal.ir.DoWhileNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.LabelNode;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
@@ -92,15 +93,16 @@
      */
     void split() {
         if (functionNode.isLazy()) {
-            LOG.fine("Postponing split of '" + functionNode.getName() + "' as it's lazy");
+            LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy");
             return;
         }
-        LOG.fine("Initiating split of '" + functionNode.getName() + "'");
+
+        LOG.finest("Initiating split of '" + functionNode.getName() + "'");
 
         long weight = WeighNodes.weigh(functionNode);
 
         if (weight >= SPLIT_THRESHOLD) {
-            LOG.fine("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
+            LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
 
             functionNode.accept(this);
 
@@ -133,6 +135,8 @@
         for (final FunctionNode function : functionNode.getFunctions()) {
             new Splitter(compiler, function, outermostCompileUnit).split();
         }
+
+        functionNode.setState(CompilationState.SPLIT);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java	Tue Mar 12 15:30:53 2013 +0100
@@ -647,21 +647,20 @@
     }
 
     private static void swap(final MethodVisitor method, final Type above, final Type below) {
-        final MethodVisitor mv = method;
         if (below.isCategory2()) {
             if (above.isCategory2()) {
-                mv.visitInsn(DUP2_X2);
-                mv.visitInsn(POP2);
+                method.visitInsn(DUP2_X2);
+                method.visitInsn(POP2);
             } else {
-                mv.visitInsn(DUP_X2);
-                mv.visitInsn(POP);
+                method.visitInsn(DUP_X2);
+                method.visitInsn(POP);
             }
         } else {
             if (above.isCategory2()) {
-                mv.visitInsn(DUP2_X1);
-                mv.visitInsn(POP2);
+                method.visitInsn(DUP2_X1);
+                method.visitInsn(POP2);
             } else {
-                mv.visitInsn(SWAP);
+                method.visitInsn(SWAP);
             }
         }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java	Tue Mar 12 15:30:53 2013 +0100
@@ -38,26 +38,25 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import jdk.nashorn.internal.codegen.Frame;
 import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.ir.annotations.ParentNode;
+import jdk.nashorn.internal.ir.annotations.Reference;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.Source;
 
 /**
  * IR representation for a list of statements and functions. All provides the
  * basis for script body.
- *
  */
 public class Block extends Node {
     /** Parent context */
-    @ParentNode @Ignore
+    @Reference
     private Block parent;
 
-    /** Owning function. */
-    @Ignore //don't print it, it is apparent in the tree
+    /** Owning function - a FunctionNode has itself as function */
+    @Reference
     protected FunctionNode function;
 
     /** List of statements */
@@ -274,6 +273,14 @@
     }
 
     /**
+     * Get an iterator for all the symbols defined in this block
+     * @return symbol iterator
+     */
+    public Iterator<Symbol> symbolIterator() {
+        return symbols.values().iterator();
+    }
+
+    /**
      * Search for symbol.
      *
      * @param name Symbol name.
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Tue Mar 12 15:30:53 2013 +0100
@@ -33,8 +33,10 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Stack;
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
@@ -45,13 +47,13 @@
 import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Parser;
+import jdk.nashorn.internal.runtime.Debug;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.UserAccessorProperty;
 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
 
 /**
  * IR representation for function (or script.)
- *
  */
 public class FunctionNode extends Block {
 
@@ -86,7 +88,9 @@
         /** method has had its types finalized */
         FINALIZED,
         /** method has been emitted to bytecode */
-        EMITTED
+        EMITTED,
+        /** code installed in a class loader */
+        INSTALLED
     }
 
     /** External function identifier. */
@@ -173,6 +177,9 @@
     @Ignore
     private final EnumSet<CompilationState> compilationState;
 
+    /** Type hints, e.g based on parameters at call site */
+    private final Map<IdentNode, Type> specializedTypes;
+
     /** Function flags. */
     private int flags;
 
@@ -256,22 +263,27 @@
         // constructed, so it can't be seen from other threads.
         this.function          = this;
         this.compilationState  = EnumSet.of(CompilationState.INITIALIZED);
+        this.specializedTypes  = new HashMap<>();
     }
 
     @SuppressWarnings("LeakingThisInConstructor")
     private FunctionNode(final FunctionNode functionNode, final CopyState cs) {
         super(functionNode, cs);
 
+        this.functions = new ArrayList<>();
+        for (final FunctionNode f : functionNode.getFunctions()) {
+            this.functions.add((FunctionNode)cs.existingOrCopy(f));
+        }
+
         this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident);
         this.name  = functionNode.name;
         this.kind  = functionNode.kind;
 
         this.parameters = new ArrayList<>();
         for (final IdentNode param : functionNode.getParameters()) {
-            this.parameters.add((IdentNode) cs.existingOrCopy(param));
+            this.parameters.add((IdentNode)cs.existingOrCopy(param));
         }
 
-        this.functions         = new ArrayList<>();
         this.firstToken        = functionNode.firstToken;
         this.lastToken         = functionNode.lastToken;
         this.namespace         = functionNode.getNamespace();
@@ -300,6 +312,7 @@
         this.function = this;
 
         this.compilationState = EnumSet.copyOf(functionNode.compilationState);
+        this.specializedTypes = new HashMap<>();
     }
 
     @Override
@@ -710,10 +723,14 @@
      * Check if this function's generated Java method needs a {@code callee} parameter. Functions that need access to
      * their parent scope, functions that reference themselves, and non-strict functions that need an Arguments object
      * (since it exposes {@code arguments.callee} property) will need to have a callee parameter.
+     *
+     * We also conservatively need a callee if we have lazy children, i.e. nested function nodes that have not yet
+     * been evaluated. _They_ may need the callee and we don't know it
+     *
      * @return true if the function's generated Java method needs a {@code callee} parameter.
      */
     public boolean needsCallee() {
-        return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
+        return hasLazyChildren() || needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
     }
 
     /**
@@ -919,6 +936,27 @@
     }
 
     /**
+     * Get a specialized type for an identity, if one exists
+     * @param node node to check specialized type for
+     * @return null if no specialization exists, otherwise type
+     */
+    public Type getSpecializedType(final IdentNode node) {
+        return specializedTypes.get(node);
+    }
+
+    /**
+     * Set parameter type hints for specialization.
+     * @param types types array of length equal to parameter list size
+     */
+    public void setParameterTypes(final Class<?>[] types) {
+        assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types";
+        //diff - skip the callee and this etc, they are not explicit params in the parse tree
+        for (int i = 0; i < types.length ; i++) {
+            specializedTypes.put(parameters.get(i), Type.typeFor(types[i]));
+        }
+    }
+
+    /**
      * Get the identifier for the variable in which the function return value
      * should be stored
      * @return an IdentNode representing the return value
@@ -1266,7 +1304,7 @@
      * @param parentBlock  a block to remember as parent
      */
     public void addReferencingParentBlock(final Block parentBlock) {
-        assert parentBlock.getFunction() == function.findParentFunction(); // all parent blocks must be in the same function
+        assert parentBlock.getFunction() == function.findParentFunction() : Debug.id(parentBlock.getFunction()) + "!=" + Debug.id(function.findParentFunction()); // all parent blocks must be in the same function
         if (parentBlock != function.getParent()) {
             if (referencingParentBlocks == null) {
                 referencingParentBlocks = new LinkedList<>();
--- a/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java	Tue Mar 12 15:30:53 2013 +0100
@@ -28,7 +28,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.Source;
 
@@ -36,9 +35,6 @@
  * IR representation of an object literal.
  */
 public class ObjectNode extends Node {
-    /** Literal context. */
-    @Ignore
-    private Block context;
 
     /** Literal elements. */
     private final List<Node> elements;
@@ -49,13 +45,11 @@
      * @param source   the source
      * @param token    token
      * @param finish   finish
-     * @param context  the block for this ObjectNode
      * @param elements the elements used to initialize this ObjectNode
      */
-    public ObjectNode(final Source source, final long token, final int finish, final Block context, final List<Node> elements) {
+    public ObjectNode(final Source source, final long token, final int finish, final List<Node> elements) {
         super(source, token, finish);
 
-        this.context  = context;
         this.elements = elements;
     }
 
@@ -68,7 +62,6 @@
             newElements.add(cs.existingOrCopy(element));
         }
 
-        this.context  = (Block)cs.existingOrCopy(objectNode.context);
         this.elements = newElements;
     }
 
@@ -80,10 +73,6 @@
     @Override
     public Node accept(final NodeVisitor visitor) {
         if (visitor.enter(this) != null) {
-            if (context != null) {
-                context = (Block)context.accept(visitor);
-            }
-
             for (int i = 0, count = elements.size(); i < count; i++) {
                 elements.set(i, elements.get(i).accept(visitor));
             }
@@ -117,14 +106,6 @@
     }
 
     /**
-     * Get the block that is this ObjectNode's literal context
-     * @return the block
-     */
-    public Block getContext() {
-        return context;
-    }
-
-    /**
      * Get the elements of this literal node
      * @return a list of elements
      */
--- a/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Tue Mar 12 15:30:53 2013 +0100
@@ -152,9 +152,9 @@
 
         if (isConvert) {
             convertPos = sb.length();
-            sb.append("((");
+            sb.append("(");
             sb.append(getType());
-            sb.append(")");
+            sb.append(")(");
         }
 
         if (!isPostfix && !isConvert) {
@@ -191,8 +191,6 @@
                 sb.setCharAt(convertPos, ' ');
             }
         }
-
-        //TODO - conversions still have too many parenthesis - makes --print-lower-parse hard to read
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/ir/annotations/ChildNode.java	Tue Mar 12 18:12:42 2013 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +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.ir.annotations;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This is a child node, a real node, not a reference, to an IR node that should
- * be traversed.
- * <p>
- * TODO Currently not in use.  Would make e.g. accept methods simple and unified
- * @see jdk.nashorn.internal.ir.Node
- */
-@Retention(value=RetentionPolicy.RUNTIME)
-public @interface ChildNode {
-    /** order of traversal compared to other children */
-    public int order() default -1;
-}
--- a/nashorn/src/jdk/nashorn/internal/ir/annotations/ParentNode.java	Tue Mar 12 18:12:42 2013 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +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.ir.annotations;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Signifies a parent of a node, i.e. node that should not be traversed if we
- * go down the AST. In automatic parsing this can be handled by @Reference
- * annotations instead, as all parents are references.
- * <p>
- * TODO The use case is automating and creating one implementation of something like
- * Node.getParent()
- *
- * @see jdk.nashorn.internal.ir.Node
- */
-@Retention(value=RetentionPolicy.RUNTIME)
-public @interface ParentNode {
-    // EMPTY
-}
--- a/nashorn/src/jdk/nashorn/internal/ir/annotations/Reference.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/annotations/Reference.java	Tue Mar 12 15:30:53 2013 +0100
@@ -33,7 +33,6 @@
  * AST traversal and cloning. Cloning currently as a rule uses
  * existingOrSame for references and otherwise existingOrCopy
  * <p>
- * TODO this could probably be automated using the @Reference annotation.
  */
 
 @Retention(value=RetentionPolicy.RUNTIME)
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Tue Mar 12 15:30:53 2013 +0100
@@ -27,6 +27,7 @@
 
 import java.lang.reflect.Field;
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Deque;
 import java.util.Iterator;
@@ -36,10 +37,10 @@
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.TernaryNode;
 import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.ir.annotations.ParentNode;
 import jdk.nashorn.internal.ir.annotations.Reference;
 import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
 
 /**
  * AST-as-text visualizer. Sometimes you want tree form and not source
@@ -47,7 +48,6 @@
  *
  * see the flags --print-ast and --print-ast-lower
  */
-
 public final class ASTWriter {
     /** Root node from which to start the traversal */
     private final Node root;
@@ -71,12 +71,22 @@
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
-        printAST(sb, null, "root", root, 0);
+        printAST(sb, null, null, "root", root, 0);
         return sb.toString();
     }
 
+    /**
+     * Return the visited nodes in an ordered list
+     * @return the list of nodes in order
+     */
+    public Node[] toArray() {
+        final List<Node> preorder = new ArrayList<>();
+        printAST(new StringBuilder(), preorder, null, "root", root, 0);
+        return preorder.toArray(new Node[preorder.size()]);
+    }
+
     @SuppressWarnings("unchecked")
-    private void printAST(final StringBuilder sb, final Field field, final String name, final Node node, final int indent) {
+    private void printAST(final StringBuilder sb, final List<Node> preorder, final Field field, final String name, final Node node, final int indent) {
         ASTWriter.indent(sb, indent);
         if (node == null) {
             sb.append("[Object ");
@@ -85,13 +95,23 @@
             return;
         }
 
+        if (preorder != null) {
+            preorder.add(node);
+        }
+
         final boolean isReference = field != null && field.getAnnotation(Reference.class) != null;
 
         Class<?> clazz = node.getClass();
         String   type  = clazz.getName();
 
         type = type.substring(type.lastIndexOf('.') + 1, type.length());
-//        type += "@" + Debug.id(node) + "#" + node.getSymbol();
+        if (isReference) {
+            type = "ref: " + type;
+        }
+        type += "@" + Debug.id(node);
+        if (node.getSymbol() != null) {
+            type += "#" + node.getSymbol();
+        }
 
         final List<Field> children = new LinkedList<>();
 
@@ -153,9 +173,7 @@
                 append('\n');
 
             for (final Field child : children) {
-                if (child.getAnnotation(ParentNode.class) != null) {
-                    continue;
-                } else if (child.getAnnotation(Ignore.class) != null) {
+                if (child.getAnnotation(Ignore.class) != null) {
                     continue;
                 }
 
@@ -168,7 +186,7 @@
                 }
 
                 if (value instanceof Node) {
-                    printAST(sb, child, child.getName(), (Node)value, indent + 1);
+                    printAST(sb, preorder, child, child.getName(), (Node)value, indent + 1);
                 } else if (value instanceof Collection) {
                     int pos = 0;
                     ASTWriter.indent(sb, indent + 1);
@@ -180,7 +198,7 @@
                         append('\n');
 
                     for (final Node member : (Collection<Node>)value) {
-                        printAST(sb, child, child.getName() + "[" + pos++ + "]", member, indent + 2);
+                        printAST(sb, preorder, child, child.getName() + "[" + pos++ + "]", member, indent + 2);
                     }
                 }
             }
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java	Tue Mar 12 15:30:53 2013 +0100
@@ -605,7 +605,7 @@
             final boolean strict    = sobj.isStrictContext();
 
             if (bulkable(sobj)) {
-                return ((NativeArray)sobj).getArray().pop();
+                return sobj.getArray().pop();
             }
 
             final long len = JSType.toUint32(sobj.getLength());
@@ -725,7 +725,7 @@
             first = sobj.get(0);
 
             if (bulkable(sobj)) {
-                ((NativeArray) sobj).getArray().shiftLeft(1);
+                sobj.getArray().shiftLeft(1);
             } else {
                 for (long k = 1; k < len; k++) {
                     sobj.set(k - 1, sobj.get(k), strict);
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java	Tue Mar 12 15:30:53 2013 +0100
@@ -162,21 +162,6 @@
     }
 
     /**
-     * Nashorn extension: get invocation handle from {@link ScriptFunction}
-     *
-     * @param self self reference
-     * @param obj script function
-     * @return the invocation handle for the given ScriptFunction
-     */
-    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
-    public static Object methodHandle(final Object self, final Object obj) {
-        if (obj instanceof ScriptFunction) {
-            return ((ScriptFunction)obj).getInvokeHandle();
-        }
-        return UNDEFINED;
-    }
-
-    /**
      * Check object identity comparison regardless of type
      *
      * @param self self reference
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeError.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeError.java	Tue Mar 12 15:30:53 2013 +0100
@@ -317,7 +317,7 @@
             return name;
         }
         // Step 10 : return name + ": " + msg
-        return (String)name + ": " + (String)msg;
+        return name + ": " + msg;
     }
 
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Tue Mar 12 15:30:53 2013 +0100
@@ -31,6 +31,7 @@
 import jdk.nashorn.internal.runtime.GlobalFunctions;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptObject;
@@ -86,8 +87,8 @@
      * @param builtin is this a built-in function
      * @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted).
      */
-    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) {
-        super(name, methodHandle, getMap(strict), scope, specs, strict, builtin, isConstructor);
+    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+        super(name, methodHandle, getMap(isStrict), scope, specs, isStrict, isBuiltin, isConstructor);
         init();
     }
 
@@ -95,14 +96,10 @@
      * Constructor called by (compiler) generated code for {@link ScriptObject}s.
      *
      * @param data static function data
-     * @param methodHandle handle for invocation
      * @param scope scope object
-     * @param allocator instance constructor for function
      */
-    public ScriptFunctionImpl(final MethodHandle methodHandle, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) {
+    public ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope) {
         super(data, getMap(data.isStrict()), scope);
-        // Set method handles in script data
-        data.setMethodHandles(methodHandle, allocator);
         init();
     }
 
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java	Tue Mar 12 18:12:42 2013 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-package jdk.nashorn.internal.objects;
-
-import static jdk.nashorn.internal.lookup.Lookup.MH;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import jdk.nashorn.internal.codegen.CompilationException;
-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.ScriptEnvironment;
-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<ScriptEnvironment> 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<ScriptEnvironment> 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() throws CompilationException {
-        final Compiler compiler = new Compiler(installer, functionNode);
-
-        compiler.compile();
-
-        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);
-        if (functionNode.needsCallee()) {
-            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) throws CompilationException {
-        Compiler.LOG.info(">>> TRAMPOLINE: Hitting trampoline for '" + functionNode.getName() + "'");
-        MethodHandle mh = compile();
-
-        Compiler.LOG.info("<<< COMPILED TO: " + mh);
-        // 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/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java	Tue Mar 12 15:30:53 2013 +0100
@@ -313,7 +313,7 @@
         }
 
         // Construct new object literal.
-        return new ObjectNode(source, objectToken, finish, null, elements);
+        return new ObjectNode(source, objectToken, finish, elements);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Tue Mar 12 15:30:53 2013 +0100
@@ -59,6 +59,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Stack;
+
 import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.codegen.Namespace;
 import jdk.nashorn.internal.ir.AccessNode;
@@ -96,7 +97,7 @@
 import jdk.nashorn.internal.ir.VarNode;
 import jdk.nashorn.internal.ir.WhileNode;
 import jdk.nashorn.internal.ir.WithNode;
-import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.JSErrorType;
@@ -116,9 +117,6 @@
     /** Is scripting mode. */
     private final boolean scripting;
 
-    /** Top level script being parsed. */
-    private FunctionNode script;
-
     /** Current function being parsed. */
     private FunctionNode function;
 
@@ -304,6 +302,7 @@
         final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, block, ident, name);
         block = function = functionBlock;
         function.setStrictMode(isStrictMode);
+        function.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
 
         return functionBlock;
     }
@@ -312,7 +311,7 @@
      * Restore the current block.
      */
     private void restoreBlock() {
-        block = block.getParent();
+        block    = block.getParent();
         function = block.getFunction();
     }
 
@@ -338,7 +337,7 @@
             popControlNode();
         }
 
-    final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
+        final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
 
         // Block closing brace.
         if (needsBraces) {
@@ -438,7 +437,7 @@
             }
 
             if (lhs instanceof IdentNode) {
-                if (! checkIdentLValue((IdentNode)lhs)) {
+                if (!checkIdentLValue((IdentNode)lhs)) {
                     return referenceError(lhs, rhs);
                 }
                 verifyStrictIdent((IdentNode)lhs, "assignment");
@@ -621,15 +620,13 @@
         // Make a fake token for the script.
         final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
         // Set up the script to append elements.
-        script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName));
-        // set kind to be SCRIPT
+
+        final FunctionNode script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName));
+
         script.setKind(FunctionNode.Kind.SCRIPT);
-        // Set the first token of the script.
         script.setFirstToken(functionToken);
-        // Gather source elements.
         sourceElements();
         expect(EOF);
-        // Set the last token of the script.
         script.setLastToken(token);
         script.setFinish(source.getLength() - 1);
 
@@ -1231,7 +1228,7 @@
                 }
 
                 if (init instanceof IdentNode) {
-                    if (! checkIdentLValue((IdentNode)init)) {
+                    if (!checkIdentLValue((IdentNode)init)) {
                         error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
                     }
                     verifyStrictIdent((IdentNode)init, "for-in iterator");
@@ -2026,7 +2023,7 @@
                 break;
 
             default:
-                if (! elision) {
+                if (!elision) {
                     error(AbstractParser.message("expected.comma", type.getNameOrType()));
                 }
                 // Add expression element.
@@ -2067,15 +2064,11 @@
         next();
 
         // Object context.
-        Block objectContext = null;
         // Prepare to accumulate elements.
         final List<Node> elements = new ArrayList<>();
         final Map<Object, PropertyNode> map = new HashMap<>();
 
-        try {
-            // Create a block for the object literal.
-            objectContext = newBlock();
-
+        // Create a block for the object literal.
             boolean commaSeen = true;
 loop:
             while (true) {
@@ -2090,97 +2083,90 @@
                     break;
 
                 default:
-                    if (! commaSeen) {
+                    if (!commaSeen) {
                         error(AbstractParser.message("expected.comma", type.getNameOrType()));
-                    }
-
-                    commaSeen = false;
-                    // Get and add the next property.
-                    final PropertyNode property = propertyAssignment();
-                    final Object key = property.getKeyName();
-                    final PropertyNode existingProperty = map.get(key);
-
-                    if (existingProperty != null) {
-                        // ECMA section 11.1.5 Object Initialiser
-                        // point # 4 on property assignment production
-                        final Node value  = property.getValue();
-                        final Node getter = property.getGetter();
-                        final Node setter = property.getSetter();
-
-                        final Node prevValue  = existingProperty.getValue();
-                        final Node prevGetter = existingProperty.getGetter();
-                        final Node prevSetter = existingProperty.getSetter();
-
-                        boolean redefinitionOk = true;
-                        // ECMA 11.1.5 strict mode restrictions
-                        if (isStrictMode) {
-                            if (value != null && prevValue != null) {
-                                redefinitionOk = false;
-                            }
-                        }
-
-                        final boolean isPrevAccessor = prevGetter != null || prevSetter != null;
-                        final boolean isAccessor = getter != null || setter != null;
-
-                        // data property redefined as accessor property
-                        if (prevValue != null && isAccessor) {
-                            redefinitionOk = false;
-                        }
-
-                        // accessor property redefined as data
-                        if (isPrevAccessor && value != null) {
+                }
+
+                commaSeen = false;
+                // Get and add the next property.
+                final PropertyNode property = propertyAssignment();
+                final Object key = property.getKeyName();
+                final PropertyNode existingProperty = map.get(key);
+
+                if (existingProperty != null) {
+                    // ECMA section 11.1.5 Object Initialiser
+                    // point # 4 on property assignment production
+                    final Node value  = property.getValue();
+                    final Node getter = property.getGetter();
+                    final Node setter = property.getSetter();
+
+                    final Node prevValue  = existingProperty.getValue();
+                    final Node prevGetter = existingProperty.getGetter();
+                    final Node prevSetter = existingProperty.getSetter();
+
+                    boolean redefinitionOk = true;
+                    // ECMA 11.1.5 strict mode restrictions
+                    if (isStrictMode) {
+                        if (value != null && prevValue != null) {
                             redefinitionOk = false;
                         }
-
-                        if (isAccessor && isPrevAccessor) {
-                            if (getter != null && prevGetter != null ||
-                                setter != null && prevSetter != null) {
-                                redefinitionOk = false;
-                            }
-                        }
-
-                        if (! redefinitionOk) {
-                            error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
-                        }
-
-                        if (value != null) {
-                            final Node existingValue = existingProperty.getValue();
-
-                            if (existingValue == null) {
-                                existingProperty.setValue(value);
-                            } else {
-                                final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
-                                existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
-                            }
-
-                            existingProperty.setGetter(null);
-                            existingProperty.setSetter(null);
+                    }
+
+                    final boolean isPrevAccessor = prevGetter != null || prevSetter != null;
+                    final boolean isAccessor = getter != null || setter != null;
+
+                    // data property redefined as accessor property
+                    if (prevValue != null && isAccessor) {
+                        redefinitionOk = false;
+                    }
+
+                    // accessor property redefined as data
+                    if (isPrevAccessor && value != null) {
+                        redefinitionOk = false;
+                    }
+
+                    if (isAccessor && isPrevAccessor) {
+                        if (getter != null && prevGetter != null ||
+                            setter != null && prevSetter != null) {
+                            redefinitionOk = false;
                         }
-
-                        if (getter != null) {
-                            existingProperty.setGetter(getter);
+                    }
+
+                    if (!redefinitionOk) {
+                        error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
+                    }
+
+                    if (value != null) {
+                        final Node existingValue = existingProperty.getValue();
+
+                        if (existingValue == null) {
+                            existingProperty.setValue(value);
+                        } else {
+                            final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
+                            existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
                         }
 
-                        if (setter != null) {
-                            existingProperty.setSetter(setter);
-                        }
-                    } else {
-                        map.put(key, property);
-                        elements.add(property);
+                        existingProperty.setGetter(null);
+                        existingProperty.setSetter(null);
                     }
 
-                    break;
+                    if (getter != null) {
+                        existingProperty.setGetter(getter);
+                    }
+
+                    if (setter != null) {
+                        existingProperty.setSetter(setter);
+                    }
+                } else {
+                    map.put(key, property);
+                    elements.add(property);
                 }
+
+                break;
             }
-        } finally {
-            restoreBlock();
         }
 
-        // Construct new object literal.
-        objectContext.setFinish(finish);
-        objectContext.setStart(Token.descPosition(objectToken));
-
-        return new ObjectNode(source, objectToken, finish, objectContext, elements);
+        return new ObjectNode(source, objectToken, finish, elements);
     }
 
     /**
@@ -2845,7 +2831,7 @@
             }
 
             if (lhs instanceof IdentNode) {
-                if (! checkIdentLValue((IdentNode)lhs)) {
+                if (!checkIdentLValue((IdentNode)lhs)) {
                     return referenceError(lhs, null);
                 }
                 verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator");
@@ -2872,7 +2858,7 @@
                     return referenceError(lhs, null);
                 }
                 if (lhs instanceof IdentNode) {
-                    if (! checkIdentLValue((IdentNode)lhs)) {
+                    if (!checkIdentLValue((IdentNode)lhs)) {
                         next();
                         return referenceError(lhs, null);
                     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Tue Mar 12 15:30:53 2013 +0100
@@ -164,7 +164,6 @@
         super(key, flags, slot);
 
         /*
-         *
          * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also
          * works in dual field mode, it only means that the property never has a primitive
          * representation.
@@ -348,11 +347,10 @@
 
     private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
         if (DEBUG_FIELDS) {
-           final MethodHandle mhd = MethodHandleFactory.addDebugPrintout(
+           return MethodHandleFactory.addDebugPrintout(
                LOG,
                mh,
                tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", forType=" + stripName(forType) + ", type=" + stripName(type) + ')');
-           return mhd;
         }
         return mh;
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java	Tue Mar 12 15:30:53 2013 +0100
@@ -25,6 +25,8 @@
 
 package jdk.nashorn.internal.runtime;
 
+import jdk.nashorn.internal.codegen.ClassEmitter;
+
 /**
  * Interface for installing classes passed to the compiler.
  * As only the code generating package (i.e. Context) knows about
@@ -52,12 +54,12 @@
      */
     public Class<?> install(final String className, final byte[] bytecode);
 
-    /*
+    /**
      * Verify generated bytecode before emission. This is called back from the
      * {@link ClassEmitter} or the {@link Compiler}. If the "--verify-code" parameter
      * hasn't been given, this is a nop
      *
-     * @param bytecode bytecode to verify
+     * @param code bytecode to verify
      */
     public void verify(final byte[] code);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,162 @@
+/*
+ * 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.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+import jdk.nashorn.internal.codegen.types.Type;
+
+/**
+ * An version of a JavaScript function, native or JavaScript.
+ * Supports lazily generating a constructor version of the invocation.
+ */
+final class CompiledFunction implements Comparable<CompiledFunction> {
+
+    private final MethodHandle invoker;
+    private MethodHandle constructor;
+
+    CompiledFunction(final MethodHandle invoker) {
+        this(invoker, null);
+    }
+
+    CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
+        this.invoker = invoker;
+        this.constructor = constructor; //isConstructor
+    }
+
+    @Override
+    public String toString() {
+        return "<invoker=" + invoker + " ctor=" + constructor + ">";
+    }
+
+    MethodHandle getInvoker() {
+        return invoker;
+    }
+
+    MethodHandle getConstructor() {
+        return constructor;
+    }
+
+    void setConstructor(final MethodHandle constructor) {
+        this.constructor = constructor;
+    }
+
+    boolean hasConstructor() {
+        return constructor != null;
+    }
+
+    MethodType type() {
+        return invoker.type();
+    }
+
+    @Override
+    public int compareTo(final CompiledFunction o) {
+        return weight() - o.weight();
+    }
+
+    private int weight() {
+        return weight(type());
+    }
+
+    private static int weight(final MethodType type) {
+        if (isVarArgsType(type)) {
+            return Integer.MAX_VALUE; //if there is a varargs it should be the heavist and last fallback
+        }
+
+        int weight = Type.typeFor(type.returnType()).getWeight();
+        for (final Class<?> paramType : type.parameterArray()) {
+            final int pweight = Type.typeFor(paramType).getWeight();
+            weight += pweight;
+        }
+        return weight;
+    }
+
+    private static boolean isVarArgsType(final MethodType type) {
+        assert type.parameterCount() >= 1 : type;
+        return type.parameterType(type.parameterCount() - 1) == Object[].class;
+    }
+
+    boolean moreGenericThan(final CompiledFunction o) {
+        return weight() > o.weight();
+    }
+
+    boolean moreGenericThan(final MethodType type) {
+        return weight() > weight(type);
+    }
+
+    /**
+     * Check whether a given method descriptor is compatible with this invocation.
+     * It is compatible if the types are narrower than the invocation type so that
+     * a semantically equivalent linkage can be performed.
+     *
+     * @param typesc
+     * @return
+     */
+    boolean typeCompatible(final MethodType type) {
+        final Class<?>[] wantedParams   = type.parameterArray();
+        final Class<?>[] existingParams = type().parameterArray();
+
+        //if we are not examining a varargs type, the number of parameters must be the same
+        if (wantedParams.length != existingParams.length && !isVarArgsType(type)) {
+            return false;
+        }
+
+        //we only go as far as the shortest array. the only chance to make this work if
+        //parameters lengths do not match is if our type ends with a varargs argument.
+        //then every trailing parameter in the given callsite can be folded into it, making
+        //us compatible (albeit slower than a direct specialization)
+        final int lastParamIndex = Math.min(wantedParams.length, existingParams.length);
+        for (int i = 0; i < lastParamIndex; i++) {
+            final Type w = Type.typeFor(wantedParams[i]);
+            final Type e = Type.typeFor(existingParams[i]);
+
+            //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
+            //we also currently don't support boolean as a javascript function callsite type.
+            //it will always box.
+            if (w.isBoolean()) {
+                return false;
+            }
+
+            //This callsite type has a vararg here. it will swallow all remaining args.
+            //for consistency, check that it's the last argument
+            if (e.isArray()) {
+                return true;
+            }
+
+            //Our arguments must be at least as wide as the wanted one, if not wider
+            if (Type.widest(w, e) != e) {
+                //e.g. this invocation takes double and callsite says "object". reject. won't fit
+                //but if invocation takes a double and callsite says "int" or "long" or "double", that's fine
+                return false;
+            }
+        }
+
+        return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
+    }
+
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,73 @@
+/*
+ * 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.runtime;
+
+import java.lang.invoke.MethodType;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+/**
+ * This is a list of code versions of a function.
+ * The list is sorted in ascending order of generic descriptors
+ */
+@SuppressWarnings("serial")
+final class CompiledFunctions extends TreeSet<CompiledFunction> {
+
+    CompiledFunction best(final MethodType type) {
+        final Iterator<CompiledFunction> iter = iterator();
+        while (iter.hasNext()) {
+            final CompiledFunction next = iter.next();
+            if (next.typeCompatible(type)) {
+                return next;
+            }
+        }
+        return mostGeneric();
+    }
+
+    boolean needsCallee() {
+        for (final CompiledFunction inv : this) {
+            assert ScriptFunctionData.needsCallee(inv.getInvoker()) == ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
+        }
+        return ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
+    }
+
+    CompiledFunction mostGeneric() {
+        return last();
+    }
+
+    /**
+     * Is the given type even more specific than this entire list? That means
+     * we have an opportunity for more specific versions of the method
+     * through lazy code generation
+     *
+     * @param type type to check against
+     * @return true if the given type is more specific than all invocations available
+     */
+    boolean isLessSpecificThan(final MethodType type) {
+        return best(type).moreGenericThan(type);
+    }
+
+
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Tue Mar 12 15:30:53 2013 +0100
@@ -80,7 +80,7 @@
 
         /**
          * Return the context for this installer
-         * @return context
+         * @return ScriptEnvironment
          */
         @Override
         public ScriptEnvironment getOwner() {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ECMAException.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ECMAException.java	Tue Mar 12 15:30:53 2013 +0100
@@ -237,7 +237,7 @@
             return (String)name;
         }
 
-        return (String)name + ": " + (String)msg;
+        return name + ": " + msg;
     }
 
     private static Throwable asThrowable(final Object obj) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java	Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,100 @@
+/*
+ * 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.runtime;
+
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
+import java.lang.invoke.MethodHandle;
+
+/**
+ * This is a subclass that represents a script function that may not be regenerated.
+ * This is used for example for bound functions and builtins.
+ */
+public final class FinalScriptFunctionData extends ScriptFunctionData {
+
+    /**
+     * Constructor - used for bind
+     *
+     * @param name          name
+     * @param arity         arity
+     * @param list          precompiled code
+     * @param isStrict      strict
+     * @param isBuiltin     builtin
+     * @param isConstructor constructor
+     */
+    FinalScriptFunctionData(final String name, int arity, CompiledFunctions functions, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+        super(name, arity, isStrict, isBuiltin, isConstructor);
+        code.addAll(functions);
+    }
+
+    /**
+     * Constructor - used from ScriptFunction. This assumes that we have code alraedy for the
+     * method (typically a native method) and possibly specializations.
+     *
+     * @param name           name
+     * @param mh             method handle for generic version of method
+     * @param specs          specializations
+     * @param isStrict       strict
+     * @param isBuiltin      builtin
+     * @param isConstructor  constructor
+     */
+    FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+        super(name, arity(mh), isStrict, isBuiltin, isConstructor);
+
+        addInvoker(mh);
+        if (specs != null) {
+            for (final MethodHandle spec : specs) {
+                addInvoker(spec);
+            }
+        }
+    }
+
+    private void addInvoker(final MethodHandle mh) {
+        boolean needsCallee = needsCallee(mh);
+        if (isConstructor(mh)) {
+            //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
+            //is too conservative a check. However, isConstructor(mh) always implies isConstructor param
+            assert isConstructor();
+            code.add(new CompiledFunction(MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor
+        } else {
+            code.add(new CompiledFunction(mh));
+        }
+    }
+
+    private static int arity(final MethodHandle mh) {
+        if (isVarArg(mh)) {
+            return -1;
+        }
+
+        //drop self, callee and boolean constructor flag to get real arity
+        return mh.type().parameterCount() - 1 - (needsCallee(mh) ? 1 : 0) - (isConstructor(mh) ? 1 : 0);
+    }
+
+    private static boolean isConstructor(final MethodHandle mh) {
+        return mh.type().parameterCount() >= 1 && mh.type().parameterType(0) == boolean.class;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,185 @@
+/*
+ * 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.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.parser.Token;
+import jdk.nashorn.internal.parser.TokenType;
+
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
+/**
+ * This is a subclass that represents a script function that may be regenerated,
+ * for example with specialization based on call site types, or lazily generated.
+ * The common denominator is that it can get new invokers during its lifespan,
+ * unlike {@link FinalScriptFunctionData}
+ */
+public final class RecompilableScriptFunctionData extends ScriptFunctionData {
+
+    private final FunctionNode functionNode;
+    private final PropertyMap  allocatorMap;
+    private final CodeInstaller<ScriptEnvironment> installer;
+    private final String allocatorClassName;
+
+    /** lazily generated allocator */
+    private MethodHandle allocator;
+
+    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+    /**
+     * Constructor - public as scripts use it
+     *
+     * @param functionNode       functionNode that represents this function code
+     * @param installer          installer for code regeneration versions of this function
+     * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
+     * @param allocatorMap       allocator map to seed instances with, when constructing
+     */
+    public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) {
+        super(functionNode.isAnonymous() ?
+                "" :
+                functionNode.getIdent().getName(),
+              functionNode.getParameters().size(),
+              functionNode.isStrictMode(),
+              false,
+              true);
+
+        this.functionNode       = functionNode;
+        this.installer          = installer;
+        this.allocatorClassName = allocatorClassName;
+        this.allocatorMap       = allocatorMap;
+    }
+
+    @Override
+    String toSource() {
+        final Source source = functionNode.getSource();
+        final long   token  = tokenFor(functionNode);
+
+        if (source != null && token != 0) {
+            return source.getString(Token.descPosition(token), Token.descLength(token));
+        }
+
+        return "function " + (name == null ? "" : name) + "() { [native code] }";
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        final Source source = functionNode.getSource();
+        final long   token  = tokenFor(functionNode);
+
+        if (source != null) {
+            sb.append(source.getName())
+                .append(':')
+                .append(source.getLine(Token.descPosition(token)))
+                .append(' ');
+        }
+
+        return sb.toString() + super.toString();
+    }
+
+    private static long tokenFor(final FunctionNode fn) {
+        final int  position   = Token.descPosition(fn.getFirstToken());
+        final int  length     = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken());
+
+        return Token.toDesc(TokenType.FUNCTION, position, length);
+    }
+
+    @Override
+    ScriptObject allocate() {
+        try {
+            ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
+            return allocator == null ? null : (ScriptObject)allocator.invokeExact(allocatorMap);
+        } catch (final RuntimeException | Error e) {
+            throw e;
+        } catch (final Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    private void ensureHasAllocator() throws ClassNotFoundException {
+        if (allocator == null && allocatorClassName != null) {
+            this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.tag(), MH.type(ScriptObject.class, PropertyMap.class));
+        }
+    }
+
+    @Override
+    protected void ensureCodeGenerated() {
+         if (!code.isEmpty()) {
+             return; // nothing to do, we have code, at least some.
+         }
+
+         // check if function node is lazy, need to compile it.
+         // note that currently function cloning is not working completely, which
+         // means that the compiler will mutate the function node it has been given
+         // once it has been compiled, it cannot be recompiled. This means that
+         // lazy compilation works (not compiled yet) but e.g. specializations won't
+         // until the copy-on-write changes for IR are in, making cloning meaningless.
+         // therefore, currently method specialization is disabled. TODO
+
+         if (functionNode.isLazy()) {
+             Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '" + functionNode.getName() + "'");
+             new Compiler(installer, functionNode).compile().install();
+
+             // we don't need to update any flags - varArgs and needsCallee are instrincic
+             // in the function world we need to get a destination node from the compile instead
+             // and replace it with our function node. TODO
+         }
+
+         // we can't get here unless we have bytecode, either from eager compilation or from
+         // running a lazy compile on the lines above
+
+         assert functionNode.hasState(CompilationState.INSTALLED);
+
+         // code exists - look it up and add it into the automatically sorted invoker list
+         code.add(
+            new CompiledFunction(
+                MH.findStatic(
+                    LOOKUP,
+                    functionNode.getCompileUnit().getCode(),
+                    functionNode.getName(),
+                    new FunctionSignature(functionNode).
+                        getMethodType())));
+    }
+
+    @Override
+    MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
+        final MethodHandle mh = super.getBestInvoker(callSiteType, args);
+        if (code.isLessSpecificThan(callSiteType)) {
+            // opportunity for code specialization - we can regenerate a better version of this method
+        }
+        return mh;
+    }
+
+}
+
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Tue Mar 12 15:30:53 2013 +0100
@@ -82,6 +82,9 @@
     /** Show full Nashorn version */
     public final boolean _fullversion;
 
+    /** Should lazy compilation take place */
+    public final boolean _lazy_compilation;
+
     /** Create a new class loaded for each compilation */
     public final boolean _loader_per_compile;
 
@@ -155,6 +158,7 @@
         _early_lvalue_error   = options.getBoolean("early.lvalue.error");
         _empty_statements     = options.getBoolean("empty.statements");
         _fullversion          = options.getBoolean("fullversion");
+        _lazy_compilation     = options.getBoolean("lazy.compilation");
         _loader_per_compile   = options.getBoolean("loader.per.compile");
         _no_syntax_extensions = options.getBoolean("no.syntax.extensions");
         _package              = options.getString("package");
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Mar 12 15:30:53 2013 +0100
@@ -33,11 +33,11 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
-import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
 import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 import jdk.nashorn.internal.runtime.linker.NashornGuards;
@@ -48,16 +48,16 @@
 public abstract class ScriptFunction extends ScriptObject {
 
     /** Method handle for prototype getter for this ScriptFunction */
-    public static final MethodHandle G$PROTOTYPE  = findOwnMH("G$prototype",  Object.class, Object.class);
+    public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class);
 
     /** Method handle for prototype setter for this ScriptFunction */
-    public static final MethodHandle S$PROTOTYPE  = findOwnMH("S$prototype",  void.class, Object.class, Object.class);
+    public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class);
 
     /** Method handle for length getter for this ScriptFunction */
-    public static final MethodHandle G$LENGTH     = findOwnMH("G$length",     int.class, Object.class);
+    public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class);
 
     /** Method handle for name getter for this ScriptFunction */
-    public static final MethodHandle G$NAME       = findOwnMH("G$name",       Object.class, Object.class);
+    public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
 
     /** Method handle for allocate function for this ScriptFunction */
     static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
@@ -67,7 +67,9 @@
     /** method handle to scope getter for this ScriptFunction */
     public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
 
-    private final ScriptFunctionData data;
+    private static final MethodHandle IS_FUNCTION_MH  = findOwnMH("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
+
+    private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
 
     /** Reference to constructor prototype. */
     protected Object prototype;
@@ -75,6 +77,8 @@
     /** The parent scope. */
     private final ScriptObject scope;
 
+    private final ScriptFunctionData data;
+
     /**
      * Constructor
      *
@@ -97,7 +101,7 @@
             final boolean builtin,
             final boolean isConstructor) {
 
-        this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope);
+        this(new FinalScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope);
     }
 
     /**
@@ -118,8 +122,8 @@
             constructorCount++;
         }
 
-        this.data = data;
-        this.scope  = scope;
+        this.data  = data;
+        this.scope = scope;
     }
 
     @Override
@@ -295,20 +299,20 @@
     /**
      * Return the most appropriate invoke handle if there are specializations
      * @param type most specific method type to look for invocation with
+     * @param callsite args for trampoline invocation
      * @return invoke method handle
      */
-    private final MethodHandle getBestInvoker(final MethodType type) {
-        return data.getBestInvoker(type);
+    private MethodHandle getBestInvoker(final MethodType type, final Object[] args) {
+        return data.getBestInvoker(type, args);
     }
 
     /**
-     * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation
-     * method handle for this ScriptFunction
-     * @see SpecializedFunction
-     * @return invokeHandle
+     * Return the most appropriate invoke handle if there are specializations
+     * @param type most specific method type to look for invocation with
+     * @return invoke method handle
      */
-    public final MethodHandle getInvokeHandle() {
-        return data.getInvoker();
+    public MethodHandle getBestInvoker(final MethodType type) {
+        return getBestInvoker(type, null);
     }
 
     /**
@@ -319,7 +323,7 @@
      * @return bound invoke handle
      */
     public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
-        return MH.bindTo(bindToCalleeIfNeeded(getInvokeHandle()), self);
+        return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self);
     }
 
     /**
@@ -329,7 +333,8 @@
      * @return the potentially bound method handle
      */
     private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) {
-        return data.needsCallee() ? MH.bindTo(methodHandle, this) : methodHandle;
+        return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle;
+
     }
 
     /**
@@ -340,15 +345,6 @@
         return data.getName();
     }
 
-    /**
-     * Does this script function need to be compiled. This determined by
-     * null checking invokeHandle
-     *
-     * @return true if this needs compilation
-     */
-    public final boolean needsCompilation() {
-        return data.getInvoker() == null;
-    }
 
     /**
      * Get the scope for this function
@@ -359,15 +355,6 @@
     }
 
     /**
-     * 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
      *
@@ -464,7 +451,7 @@
     @Override
     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
         final MethodType type = desc.getMethodType();
-        return new GuardedInvocation(pairArguments(data.getBestConstructor(type), type), null, NashornGuards.getFunctionGuard(this));
+        return new GuardedInvocation(pairArguments(data.getBestConstructor(type.changeParameterType(0, ScriptFunction.class), null), type), null, getFunctionGuard(this));
     }
 
     @SuppressWarnings("unused")
@@ -472,7 +459,7 @@
         if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
             return obj;
         }
-        return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj);
+        return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(obj);
     }
 
     /**
@@ -506,8 +493,7 @@
         MethodHandle guard = null;
 
         if (data.needsCallee()) {
-            final MethodHandle callHandle = getBestInvoker(type);
-
+            final MethodHandle callHandle = getBestInvoker(type, request.getArguments());
             if (NashornCallSiteDescriptor.isScope(desc)) {
                 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
                 // (callee, this, args...) => (callee, args...)
@@ -525,13 +511,12 @@
                     if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) {
                         boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
                     } else {
-                        guard = NashornGuards.getNonStrictFunctionGuard(this);
+                        guard = getNonStrictFunctionGuard(this);
                     }
                 }
             }
         } else {
-            final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1));
-
+            final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1), request.getArguments());
             if (NashornCallSiteDescriptor.isScope(desc)) {
                 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
                 // (this, args...) => (args...)
@@ -545,7 +530,8 @@
         }
 
         boundHandle = pairArguments(boundHandle, type);
-        return new GuardedInvocation(boundHandle, guard == null ? NashornGuards.getFunctionGuard(this) : guard);
+
+        return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this) : guard);
    }
 
     /**
@@ -554,13 +540,50 @@
      * These don't want a callee parameter, so bind that. Name binding is optional.
      */
     MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
-        return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type)), bindName), type);
+        return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type, null)), bindName), type);
     }
 
     private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
         return bindName == null ? methodHandle : MH.insertArguments(methodHandle, 1, bindName);
     }
 
+    /**
+     * Get the guard that checks if a {@link ScriptFunction} is equal to
+     * a known ScriptFunction, using reference comparison
+     *
+     * @param function The ScriptFunction to check against. This will be bound to the guard method handle
+     *
+     * @return method handle for guard
+     */
+    private static MethodHandle getFunctionGuard(final ScriptFunction function) {
+        assert function.data != null;
+        return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
+    }
+
+    /**
+     * Get a guard that checks if a {@link ScriptFunction} is equal to
+     * a known ScriptFunction using reference comparison, and whether the type of
+     * the second argument (this-object) is not a JavaScript primitive type.
+     *
+     * @param function The ScriptFunction to check against. This will be bound to the guard method handle
+     *
+     * @return method handle for guard
+     */
+    private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) {
+        assert function.data != null;
+        return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data);
+    }
+
+    @SuppressWarnings("unused")
+    private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) {
+        return self instanceof ScriptFunction && ((ScriptFunction)self).data == data;
+    }
+
+    @SuppressWarnings("unused")
+    private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) {
+        return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject;
+    }
+
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
         final Class<?>   own = ScriptFunction.class;
         final MethodType mt  = MH.type(rtype, types);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Tue Mar 12 15:30:53 2013 +0100
@@ -32,227 +32,94 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
-import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.parser.Token;
-import jdk.nashorn.internal.parser.TokenType;
+
+import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
 
 /**
  * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
  * Instances of this class are created during codegen and stored in script classes'
  * constants array to reduce function instantiation overhead during runtime.
  */
-public final class ScriptFunctionData {
-    private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
-    private static final MethodHandle NEWFILTER     = findOwnMH("newFilter", Object.class, Object.class, Object.class);
-
-    // per-function object flags
-    private static final int IS_STRICT      = 0b0000_0001;
-    private static final int IS_BUILTIN     = 0b0000_0010;
-    private static final int HAS_CALLEE     = 0b0000_0100;
-    private static final int IS_VARARGS     = 0b0000_1000;
-    private static final int IS_CONSTRUCTOR = 0b0001_0000;
+public abstract class ScriptFunctionData {
 
-    /** Name of the function or "" */
-    private final String name;
-    /** Source of this function, or null */
-    private final Source source;
-    /** Map for new instance constructor */
-    private PropertyMap  allocatorMap;
-    /** Start position and length in source */
-    private final long   token;
-    /** Number of expected arguments, either taken from FunctionNode or calculated from method handle signature*/
-    private int          arity;
-    private final int    flags;
+    /** Name of the function or "" for anonynous functions */
+    protected final String name;
+
+    /** All versions of this function that have been generated to code */
+    protected final CompiledFunctions code;
 
-    /** Reference to code for this method. */
-    private MethodHandle invoker;
-    /** Reference to code for this method when called to create "new" object. This must always be populated with a
-     * result of calling {@link #composeConstructor(MethodHandle)} on the value of the {@link #invoker} field. */
-    private MethodHandle constructor;
-    /** Constructor to create a new instance. */
-    private MethodHandle allocator;
-    /** Generic invoker to used in {@link ScriptFunction#invoke(Object, Object...)}. */
-    private MethodHandle genericInvoker;
-    /** Specializations - see @SpecializedFunction */
-    private MethodHandle[] invokeSpecializations;
-    /** Specializations - see @SpecializedFunction. Same restrictions as for {@link #constructor} apply; only populate
-     * with method handles returned from {@link #composeConstructor(MethodHandle)}. */
-    private MethodHandle[] constructSpecializations;
+    private int arity;
+
+    private final boolean isStrict;
 
-    /**
-     * Constructor
-     * @param fn the function node
-     * @param allocatorMap the allocator property map
-     */
-    public ScriptFunctionData(final FunctionNode fn, final PropertyMap allocatorMap) {
+    private final boolean isBuiltin;
 
-        final long firstToken = fn.getFirstToken();
-        final long lastToken  = fn.getLastToken();
-        final int  position   = Token.descPosition(firstToken);
-        final int  length     = Token.descPosition(lastToken) - position + Token.descLength(lastToken);
+    private final boolean isConstructor;
 
-        this.name         = fn.isAnonymous() ? "" : fn.getIdent().getName();
-        this.source       = fn.getSource();
-        this.allocatorMap = allocatorMap;
-        this.token        = Token.toDesc(TokenType.FUNCTION, position, length);
-        this.arity        = fn.getParameters().size();
-        this.flags        = makeFlags(fn.needsCallee(), fn.isVarArg(), fn.isStrictMode(), false, true);
-    }
+    private static final MethodHandle NEWFILTER     = findOwnMH("newFilter", Object.class, Object.class, Object.class);
+    private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
 
     /**
      * 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
+     * @param name          script function name
+     * @param arity         arity
+     * @param isStrict      is the function strict
+     * @param isBuiltin     is the function built in
+     * @param isConstructor is the function a constructor
      */
-    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);
+    protected ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+        this.name          = name;
+        this.arity         = arity;
+        this.code          = new CompiledFunctions();
+        this.isStrict      = isStrict;
+        this.isBuiltin     = isBuiltin;
+        this.isConstructor = isConstructor;
     }
 
-    private ScriptFunctionData(final String name, final Source source, final long token, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) {
-        this.name   = name;
-        this.source = source;
-        this.token  = token;
-
-        final boolean isVarArg = isVarArg(methodHandle);
-        final boolean needsCallee = needsCallee(methodHandle);
-
-        this.flags = makeFlags(needsCallee, isVarArg, strict, builtin, isConstructor);
-        int lArity = isVarArg ? -1 : methodHandle.type().parameterCount() - 1; //drop the self param for arity
-
-        if (needsCallee && !isVarArg) {
-            lArity--;
-        }
-
-        if (isConstructor(methodHandle)) {
-            assert isConstructor;
-            if (!isVarArg) {
-                lArity--;    // drop the boolean flag for arity
-            }
-            /*
-             * We insert a boolean argument to tell if the method was invoked as constructor or not if the method
-             * handle's first argument is boolean.
-             */
-            this.invoker     = MH.insertArguments(methodHandle, 0, false);
-            this.constructor = composeConstructor(MH.insertArguments(methodHandle, 0, true));
-
-            if (specs != null) {
-                this.invokeSpecializations    = new MethodHandle[specs.length];
-                this.constructSpecializations = new MethodHandle[specs.length];
-                for (int i = 0; i < specs.length; i++) {
-                    this.invokeSpecializations[i]    = MH.insertArguments(specs[i], 0, false);
-                    this.constructSpecializations[i] = composeConstructor(MH.insertArguments(specs[i], 0, true));
-                }
-            }
-        } else {
-            this.invoker                  = methodHandle;
-            this.constructor              = null; // delay composition of the constructor
-            this.invokeSpecializations    = specs;
-            this.constructSpecializations = null; // delay composition of the constructors
-        }
-        this.arity = lArity;
-    }
-
-    /**
-     * Get the arity of the function.
-     * @return the arity
-     */
-    int getArity() {
+    final int getArity() {
         return arity;
     }
 
     /**
-     * Set the arity of the function.
-     * @param arity the arity
+     * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final
+     * @param arity new arity
      */
-    void setArity(int arity) {
+    void setArity(final int arity) {
         this.arity = arity;
     }
 
-    /**
-     * Get the function name.
-     * @return function name
-     */
-    String getName() {
-        return name;
+    CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
+        final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args);
+
+        if (isConstructor()) {
+            ensureConstructor(originalInv);
+            return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args));
+        }
+
+        return new CompiledFunction(boundInvoker);
     }
 
     /**
-     * Get this function as a String containing its source code. If no source code
-     * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
-     * @return string representation of this function's source
+     * Is this a ScriptFunction generated with strict semantics?
+     * @return true if strict, false otherwise
      */
-    String toSource() {
-        if (source != null && token != 0) {
-            return source.getString(Token.descPosition(token), Token.descLength(token));
-        }
-
-        return "function " + (name == null ? "" : name) + "() { [native code] }";
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-
-        sb.append(super.toString())
-                .append(" [ ")
-                .append(invoker)
-                .append(", ")
-                .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
-
-        if (source != null) {
-            sb.append(" @ ")
-                    .append(source.getName())
-                    .append(':')
-                    .append(source.getLine(Token.descPosition(token)));
-        }
-        sb.append(" ]");
-
-        return sb.toString();
+    public boolean isStrict() {
+        return isStrict;
     }
 
-    /**
-     * Returns true if the function needs a callee argument.
-     * @return the needsCallee flag
-     */
-    boolean needsCallee() {
-        return (flags & HAS_CALLEE) != 0;
-    }
-
-    /**
-     * Returns true if this is a strict-mode function.
-     * @return the strict flag
-     */
-    public boolean isStrict() {
-        return (flags & IS_STRICT) != 0;
+    boolean isBuiltin() {
+        return isBuiltin;
     }
 
-    /**
-     * Returns true if this is a built-in function.
-     * @return the built-in flag
-     */
-    private boolean isBuiltin() {
-        return (flags & IS_BUILTIN) != 0;
+    boolean isConstructor() {
+        return isConstructor;
     }
 
-    /**
-     * Returns true if this function can be used as a constructor.
-     * @return the constructor flag
-     */
-    private boolean isConstructor() {
-        return (flags & IS_CONSTRUCTOR) != 0;
-    }
-
-    /**
-     * Returns true if this is a var-arg function.
-     * @return the var-arg flag
-     */
-    private boolean isVarArg() {
-        return (flags & IS_VARARGS) != 0;
+    boolean needsCallee() {
+        // we don't know if we need a callee or not unless we are generated
+        ensureCodeGenerated();
+        return code.needsCallee();
     }
 
     /**
@@ -261,127 +128,408 @@
      * @return true if this argument must be an object
      */
     boolean needsWrappedThis() {
-        return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
+        return !isStrict && !isBuiltin;
+    }
+
+    String toSource() {
+        return "function " + (name == null ? "" : name) + "() { [native code] }";
+    }
+
+    String getName() {
+        return name;
+    }
+
+    /**
+     * Get this function as a String containing its source code. If no source code
+     * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
+     *
+     * @return string representation of this function
+     */
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+
+        sb.append("name='").
+                append(name.isEmpty() ? "<anonymous>" : name).
+                append("' ").
+                append(code.size()).
+                append(" invokers=").
+                append(code);
+
+        return sb.toString();
     }
 
     /**
-     * Get the method handle used to invoke this function.
-     * @return the invoke handle
+     * Pick the best invoker, i.e. the one version of this method with as narrow and specific
+     * types as possible. If the call site arguments are objects, but boxed primitives we can
+     * also try to get a primitive version of the method and do an unboxing filter, but then
+     * we need to insert a guard that checks the argument is really always a boxed primitive
+     * and not suddenly a "real" object
+     *
+     * @param callSiteType callsite type
+     * @param args         arguments at callsite on first trampoline invocation
+     * @return method handle to best invoker
      */
-    MethodHandle getInvoker() {
-        return invoker;
+    MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
+        return getBest(callSiteType).getInvoker();
     }
 
-    MethodHandle getBestInvoker(final MethodType type) {
-        return SpecializedMethodChooser.candidateWithLowestWeight(type, invoker, invokeSpecializations);
+    MethodHandle getBestInvoker(final MethodType callSiteType) {
+        return getBestInvoker(callSiteType, null);
+    }
+
+    MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) {
+        if (!isConstructor()) {
+            throw typeError("not.a.constructor", toSource());
+        }
+        ensureCodeGenerated();
+
+        final CompiledFunction best = getBest(callSiteType);
+        ensureConstructor(best);
+        return best.getConstructor();
+    }
+
+    MethodHandle getBestConstructor(final MethodType callSiteType) {
+        return getBestConstructor(callSiteType, null);
     }
 
     /**
-     * Get the method handle used to invoke this function as a constructor.
-     * @return the constructor handle
+     * Subclass responsibility. If we can have lazy code generation, this is a hook to ensure that
+     * code exists before performing an operation.
      */
-    private MethodHandle getConstructor() {
-        if (constructor == null) {
-            constructor = composeConstructor(invoker);
-        }
+    protected void ensureCodeGenerated() {
+        //empty
+    }
 
-        return constructor;
+    /**
+     * Return a generic Object/Object invoker for this method. It will ensure code
+     * is generated, get the most generic of all versions of this function and adapt it
+     * to Objects.
+     *
+     * TODO this is only public because {@link JavaAdapterFactory} can't supply us with
+     * a MethodType that we can use for lookup due to boostrapping problems. Can be fixed
+     *
+     * @return generic invoker of this script function
+     */
+    public final MethodHandle getGenericInvoker() {
+        ensureCodeGenerated();
+        return composeGenericMethod(code.mostGeneric().getInvoker());
+    }
+
+    private CompiledFunction getBest(final MethodType callSiteType) {
+        ensureCodeGenerated();
+        return code.best(callSiteType);
     }
 
-    MethodHandle getBestConstructor(MethodType descType) {
-        if (!isConstructor()) {
-            throw typeError("not.a.constructor", toSource());
-        }
-        return SpecializedMethodChooser.candidateWithLowestWeight(descType, getConstructor(), getConstructSpecializations());
+    /**
+     * 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.
+     */
+    ScriptObject allocate() {
+        return null;
     }
 
-    private MethodHandle composeConstructor(MethodHandle ctor) {
+    /**
+     * This method is used to create the immutable portion of a bound function.
+     * See {@link ScriptFunction#makeBoundFunction(Object, Object[])}
+     *
+     * @param fn the original function being bound
+     * @param self this reference to bind. Can be null.
+     * @param args additional arguments to bind. Can be null.
+     */
+    ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
+        ensureCodeGenerated();
+
+        final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
+        final int length = args == null ? 0 : args.length;
+
+        CompiledFunctions boundList = new CompiledFunctions();
+        for (final CompiledFunction inv : code) {
+            boundList.add(bind(inv, fn, self, allArgs));
+        }
+        ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor());
+        return boundData;
+    }
+
+    /**
+     * Compose a constructor given a primordial constructor handle
+     *
+     * @param ctor         primordial constructor handle
+     * @param needsCallee  do we need to pass a callee
+     *
+     * @return the composed constructor
+     */
+    protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) {
         // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
         // "this" in the first argument position is what allows the elegant folded composition of
         // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
         // always returns Object.
-        MethodHandle composedCtor = changeReturnTypeToObject(swapCalleeAndThis(ctor));
+        MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
+
+        composedCtor = changeReturnTypeToObject(composedCtor);
 
         final MethodType ctorType = composedCtor.type();
+
         // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
         // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
         // (this, [callee, ]args...) => ([callee, ]args...)
         final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
+
         // Fold constructor into newFilter that replaces the return value from the constructor with the originally
         // allocated value when the originally allocated value is a primitive.
         // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
         composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor);
 
         // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
-        if (needsCallee()) {
+        if (needsCallee) {
             // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
             // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
             // or...
             return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE);
         }
+
         // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
         // (this, args...) filter (callee) => (callee, args...)
         return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE);
     }
 
     /**
-     * Get an adapted version of the invoker handle that only uses {@code Object} as parameter and return types.
-     * @return the generic invoke handle
+     * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
+     * method handle. If this function's method handles don't need a callee parameter, returns the original method
+     * handle unchanged.
+     *
+     * @param mh a method handle with order of arguments {@code (callee, this, args...)}
+     *
+     * @return a method handle with order of arguments {@code (this, callee, args...)}
+     */
+    private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
+        final MethodType type = mh.type();
+        assert type.parameterType(0) == ScriptFunction.class : type;
+        assert type.parameterType(1) == Object.class : type;
+        final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
+        final int[] reorder = new int[type.parameterCount()];
+        reorder[0] = 1;
+        assert reorder[1] == 0;
+        for (int i = 2; i < reorder.length; ++i) {
+            reorder[i] = i;
+        }
+        return MethodHandles.permuteArguments(mh, newType, reorder);
+    }
+
+    /**
+     * Convert this argument for non-strict functions according to ES 10.4.3
+     *
+     * @param thiz the this argument
+     *
+     * @return the converted this object
+     */
+    private Object convertThisObject(final Object thiz) {
+        if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
+            if (JSType.nullOrUndefined(thiz)) {
+                return Context.getGlobalTrusted();
+            }
+
+            if (isPrimitiveThis(thiz)) {
+                return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
+            }
+        }
+
+        return thiz;
+    }
+
+    static boolean isPrimitiveThis(final Object obj) {
+        return obj instanceof String || obj instanceof ConsString ||
+               obj instanceof Number || obj instanceof Boolean;
+    }
+
+    /**
+     * Creates an invoker method handle for a bound function.
+     *
+     * @param targetFn the function being bound
+     * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
+     * any of its specializations.
+     * @param self the "this" value being bound
+     * @param args additional arguments being bound
+     *
+     * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
+     * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
+     * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
+     * to the original invoker on invocation.
      */
-    private MethodHandle getGenericInvoker() {
-        if (genericInvoker == null) {
-            assert invoker != null : "invoker is null";
-            genericInvoker = makeGenericMethod(invoker);
+    private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
+        // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
+        // in the target and will be ignored anyway.
+        final boolean isTargetBound = targetFn.isBoundFunction();
+
+        final boolean needsCallee = needsCallee(originalInvoker);
+        assert needsCallee == needsCallee() : "callee contract violation 2";
+        assert !(isTargetBound && needsCallee); // already bound functions don't need a callee
+
+        final Object boundSelf = isTargetBound ? null : convertThisObject(self);
+        final MethodHandle boundInvoker;
+
+        if (isVarArg(originalInvoker)) {
+            // First, bind callee and this without arguments
+            final MethodHandle noArgBoundInvoker;
+
+            if (isTargetBound) {
+                // Don't bind either callee or this
+                noArgBoundInvoker = originalInvoker;
+            } else if (needsCallee) {
+                // Bind callee and this
+                noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
+            } else {
+                // Only bind this
+                noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
+            }
+            // Now bind arguments
+            if (args.length > 0) {
+                boundInvoker = varArgBinder(noArgBoundInvoker, args);
+            } else {
+                boundInvoker = noArgBoundInvoker;
+            }
+        } else {
+            final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(), args.length + (isTargetBound ? 0 : (needsCallee  ? 2 : 1)))];
+            int next = 0;
+            if (!isTargetBound) {
+                if (needsCallee) {
+                    boundArgs[next++] = targetFn;
+                }
+                boundArgs[next++] = boundSelf;
+            }
+            // If more bound args were specified than the function can take, we'll just drop those.
+            System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
+            // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
+            // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
+            // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
+            // start at position 1. If the function is not bound, we start inserting arguments at position 0.
+            boundInvoker = MH.insertArguments(originalInvoker, isTargetBound ? 1 : 0, boundArgs);
+        }
+
+        if (isTargetBound) {
+            return boundInvoker;
         }
-        return genericInvoker;
+
+        // If the target is not already bound, add a dropArguments that'll throw away the passed this
+        return MH.dropArguments(boundInvoker, 0, Object.class);
+    }
+
+    /**
+     * Creates a constructor method handle for a bound function using the passed constructor handle.
+     *
+     * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
+     * @param fn the function being bound
+     * @param args arguments being bound
+     *
+     * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
+     * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
+     * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
+     * this script function data object has no constructor handle, null is returned.
+     */
+    private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
+        assert originalConstructor != null;
+
+        // If target function is already bound, don't bother binding the callee.
+        final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
+            MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
+
+        if (args.length == 0) {
+            return calleeBoundConstructor;
+        }
+
+        if (isVarArg(calleeBoundConstructor)) {
+            return varArgBinder(calleeBoundConstructor, args);
+        }
+
+        final Object[] boundArgs;
+
+        final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
+        if (args.length <= maxArgCount) {
+            boundArgs = args;
+        } else {
+            boundArgs = new Object[maxArgCount];
+            System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
+        }
+
+        return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
+    }
+
+    /**
+     * Takes a method handle, and returns a potentially different method handle that can be used in
+     * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
+     * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
+     * {@code Object} as well, except for the following ones:
+     * <ul>
+     *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
+     *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
+     *   (callee) as an argument.</li>
+     * </ul>
+     *
+     * @param mh the original method handle
+     *
+     * @return the new handle, conforming to the rules above.
+     */
+    protected MethodHandle composeGenericMethod(final MethodHandle mh) {
+        final MethodType type = mh.type();
+        MethodType newType = type.generic();
+        if (isVarArg(mh)) {
+            newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
+        }
+        if (needsCallee(mh)) {
+            newType = newType.changeParameterType(0, ScriptFunction.class);
+        }
+        return type.equals(newType) ? mh : mh.asType(newType);
     }
 
     /**
      * Execute this script function.
+     *
      * @param self  Target object.
      * @param arguments  Call arguments.
      * @return ScriptFunction result.
+     *
      * @throws Throwable if there is an exception/error with the invocation or thrown from it
      */
     Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
-        final MethodHandle genInvoker = getGenericInvoker();
-        final Object selfObj = convertThisObject(self);
-        final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
+        final MethodHandle mh = getGenericInvoker();
+
+        final Object       selfObj    = convertThisObject(self);
+        final Object[]     args       = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 
-        if (isVarArg()) {
-            if (needsCallee()) {
-                return genInvoker.invokeExact(fn, selfObj, args);
+        if (isVarArg(mh)) {
+            if (needsCallee(mh)) {
+                return mh.invokeExact(fn, selfObj, args);
             }
-            return genInvoker.invokeExact(selfObj, args);
+            return mh.invokeExact(selfObj, args);
         }
 
-        final int paramCount = genInvoker.type().parameterCount();
-        if (needsCallee()) {
+        final int paramCount = mh.type().parameterCount();
+        if (needsCallee(mh)) {
             switch (paramCount) {
             case 2:
-                return genInvoker.invokeExact(fn, selfObj);
+                return mh.invokeExact(fn, selfObj);
             case 3:
-                return genInvoker.invokeExact(fn, selfObj, getArg(args, 0));
+                return mh.invokeExact(fn, selfObj, getArg(args, 0));
             case 4:
-                return genInvoker.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
+                return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
             case 5:
-                return genInvoker.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+                return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
             default:
-                return genInvoker.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
+                return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
             }
         }
 
         switch (paramCount) {
         case 1:
-            return genInvoker.invokeExact(selfObj);
+            return mh.invokeExact(selfObj);
         case 2:
-            return genInvoker.invokeExact(selfObj, getArg(args, 0));
+            return mh.invokeExact(selfObj, getArg(args, 0));
         case 3:
-            return genInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
+            return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
         case 4:
-            return genInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+            return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
         default:
-            return genInvoker.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
+            return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
         }
     }
 
@@ -389,15 +537,13 @@
         return i < args.length ? args[i] : UNDEFINED;
     }
 
-    private Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
+    private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
         final Object[] finalArgs = new Object[argCount];
 
         int nextArg = 0;
-        if (needsCallee()) {
-            assert fn != null;
+        if (fn != null) {
+            //needs callee
             finalArgs[nextArg++] = fn;
-        } else {
-            assert fn == null;
         }
         finalArgs[nextArg++] = self;
 
@@ -413,255 +559,14 @@
 
         return finalArgs;
     }
-
-    /**
-     * Get the specialized construct handles for this function.
-     * @return array of specialized construct handles
-     */
-    private MethodHandle[] getConstructSpecializations() {
-        if(constructSpecializations == null && invokeSpecializations != null) {
-            final MethodHandle[] ctors = new MethodHandle[invokeSpecializations.length];
-            for(int i = 0; i < ctors.length; ++i) {
-                ctors[i] = composeConstructor(invokeSpecializations[i]);
-            }
-            constructSpecializations = ctors;
-        }
-        return constructSpecializations;
-    }
-
-    /**
-     * Set the method handles for this function.
-     * @param invoker the invoker handle
-     * @param allocator the allocator handle
-     */
-    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) {
-            this.invoker     = invoker;
-            this.constructor = null; // delay constructor composition
-            this.allocator   = allocator;
-        }
-    }
-
-    /**
-     * 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.
-     */
-    ScriptObject allocate() {
-        if (allocator == null) {
-            return null;
-        }
-
-        try {
-            return (ScriptObject)allocator.invokeExact(allocatorMap);
-        } catch (final RuntimeException | Error e) {
-            throw e;
-        } catch (final Throwable t) {
-            throw new RuntimeException(t);
-        }
-    }
-
-    /**
-     * This method is used to create the immutable portion of a bound function.
-     * See {@link ScriptFunction#makeBoundFunction(Object, Object[])}
-     *
-     * @param fn the original function being bound
-     * @param self this reference to bind. Can be null.
-     * @param args additional arguments to bind. Can be null.
-     */
-    ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
-        final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
-
-        final boolean isConstructor = isConstructor();
-        // Note that the new ScriptFunctionData's method handle will not need a callee regardless of whether the
-        // original did.
-        final ScriptFunctionData boundData = new ScriptFunctionData(name, source, token,
-                bindInvokeHandle(invoker, fn, self, allArgs), bindInvokeSpecializations(fn, self, allArgs), isStrict(), isBuiltin(), isConstructor);
-        if(isConstructor) {
-            // Can't just rely on bound invoke as a basis for constructor, as it ignores the passed "this" in favor of the
-            // bound "this"; constructor on the other hand must see the actual "this" received from the allocator.
-
-            // Binding a function will force constructor composition in getConstructor(); not really any way around that
-            // as it's the composed constructor that has to be bound to the function.
-            boundData.constructor = bindConstructHandle(getConstructor(), fn, allArgs);
-            boundData.constructSpecializations = bindConstructorSpecializations(fn, allArgs);
-        }
-        assert boundData.allocator == null;
-        final int thisArity = getArity();
-        if(thisArity != -1) {
-            boundData.setArity(Math.max(0, thisArity - args.length));
-        } else {
-            assert boundData.getArity() == -1;
-        }
-        return boundData;
-    }
-
-    /**
-     * Convert this argument for non-strict functions according to ES 10.4.3
-     *
-     * @param thiz the this argument
-     *
-     * @return the converted this object
-     */
-    Object convertThisObject(final Object thiz) {
-        if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
-            if (JSType.nullOrUndefined(thiz)) {
-                return Context.getGlobalTrusted();
-            }
-
-            if (isPrimitiveThis(thiz)) {
-                return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
-            }
-        }
-
-        return thiz;
-    }
-
-    static boolean isPrimitiveThis(Object obj) {
-        return obj instanceof String || obj instanceof ConsString ||
-               obj instanceof Number || obj instanceof Boolean;
-    }
-
-    /**
-     * Creates an invoker method handle for a bound function.
-     * @param targetFn the function being bound
-     * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
-     * any of its specializations.
-     * @param self the "this" value being bound
-     * @param args additional arguments being bound
-     * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
-     * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
-     * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
-     * to the original invoker on invocation.
-     */
-    private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
-        // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
-        // in the target and will be ignored anyway.
-        final boolean isTargetBound = targetFn.isBoundFunction();
-        assert !(isTargetBound && needsCallee()); // already bound functions don't need a callee
-        final Object boundSelf = isTargetBound ? null : convertThisObject(self);
-        final MethodHandle boundInvoker;
-        if(isVarArg(originalInvoker)) {
-            // First, bind callee and this without arguments
-            final MethodHandle noArgBoundInvoker;
-            if(isTargetBound) {
-                // Don't bind either callee or this
-                noArgBoundInvoker = originalInvoker;
-            } else if(needsCallee()) {
-                // Bind callee and this
-                noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
-            } else {
-                // Only bind this
-                noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
-            }
-            // Now bind arguments
-            if(args.length > 0) {
-                boundInvoker = varArgBinder(noArgBoundInvoker, args);
-            } else {
-                boundInvoker = noArgBoundInvoker;
-            }
-        } else {
-            final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(),
-                    args.length + (isTargetBound ? 0 : (needsCallee() ? 2 : 1)))];
-            int next = 0;
-            if(!isTargetBound) {
-                if(needsCallee()) {
-                    boundArgs[next++] = targetFn;
-                }
-                boundArgs[next++] = boundSelf;
-            }
-            // If more bound args were specified than the function can take, we'll just drop those.
-            System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
-            // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
-            // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
-            // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
-            // start at position 1. If the function is not bound, we start inserting arguments at position 0.
-            boundInvoker = MH.insertArguments(originalInvoker, isTargetBound ? 1 : 0, boundArgs);
-        }
-        if(isTargetBound) {
-            return boundInvoker;
-        }
-        // If the target is not already bound, add a dropArguments that'll throw away the passed this
-        return MH.dropArguments(boundInvoker, 0, Object.class);
-    }
-
-    private MethodHandle[] bindInvokeSpecializations(final ScriptFunction fn, final Object self, final Object[] args) {
-        if(invokeSpecializations == null) {
-            return null;
-        }
-        final MethodHandle[] boundSpecializations = new MethodHandle[invokeSpecializations.length];
-        for(int i = 0; i < invokeSpecializations.length; ++i) {
-            boundSpecializations[i] = bindInvokeHandle(invokeSpecializations[i], fn, self, args);
-        }
-        return boundSpecializations;
-    }
-
-    /**
-     * Creates a constructor method handle for a bound function using the passed constructor handle.
-     * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
-     * @param fn the function being bound
-     * @param args arguments being bound
-     * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
-     * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
-     * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
-     * this script function data object has no constructor handle, null is returned.
-     */
-    private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
-        if(originalConstructor == null) {
-            return null;
-        }
-
-        // If target function is already bound, don't bother binding the callee.
-        final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
-            MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
-        if(args.length == 0) {
-            return calleeBoundConstructor;
-        }
-
-        if(isVarArg(calleeBoundConstructor)) {
-            return varArgBinder(calleeBoundConstructor, args);
-        }
-
-        final Object[] boundArgs;
-        final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
-        if (args.length <= maxArgCount) {
-            boundArgs = args;
-        } else {
-            boundArgs = new Object[maxArgCount];
-            System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
-        }
-        return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
-    }
-
-    private MethodHandle[] bindConstructorSpecializations(final ScriptFunction fn, final Object[] args) {
-        final MethodHandle[] ctorSpecs = getConstructSpecializations();
-        if(ctorSpecs == null) {
-            return null;
-        }
-        final MethodHandle[] boundSpecializations = new MethodHandle[ctorSpecs.length];
-        for(int i = 0; i < ctorSpecs.length; ++i) {
-            boundSpecializations[i] = bindConstructHandle(ctorSpecs[i], fn, args);
-        }
-        return boundSpecializations;
-    }
-
     /**
      * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the
      * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on
      * invocation
+     *
      * @param mh the handle
      * @param args the bound arguments
+     *
      * @return the bound method handle
      */
     private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) {
@@ -671,41 +576,20 @@
     }
 
     /**
-     * Convert boolean flags to int.
-     * @param needsCallee needs-callee flag
-     * @param isVarArg var-arg flag
-     * @param isStrict strict flag
-     * @param isBuiltin builtin flag
-     * @return int flags
+     * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
+     * {@code Object}, the handle is returned unchanged.
+     *
+     * @param mh the handle to adapt
+     * @return the adapted handle
      */
-    private static int makeFlags(final boolean needsCallee, final boolean isVarArg, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
-        int flags = 0;
-        if (needsCallee) {
-            flags |= HAS_CALLEE;
-        }
-        if (isVarArg) {
-            flags |= IS_VARARGS;
-        }
-        if (isStrict) {
-            flags |= IS_STRICT;
-        }
-        if (isBuiltin) {
-            flags |= IS_BUILTIN;
-        }
-        if (isConstructor) {
-            flags |= IS_CONSTRUCTOR;
-        }
-
-        return flags;
+    private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
+        return MH.asType(mh, mh.type().changeReturnType(Object.class));
     }
 
-    /**
-     * Test if a methodHandle refers to a constructor.
-     * @param methodHandle MethodHandle to test.
-     * @return True if method is a constructor.
-     */
-    private static boolean isConstructor(final MethodHandle methodHandle) {
-        return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
+    private void ensureConstructor(final CompiledFunction inv) {
+        if (!inv.hasConstructor()) {
+            inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker())));
+        }
     }
 
     /**
@@ -713,102 +597,56 @@
      * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
      * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
      * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
-     * they also always receive a callee.
-     * @param methodHandle the examined method handle
+     * they also always receive a callee).
+     *
+     * @param mh the examined method handle
+     *
      * @return true if the method handle expects a callee, false otherwise
      */
-    private static boolean needsCallee(MethodHandle methodHandle) {
-        final MethodType type = methodHandle.type();
-        final int len = type.parameterCount();
-        if(len == 0) {
+    protected static boolean needsCallee(final MethodHandle mh) {
+        final MethodType type   = mh.type();
+        final int        length = type.parameterCount();
+
+        if (length == 0) {
             return false;
         }
-        if(type.parameterType(0) == boolean.class) {
-            return len > 1 && type.parameterType(1) == ScriptFunction.class;
+
+        if (type.parameterType(0) == boolean.class) {
+            return length > 1 && type.parameterType(1) == ScriptFunction.class;
         }
+
         return type.parameterType(0) == ScriptFunction.class;
     }
 
-    private static boolean isVarArg(MethodHandle methodHandle) {
-        final MethodType type = methodHandle.type();
-        return type.parameterType(type.parameterCount() - 1).isArray();
-    }
-
     /**
-     * Takes a method handle, and returns a potentially different method handle that can be used in
-     * {@link ScriptFunction#invoke(Object, Object...)} or {@link ScriptFunction#construct(Object, Object...)}.
-     * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
-     * {@code Object} as well, except for the following ones:
-     * <ul>
-     *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
-     *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
-     *   (callee) as an argument.</li>
-     * </ul>
+     * Check if a javascript function methodhandle is a vararg handle
      *
-     * @param handle the original method handle
-     * @return the new handle, conforming to the rules above.
+     * @param mh method handle to check
+     *
+     * @return true if vararg
      */
-    private MethodHandle makeGenericMethod(final MethodHandle handle) {
-        final MethodType type = handle.type();
-        MethodType newType = type.generic();
-        if (isVarArg()) {
-            newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
-        }
-        if (needsCallee()) {
-            newType = newType.changeParameterType(0, ScriptFunction.class);
-        }
-        return type.equals(newType) ? handle : handle.asType(newType);
-    }
-
-    /**
-     * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
-     * {@code Object}, the handle is returned unchanged.
-     * @param mh the handle to adapt
-     * @return the adapted handle
-     */
-    private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
-        return MH.asType(mh, mh.type().changeReturnType(Object.class));
-    }
-
-
-    /**
-     * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
-     * method handle. If this function's method handles don't need a callee parameter, returns the original method
-     * handle unchanged.
-     * @param mh a method handle with order of arguments {@code (callee, this, args...)}
-     * @return a method handle with order of arguments {@code (this, callee, args...)}
-     */
-    private MethodHandle swapCalleeAndThis(final MethodHandle mh) {
-        if (!needsCallee()) {
-            return mh;
-        }
+    protected static boolean isVarArg(final MethodHandle mh) {
         final MethodType type = mh.type();
-        assert type.parameterType(0) == ScriptFunction.class;
-        assert type.parameterType(1) == Object.class;
-        final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
-        final int[] reorder = new int[type.parameterCount()];
-        reorder[0] = 1;
-        assert reorder[1] == 0;
-        for (int i = 2; i < reorder.length; ++i) {
-            reorder[i] = i;
-        }
-        return MethodHandles.permuteArguments(mh, newType, reorder);
+        return type.parameterType(type.parameterCount() - 1).isArray();
     }
 
     @SuppressWarnings("unused")
     private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
-        if(array2 == null) {
+        if (array2 == null) {
             // Must clone it, as we can't allow the receiving method to alter the array
             return array1.clone();
         }
+
         final int l2 = array2.length;
-        if(l2 == 0) {
+        if (l2 == 0) {
             return array1.clone();
         }
+
         final int l1 = array1.length;
         final Object[] concat = new Object[l1 + l2];
         System.arraycopy(array1, 0, concat, 0, l1);
         System.arraycopy(array2, 0, concat, l1, l2);
+
         return concat;
     }
 
@@ -820,5 +658,4 @@
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
         return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
     }
-
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java	Tue Mar 12 18:12:42 2013 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +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.runtime;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodType;
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.runtime.options.Options;
-
-class SpecializedMethodChooser {
-    /** Should specialized function and specialized constructors for the builtin be used if available? */
-    private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
-
-    static MethodHandle candidateWithLowestWeight(final MethodType descType, final MethodHandle initialCandidate, final MethodHandle[] specs) {
-        if (DISABLE_SPECIALIZATION || specs == null) {
-            return initialCandidate;
-        }
-
-        int          minimumWeight = Integer.MAX_VALUE;
-        MethodHandle candidate     = initialCandidate;
-
-        for (final MethodHandle spec : specs) {
-            final MethodType specType = spec.type();
-
-            if (!typeCompatible(descType, specType)) {
-                continue;
-            }
-
-            //return type is ok. we want a wider or equal one for our callsite.
-            final int specWeight = weigh(specType);
-            if (specWeight < minimumWeight) {
-                candidate = spec;
-                minimumWeight = specWeight;
-            }
-        }
-
-        return candidate;
-    }
-
-    private static boolean typeCompatible(final MethodType desc, final MethodType spec) {
-        //spec must fit in desc
-        final Class<?>[] dparray = desc.parameterArray();
-        final Class<?>[] sparray = spec.parameterArray();
-
-        if (dparray.length != sparray.length) {
-            return false;
-        }
-
-        for (int i = 0; i < dparray.length; i++) {
-            final Type dp = Type.typeFor(dparray[i]);
-            final Type sp = Type.typeFor(sparray[i]);
-
-            if (dp.isBoolean()) {
-                return false; //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
-            }
-
-            //specialization arguments must be at least as wide as dp, if not wider
-            if (Type.widest(dp, sp) != sp) {
-                //e.g. specialization takes double and callsite says "object". reject.
-                //but if specialization says double and callsite says "int" or "long" or "double", that's fine
-                return false;
-            }
-        }
-
-        return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
-    }
-
-    private static int weigh(final MethodType t) {
-        int weight = Type.typeFor(t.returnType()).getWeight();
-        for (final Class<?> paramType : t.parameterArray()) {
-            final int pweight = Type.typeFor(paramType).getWeight();
-            weight += pweight;
-        }
-        return weight;
-    }
-}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java	Tue Mar 12 15:30:53 2013 +0100
@@ -39,7 +39,7 @@
 import jdk.nashorn.internal.runtime.ScriptObject;
 
 /**
- * Utility class shared by {@link NashornLinker} and {@code NashornPrimitiveLinker} for converting JS values to Java
+ * Utility class shared by {@code NashornLinker} and {@code NashornPrimitiveLinker} for converting JS values to Java
  * types.
  */
 public class JavaArgumentConverters {
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java	Tue Mar 12 15:30:53 2013 +0100
@@ -40,8 +40,6 @@
     private static final MethodHandle IS_SCRIPTOBJECT          = findOwnMH("isScriptObject", boolean.class, Object.class);
     private static final MethodHandle IS_SCRIPTFUNCTION        = findOwnMH("isScriptFunction", boolean.class, Object.class);
     private static final MethodHandle IS_MAP                   = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
-    private static final MethodHandle IS_FUNCTION_MH           = findOwnMH("isFunctionMH", boolean.class, Object.class, MethodHandle.class);
-    private static final MethodHandle IS_NONSTRICT_FUNCTION    = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, MethodHandle.class);
     private static final MethodHandle IS_INSTANCEOF_2          = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
 
     // don't create me!
@@ -87,33 +85,6 @@
         return MH.insertArguments(IS_INSTANCEOF_2, 1, class1, class2);
     }
 
-    /**
-     * Get the guard that checks if a {@link ScriptFunction} is equal to
-     * a known ScriptFunction, using reference comparison
-     *
-     * @param function The ScriptFunction to check against. This will be bound to the guard method handle
-     *
-     * @return method handle for guard
-     */
-    public static MethodHandle getFunctionGuard(final ScriptFunction function) {
-        assert function.getInvokeHandle() != null;
-        return MH.insertArguments(IS_FUNCTION_MH, 1, function.getInvokeHandle());
-    }
-
-    /**
-     * Get a guard that checks if a {@link ScriptFunction} is equal to
-     * a known ScriptFunction using reference comparison, and whether the type of
-     * the second argument (this-object) is not a JavaScript primitive type.
-     *
-     * @param function The ScriptFunction to check against. This will be bound to the guard method handle
-     *
-     * @return method handle for guard
-     */
-    public static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) {
-        assert function.getInvokeHandle() != null;
-        return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.getInvokeHandle());
-    }
-
     @SuppressWarnings("unused")
     private static boolean isScriptObject(final Object self) {
         return self instanceof ScriptObject;
@@ -130,16 +101,6 @@
     }
 
     @SuppressWarnings("unused")
-    private static boolean isFunctionMH(final Object self, final MethodHandle mh) {
-        return self instanceof ScriptFunction && ((ScriptFunction)self).getInvokeHandle() == mh;
-    }
-
-    @SuppressWarnings("unused")
-    private static boolean isNonStrictFunction(final Object self, final Object arg, final MethodHandle mh) {
-        return self instanceof ScriptFunction && ((ScriptFunction)self).getInvokeHandle() == mh && arg instanceof ScriptObject;
-    }
-
-    @SuppressWarnings("unused")
     private static boolean isInstanceOf2(final Object self, final Class<?> class1, final Class<?> class2) {
         return class1.isInstance(self) || class2.isInstance(self);
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java	Tue Mar 12 15:30:53 2013 +0100
@@ -278,7 +278,7 @@
                     this.valueNextArg = Boolean.parseBoolean(arg);
                     break;
                 default:
-                    throw new IllegalArgumentException();
+                    throw new IllegalArgumentException(keyToken);
                 }
             }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties	Tue Mar 12 15:30:53 2013 +0100
@@ -165,6 +165,12 @@
     desc="Generate local variable table in .class files." \
 }
 
+nashorn.option.lazy.compilation = {                                                                      \
+    name="--lazy-compilation",                                                                           \
+    is_undocumented=true,                                                                                \
+    desc="EXPERIMENTAL: Use lazy code generation strategies - do not compile the entire script at once." \
+}
+
 nashorn.option.loader.per.compile = {              \
     name="--loader-per-compile",                   \
     is_undocumented=true,                          \
--- a/nashorn/test/script/currently-failing/JDK-8006529.js	Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/test/script/currently-failing/JDK-8006529.js	Tue Mar 12 15:30:53 2013 +0100
@@ -39,12 +39,13 @@
  * 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 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 ScriptEnvironment = Java.type("jdk.nashorn.internal.runtime.ScriptEnvironment")
-var Source         = Java.type("jdk.nashorn.internal.runtime.Source")
-var FunctionNode   = Java.type("jdk.nashorn.internal.ir.FunctionNode")
+var Source            = Java.type("jdk.nashorn.internal.runtime.Source")
+var FunctionNode      = Java.type("jdk.nashorn.internal.ir.FunctionNode")
+var ThrowErrorManager = Java.type("jdk.nashorn.internal.runtime.Context$ThrowErrorManager");
 
 // Compiler class methods and fields
 var parseMethod = Parser.class.getMethod("parse");
@@ -90,7 +91,7 @@
 // representing it.
 function compile(source) {
     var source   = new Source("<no name>", source);
-    var parser   = new Parser(Context.getContext().getEnv(), source, null);
+    var parser   = new Parser(Context.getContext().getEnv(), source, new ThrowErrorManager());
     var func     = parseMethod.invoke(parser);
     var compiler = new Compiler(Context.getContext().getEnv(), func);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/currently-failing/clone_ir.js	Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * clone_ir : Check that functionNode.clone copies all nodes and that they
+ * are not the same references
+ *
+ * @test
+ * @run
+ */
+
+var js1 = "var tuple = { func : function f(x) { if (x) { print('true'); { print('block_under-true'); } } else { print('false'); } } }";
+
+var Parser            = Java.type("jdk.nashorn.internal.parser.Parser");
+var ASTWriter         = Java.type("jdk.nashorn.internal.ir.debug.ASTWriter");
+var Context           = Java.type("jdk.nashorn.internal.runtime.Context");
+var ScriptEnvironment = Java.type("jdk.nashorn.internal.runtime.ScriptEnvironment");
+var Source            = Java.type("jdk.nashorn.internal.runtime.Source");
+var FunctionNode      = Java.type("jdk.nashorn.internal.ir.FunctionNode");
+var ThrowErrorManager = Java.type("jdk.nashorn.internal.runtime.Context$ThrowErrorManager");
+var System            = Java.type("java.lang.System");
+
+var toArrayMethod = ASTWriter.class.getMethod("toArray");
+var parseMethod  = Parser.class.getMethod("parse");    
+
+function toString(obj) {
+    var output = "{ ";
+    for (property in obj) {
+	output += property + ': ' + obj[property]+'; ';
+    }
+    return output + '}'
+}
+
+function flatten(func) {
+    var writer   = new ASTWriter(func);
+    var funcList = toArrayMethod.invoke(writer);
+    
+    var res = [];
+    for each (x in funcList) {
+	    res.push({ name: x.getClass().getName(), id: System.identityHashCode(x) });
+	}
+    return res;
+}
+
+function check(contents) {
+    return check_src(new Source("<no name>", contents));
+}
+
+function check_src(src) {
+    var parser  = new Parser(Context.getContext().getEnv(), src, new ThrowErrorManager());
+
+    var func = parseMethod.invoke(parser);
+    print(func);
+    var func2 = func.clone();
+
+    var f1 = flatten(func);
+    var f2 = flatten(func2);
+
+    print(f1.map(toString));
+    print(f2.map(toString));
+
+    if (f1.length != f2.length) {
+	print("length difference between original and clone " + f1.length + " != " + f2.length);
+	return false;
+    }
+
+    for (var i = 0; i < f1.length; i++) {
+	if (f1[i].name !== f2[i].name) {
+	    print("name conflict at " + i + " " + f1[i].name + " != " + f2[i].name);
+	    return false;
+	} else if (f1[i].id === f2[i].id) {
+	    print("id problem at " + i + " " + toString(f1[i]) + " was not deep copied to " + toString(f2[i]) + " became " + f1[i].id + " != " + f2[i].id);
+	    return false;
+	}
+    }
+    
+    return true;
+}
+
+print(check(js1));