8007273: Creation of ScriptFunctions can be refactored
authorhannesw
Wed, 06 Feb 2013 10:31:58 +0100
changeset 16216 46ed48fd84d3
parent 16215 92f00bfd6059
child 16217 c5ac9be5c444
8007273: Creation of ScriptFunctions can be refactored Reviewed-by: lagergren, attila
nashorn/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java
nashorn/src/jdk/nashorn/internal/codegen/objects/ObjectCreator.java
nashorn/src/jdk/nashorn/internal/objects/Global.java
nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
--- a/nashorn/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Tue Feb 05 22:07:04 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Wed Feb 06 10:31:58 2013 +0100
@@ -28,7 +28,6 @@
 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
 import static jdk.nashorn.internal.codegen.Compiler.SCRIPTFUNCTION_IMPL_OBJECT;
 import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
 
@@ -40,14 +39,10 @@
 import jdk.nashorn.internal.codegen.FunctionSignature;
 import jdk.nashorn.internal.codegen.MethodEmitter;
 import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.IdentNode;
 import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.parser.Token;
-import jdk.nashorn.internal.parser.TokenType;
 import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.Source;
 
 /**
  * Analyze a function object's characteristics for appropriate code
@@ -79,58 +74,28 @@
      */
     @Override
     public void makeObject(final MethodEmitter method) {
-        makeMap();
 
-        final IdentNode identNode  = functionNode.getIdent();
-        final String    signature  = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
-        final long      firstToken = functionNode.getFirstToken();
-        final long      lastToken  = functionNode.getLastToken();
-        final int       position   = Token.descPosition(firstToken);
-        final int       length     = Token.descPosition(lastToken) - position + Token.descLength(lastToken);
-        final long      token      = Token.toDesc(TokenType.FUNCTION, position, length);
+        final PropertyMap map = makeMap();
+        final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
+        final ScriptFunctionData scriptFunctionData = new ScriptFunctionData(functionNode, map);
 
         /*
-         * Instantiate the function object, must be referred to by name as
-         * class is not available at compile time
+         * Instantiate the function object
          */
         method._new(SCRIPTFUNCTION_IMPL_OBJECT).dup();
-        method.load(functionNode.isAnonymous() ? "" : identNode.getName());
+        codegen.loadConstant(scriptFunctionData);
         loadHandle(method, signature);
         if(functionNode.needsParentScope()) {
             method.loadScope();
         } else {
             method.loadNull();
         }
-        method.getStatic(compileUnit.getUnitClassName(), SOURCE.tag(), SOURCE.descriptor());
-        method.load(token);
         method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
 
         /*
-         * Emit code for the correct property map for the object
-         */
-        loadMap(method);
-
-        /*
          * Invoke the constructor
          */
-        method.load(functionNode.needsCallee());
-        method.load(functionNode.isStrictMode());
-        method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_OBJECT,
-                    String.class,
-                    MethodHandle.class,
-                    ScriptObject.class,
-                    Source.class,
-                    long.class,
-                    MethodHandle.class,
-                    PropertyMap.class,
-                    boolean.class,
-                    boolean.class));
+        method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_OBJECT, ScriptFunctionData.class, MethodHandle.class, ScriptObject.class, MethodHandle.class));
 
-
-        if (functionNode.isVarArg()) {
-            method.dup();
-            method.load(functionNode.getParameters().size());
-            method.invoke(ScriptFunction.SET_ARITY);
-        }
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/objects/ObjectCreator.java	Tue Feb 05 22:07:04 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/objects/ObjectCreator.java	Wed Feb 06 10:31:58 2013 +0100
@@ -127,14 +127,15 @@
 
     /**
      * Construct the property map appropriate for the object.
+     * @return the newly created property map
      */
-    protected void makeMap() {
+    protected PropertyMap makeMap() {
         if (keys.isEmpty()) { //empty map
             propertyMap = PropertyMap.newMap(fieldObjectClass);
-            return;
+        } else {
+            propertyMap = newMapCreator(fieldObjectClass).makeMap(isVarArg());
         }
-
-        propertyMap = newMapCreator(fieldObjectClass).makeMap(isVarArg());
+        return propertyMap;
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java	Tue Feb 05 22:07:04 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java	Wed Feb 06 10:31:58 2013 +0100
@@ -34,7 +34,6 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.ref.SoftReference;
-import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -394,7 +393,7 @@
 
     @Override
     public ScriptFunction newScriptFunction(final String name, final MethodHandle handle, final ScriptObject scope, final boolean strict) {
-        return new ScriptFunctionImpl(name, handle, scope, strict, null);
+        return new ScriptFunctionImpl(name, handle, scope, null, strict, false);
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Tue Feb 05 22:07:04 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Wed Feb 06 10:31:58 2013 +0100
@@ -30,6 +30,8 @@
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+
+import jdk.nashorn.internal.runtime.ScriptFunctionData;
 import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
 import jdk.nashorn.internal.runtime.GlobalFunctions;
 import jdk.nashorn.internal.runtime.Property;
@@ -37,7 +39,6 @@
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
-import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.linker.Lookup;
 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
 
@@ -46,109 +47,68 @@
  * function objects -- to expose properties like "prototype", "length" etc.
  */
 public class ScriptFunctionImpl extends ScriptFunction {
-    // 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;
-
-    // set this function to be a builtin function
-    private void setIsBuiltin() {
-        flags |= IS_BUILTIN;
-    }
-
-    // set this function to be a ECMAScript strict function
-    private void setIsStrict() {
-        flags |= IS_STRICT;
-    }
 
     private static final MethodHandle BOUND_FUNCTION    = findOwnMH("boundFunction",    Object.class, ScriptFunction.class, Object.class, Object[].class, Object.class, Object[].class);
     private static final MethodHandle BOUND_CONSTRUCTOR = findOwnMH("boundConstructor", Object.class, ScriptFunction.class, Object[].class, Object.class, Object[].class);
 
     private static final PropertyMap nasgenmap$;
 
-    private int flags;
-
     /**
-     * Constructor
-     *
-     * Called by Nasgen generated code, no membercount, use the default map
-     * Creates builtin functions only
+     * Constructor called by Nasgen generated code, no membercount, use the default map.
+     * Creates builtin functions only.
      *
      * @param name name of function
      * @param invokeHandle handle for invocation
      * @param specs specialized versions of this method, if available, null otherwise
      */
     ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) {
-        this(name, invokeHandle, nasgenmap$, specs);
-    }
-
-    /**
-     * Constructor
-     *
-     * Called by Nasgen generated code, no membercount, use the default map
-     * Creates builtin functions only
-     *
-     * @param name name of function
-     * @param methodHandle handle for invocation
-     * @param map initial property map
-     * @param specs specialized versions of this method, if available, null otherwise
-     */
-    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final PropertyMap map, final MethodHandle[] specs) {
-        super(name, methodHandle, (nasgenmap$ == map) ? nasgenmap$ : map.addAll(nasgenmap$), null, null, 0, false, specs);
-        this.setIsBuiltin();
+        super(name, invokeHandle, nasgenmap$, null, specs, false, true);
         init();
     }
 
     /**
-     * Constructor
+     * Constructor called by Nasgen generated code, no membercount, use the map passed as argument.
+     * Creates builtin functions only.
      *
-     * Called by Global.newScriptFunction (runtime)
+     * @param name name of function
+     * @param invokeHandle handle for invocation
+     * @param map initial property map
+     * @param specs specialized versions of this method, if available, null otherwise
+     */
+    ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs) {
+        super(name, invokeHandle, map.addAll(nasgenmap$), null, specs, false, true);
+        init();
+    }
+
+    /**
+     * Constructor called by Global.newScriptFunction (runtime).
      *
      * @param name name of function
      * @param methodHandle handle for invocation
      * @param scope scope object
+     * @param specs specialized versions of this method, if available, null otherwise
      * @param strict are we in strict mode
-     * @param specs specialized versions of this method, if available, null otherwise
+     * @param builtin is this a built-in function
      */
-    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final boolean strict, final MethodHandle[] specs) {
-        super(name, methodHandle, getMap(strict), scope, specs);
-        if (strict) {
-            this.setIsStrict();
-        }
+    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
+        super(name, methodHandle, getMap(strict), scope, specs, strict, builtin);
         init();
     }
 
     /**
-     * Constructor
+     * Constructor called by (compiler) generated code for {@link ScriptObject}s.
+     * Code is generated by {@link FunctionObjectCreator}
      *
-     * Called by (compiler) generated code for {@link ScriptObject}s. Code is
-     * generated by {@link FunctionObjectCreator}
-     *
-     * TODO this is a horrible constructor - can we do it with fewer args?
-     *
-     * @param name name of function
+     * @param data static function data
      * @param methodHandle handle for invocation
      * @param scope scope object
-     * @param source source
-     * @param token token
      * @param allocator instance constructor for function
-     * @param allocatorMap initial map that constructor will keep reference to for future instantiations
-     * @param needCallee does the function use the {@code callee} variable
-     * @param strict are we in strict mode
      */
-    public ScriptFunctionImpl(
-            final String name,
-            final MethodHandle methodHandle,
-            final ScriptObject scope,
-            final Source source,
-            final long token,
-            final MethodHandle allocator,
-            final PropertyMap allocatorMap,
-            final boolean needCallee,
-            final boolean strict) {
-        super(name, methodHandle, getMap(strict), scope, source, token, allocator, allocatorMap, needCallee, null);
-        if (strict) {
-            this.setIsStrict();
+    public ScriptFunctionImpl(final ScriptFunctionData data, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle allocator) {
+        super(data, getMap(data.isStrict()), scope);
+        // Set method handles in script data
+        if (data.getInvoker() == null) {
+            data.setMethodHandles(methodHandle, allocator);
         }
         init();
     }
@@ -167,11 +127,11 @@
     static synchronized ScriptFunction getTypeErrorThrower() {
         if (typeErrorThrower == null) {
             //name handle
-            final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, false, null);
+            final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false);
             // clear constructor handle...
-            func.constructHandle = null;
-            func.prototype       = UNDEFINED;
-            typeErrorThrower     = func;
+            func.setConstructHandle(null);
+            func.setPrototype(UNDEFINED);
+            typeErrorThrower = func;
         }
 
         return typeErrorThrower;
@@ -216,28 +176,8 @@
         return new AnonymousFunction();
     }
 
-    @Override
-    public final boolean isStrict() {
-        return (flags & IS_STRICT) != 0;
-    }
-
-    @Override
-    public final boolean hasCalleeParameter() {
-        return (flags & HAS_CALLEE) != 0;
-    }
-
-    @Override
-    protected void setHasCalleeParameter() {
-        flags |= HAS_CALLEE;
-    }
-
-    @Override
-    public final boolean isBuiltin() {
-        return (flags & IS_BUILTIN) != 0;
-    }
-
     /**
-     * Factory method for non-constructor functions
+     * Factory method for non-constructor built-in functions
      *
      * @param name   function name
      * @param methodHandle handle for invocation
@@ -246,9 +186,7 @@
      * @return new ScriptFunction
      */
     public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict) {
-        final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, strict, specs);
-
-        func.setIsBuiltin();
+        final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, strict, true);
         func.setConstructHandle(null);
         func.setPrototype(UNDEFINED);
 
@@ -256,7 +194,7 @@
     }
 
     /**
-     * Factory method for non-constructor functions
+     * Factory method for non-constructor built-in functions
      *
      * @param name   function name
      * @param methodHandle handle for invocation
@@ -268,7 +206,7 @@
     }
 
     /**
-     * Factory method for non-constructor functions
+     * Factory method for non-constructor built-in functions
      *
      * @param name   function name
      * @param methodHandle handle for invocation
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java	Tue Feb 05 22:07:04 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java	Wed Feb 06 10:31:58 2013 +0100
@@ -383,6 +383,7 @@
      * @return New {@link PropertyMap} with added properties.
      */
     public PropertyMap addAll(final PropertyMap other) {
+        assert this != other : "adding property map to itself";
         final Property[] otherProperties = other.properties.getProperties();
         final PropertyHashMap newProperties = properties.immutableAdd(otherProperties);
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Feb 05 22:07:04 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Wed Feb 06 10:31:58 2013 +0100
@@ -37,7 +37,6 @@
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
 import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
-import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 import jdk.nashorn.internal.runtime.linker.NashornGuards;
@@ -70,144 +69,53 @@
 
     private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
 
-    /** method handle to arity setter for this ScriptFunction */
-    public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class);
     /** method handle to scope getter for this ScriptFunction */
     public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
 
     /** 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");
 
-    /** Name of function or null. */
-    private final String name;
-
-    /** Source of function. */
-    private final Source source;
-
-    /** Start position and length in source. */
-    private final long token;
-
-    /** Reference to code for this method. */
-    private final MethodHandle invokeHandle;
-
-    /** Reference to code for this method when called to create "new" object */
-    protected MethodHandle constructHandle;
+    private final ScriptFunctionData data;
 
     /** Reference to constructor prototype. */
     protected Object prototype;
 
-    /** Constructor to create a new instance. */
-    private MethodHandle allocator;
-
-    /** Map for new instance constructor. */
-    private PropertyMap allocatorMap;
-
     /** The parent scope. */
     private final ScriptObject scope;
 
-    /** Specializations - see @SpecializedFunction */
-    private MethodHandle[] invokeSpecializations;
-
-    /** Specializations - see @SpecializedFunction */
-    private MethodHandle[] constructSpecializations;
-
-    /** This field is either computed in constructor or set explicitly by calling setArity method. */
-    private int arity;
-
     /**
      * Constructor
      *
-     * @param name          function name
-     * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
-     * @param map           property map
-     * @param scope         scope
-     * @param specs         specialized version of this function - other method handles
+     * @param name         function name
+     * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
+     * @param map          property map
+     * @param scope        scope
+     * @param specs        specialized version of this function - other method handles
+     *
      */
     protected ScriptFunction(
             final String name,
             final MethodHandle methodHandle,
             final PropertyMap map,
             final ScriptObject scope,
-            final MethodHandle[] specs) {
-        this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs);
-    }
+            final MethodHandle[] specs,
+            final boolean strict,
+            final boolean builtin) {
 
-    /**
-     * Heuristic to figure out if the method handle has a callee argument. If it's type is either
-     * {@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
-     * @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) {
-            return false;
-        }
-        if(type.parameterType(0) == boolean.class) {
-            return len > 2 && type.parameterType(2) == ScriptFunction.class;
-        }
-        return len > 1 && type.parameterType(1) == ScriptFunction.class;
+        this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin), map, scope);
     }
 
     /**
      * Constructor
      *
-     * @param name          function name
-     * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
+     * @param data          static function data
      * @param map           property map
      * @param scope         scope
-     * @param source        the source
-     * @param token         token
-     * @param allocator     method handle to this function's allocator - see JO$ classes
-     * @param allocatorMap  property map to be used for all constructors
-     * @param needsCallee   does this method use the {@code callee} variable
-     * @param specs         specialized version of this function - other method handles
      */
     protected ScriptFunction(
-            final String name,
-            final MethodHandle methodHandle,
+            final ScriptFunctionData data,
             final PropertyMap map,
-            final ScriptObject scope,
-            final Source source,
-            final long token,
-            final MethodHandle allocator,
-            final PropertyMap allocatorMap,
-            final boolean needsCallee,
-            final MethodHandle[] specs) {
-
-        this(name, methodHandle, map, scope, source, token, needsCallee, specs);
-
-        //this is the internal constructor
-
-        this.allocator    = allocator;
-        this.allocatorMap = allocatorMap;
-    }
-
-    /**
-     * Constructor
-     *
-     * @param name              function name
-     * @param methodHandle      method handle to function (if specializations are present, assumed to be most generic)
-     * @param map               property map
-     * @param scope             scope
-     * @param source            the source
-     * @param token             token
-     * @param needsCallee       does this method use the {@code callee} variable
-     * @param specs             specialized version of this function - other method handles
-     */
-    protected ScriptFunction(
-            final String name,
-            final MethodHandle methodHandle,
-            final PropertyMap map,
-            final ScriptObject scope,
-            final Source source,
-            final long token,
-            final boolean needsCallee,
-            final MethodHandle[] specs) {
+            final ScriptObject scope) {
 
         super(map);
 
@@ -215,89 +123,8 @@
             constructorCount++;
         }
 
-        this.name   = name;
-        this.source = source;
-        this.token  = token;
+        this.data = data;
         this.scope  = scope;
-        if(needsCallee) {
-            setHasCalleeParameter();
-        }
-
-        final MethodType type       = methodHandle.type();
-        final int        paramCount = type.parameterCount();
-        final boolean    isVarArg   = type.parameterType(paramCount - 1).isArray();
-
-        final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg));
-
-        this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
-
-        if (needsCallee && !isVarArg) {
-            this.arity--;
-        }
-
-        if (scope != null) {
-            this.invokeHandle    = mh;
-            this.constructHandle = mh;
-        } else if (isConstructor(mh)) {
-            if (!isVarArg) {
-                this.arity--;    // 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.invokeHandle    = MH.insertArguments(mh, 0, false);
-            this.constructHandle = MH.insertArguments(mh, 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] = MH.insertArguments(specs[i], 0, true);
-                }
-            }
-        } else {
-            this.invokeHandle             = mh;
-            this.constructHandle          = mh;
-            this.invokeSpecializations    = specs;
-            this.constructSpecializations = specs;
-        }
-    }
-
-    /**
-     * Takes a method type, and returns a (potentially different method type) that the method handles used by
-     * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and
-     * {@link #construct(Object, Object...)}. The returned method type 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>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor
-     * invocation,</li>
-     * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
-     * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be
-     * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li>
-     * @param type the original type
-     * @param hasCallee true if the function uses the callee argument
-     * @param isVarArg if the function is a vararg
-     * @return the new type, conforming to the rules above.
-     */
-    private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) {
-        // Generify broadly
-        MethodType newType = type.generic().changeReturnType(Object.class);
-        if(isVarArg) {
-            // Change back to vararg if we over-generified it
-            newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
-        }
-        final boolean hasBoolean = type.parameterType(0) == boolean.class;
-        if(hasBoolean) {
-            // Restore the initial boolean argument
-            newType = newType.changeParameterType(0, boolean.class);
-        }
-        if(hasCallee) {
-            // Restore the ScriptFunction argument
-            newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class);
-        }
-        return newType;
     }
 
     @Override
@@ -329,7 +156,7 @@
      * @return arity
      */
     public final int getArity() {
-        return arity;
+        return data.getArity();
     }
 
     /**
@@ -337,27 +164,32 @@
      * @param arity arity
      */
     public final void setArity(final int arity) {
-        this.arity = arity;
+        data.setArity(arity);
     }
 
     /**
      * Is this a ECMAScript 'use strict' function?
      * @return true if function is in strict mode
      */
-    public abstract boolean isStrict();
+    public boolean isStrict() {
+        return data.isStrict();
+    }
 
     /**
      * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ?
      * @return true if built-in
      */
-    public abstract boolean isBuiltin();
+    public boolean isBuiltin() {
+        return data.isBuiltin();
+    }
 
     /**
-     * Is this a non-strict and not-built-in script function?
-     * @return true if neither strict nor built-in
+     * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
+     * according to ECMA 10.4.3.
+     * @return true if this argument must be an object
      */
-    public boolean isNonStrictFunction() {
-        return !isStrict() && !isBuiltin();
+    public boolean needsWrappedThis() {
+        return data.needsWrappedThis();
     }
 
     /**
@@ -372,43 +204,44 @@
             invokes++;
         }
 
+        final MethodHandle invoker = data.getGenericInvoker();
         final Object selfObj = convertThisObject(self);
         final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 
-        if (isVarArg(invokeHandle)) {
-            if (hasCalleeParameter()) {
-                return invokeHandle.invokeExact(selfObj, this, args);
+        if (data.isVarArg()) {
+            if (data.needsCallee()) {
+                return invoker.invokeExact(selfObj, this, args);
             }
-            return invokeHandle.invokeExact(selfObj, args);
+            return invoker.invokeExact(selfObj, args);
         }
 
-        final int paramCount = invokeHandle.type().parameterCount();
-        if (hasCalleeParameter()) {
+        final int paramCount = invoker.type().parameterCount();
+        if (data.needsCallee()) {
             switch (paramCount) {
             case 2:
-                return invokeHandle.invokeExact(selfObj, this);
+                return invoker.invokeExact(selfObj, this);
             case 3:
-                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0));
+                return invoker.invokeExact(selfObj, this, getArg(args, 0));
             case 4:
-                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
+                return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
             case 5:
-                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+                return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
             default:
-                return invokeHandle.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
+                return invoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
             }
         }
 
         switch (paramCount) {
         case 1:
-            return invokeHandle.invokeExact(selfObj);
+            return invoker.invokeExact(selfObj);
         case 2:
-            return invokeHandle.invokeExact(selfObj, getArg(args, 0));
+            return invoker.invokeExact(selfObj, getArg(args, 0));
         case 3:
-            return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
+            return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
         case 4:
-            return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+            return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
         default:
-            return invokeHandle.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
+            return invoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
         }
     }
 
@@ -424,44 +257,45 @@
      * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it
      */
     public Object construct(final Object self, final Object... args) throws Throwable {
-        if (constructHandle == null) {
+        if (data.getConstructor() == null) {
             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
         }
 
-        if (isVarArg(constructHandle)) {
-            if (hasCalleeParameter()) {
-                return constructHandle.invokeExact(self, this, args);
+        final MethodHandle constructor = data.getGenericConstructor();
+        if (data.isVarArg()) {
+            if (data.needsCallee()) {
+                return constructor.invokeExact(self, this, args);
             }
-            return constructHandle.invokeExact(self, args);
+            return constructor.invokeExact(self, args);
         }
 
-        final int paramCount = constructHandle.type().parameterCount();
-        if (hasCalleeParameter()) {
+        final int paramCount = constructor.type().parameterCount();
+        if (data.needsCallee()) {
             switch (paramCount) {
             case 2:
-                return constructHandle.invokeExact(self, this);
+                return constructor.invokeExact(self, this);
             case 3:
-                return constructHandle.invokeExact(self, this, getArg(args, 0));
+                return constructor.invokeExact(self, this, getArg(args, 0));
             case 4:
-                return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
+                return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
             case 5:
-                return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+                return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
             default:
-                return constructHandle.invokeWithArguments(withArguments(self, this, args));
+                return constructor.invokeWithArguments(withArguments(self, this, args));
             }
         }
 
         switch(paramCount) {
         case 1:
-            return constructHandle.invokeExact(self);
+            return constructor.invokeExact(self);
         case 2:
-            return constructHandle.invokeExact(self, getArg(args, 0));
+            return constructor.invokeExact(self, getArg(args, 0));
         case 3:
-            return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1));
+            return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1));
         case 4:
-            return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+            return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
         default:
-            return constructHandle.invokeWithArguments(withArguments(self, null, args));
+            return constructor.invokeWithArguments(withArguments(self, null, args));
         }
     }
 
@@ -509,9 +343,9 @@
 
         ScriptObject object = null;
 
-        if (allocator != null) {
+        if (data.getAllocator() != null) {
             try {
-                object = (ScriptObject)allocator.invokeExact(allocatorMap);
+                object = (ScriptObject)data.getAllocator().invokeExact(data.getAllocatorMap());
             } catch (final RuntimeException | Error e) {
                 throw e;
             } catch (final Throwable t) {
@@ -546,25 +380,6 @@
      */
     public abstract ScriptFunction makeBoundFunction(Object self, Object[] args);
 
-    /**
-     * 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;
-    }
-
-    /**
-     * Test if a methodHandle refers to a variable argument method.
-     * @param methodHandle MethodHandle to test.
-     * @return True if variable arguments.
-     */
-    public boolean isVarArg(final MethodHandle methodHandle) {
-        return hasCalleeParameter()
-                ? methodHandle.type().parameterCount() == 3 && methodHandle.type().parameterType(2).isArray()
-                : methodHandle.type().parameterCount() == 2 && methodHandle.type().parameterType(1).isArray();
-    }
 
     @Override
     public final String safeToString() {
@@ -573,23 +388,7 @@
 
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder();
-
-        sb.append(super.toString())
-            .append(" [ ")
-            .append(invokeHandle)
-            .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();
+        return data.toString();
     }
 
     /**
@@ -598,11 +397,7 @@
      * @return string representation of this function's source
      */
     public final String toSource() {
-        if (source != null && token != 0) {
-            return source.getString(Token.descPosition(token), Token.descLength(token));
-        }
-
-        return "function " + (name == null ? "" : name) + "() { [native code] }";
+        return data.toSource();
     }
 
     /**
@@ -696,7 +491,7 @@
      * @return invoke method handle
      */
     public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) {
-        return candidateWithLowestWeight(type, getInvokeHandle(), invokeSpecializations);
+        return candidateWithLowestWeight(type, getInvokeHandle(), data.getInvokeSpecializations());
     }
 
     /**
@@ -706,7 +501,7 @@
      * @return invokeHandle
      */
     public final MethodHandle getInvokeHandle() {
-        return invokeHandle;
+        return data.getInvoker();
     }
 
     /**
@@ -718,19 +513,9 @@
      */
     public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
         final MethodHandle bound = MH.bindTo(getInvokeHandle(), self);
-        return hasCalleeParameter() ? MH.bindTo(bound, this) : bound;
+        return data.needsCallee() ? MH.bindTo(bound, this) : bound;
     }
 
-    /**
-     * Check whether the ScriptFunction has callee parameter
-     * @return true if callee parameter
-     */
-    protected abstract boolean hasCalleeParameter();
-
-    /**
-     * Flag ScriptFunction as needing a callee parameter
-     */
-    protected abstract void setHasCalleeParameter();
 
     /**
      * Get the construct handle - the most generic (and if no specializations are in place, only) constructor
@@ -740,7 +525,7 @@
      * @return construct handle
      */
     public final MethodHandle getConstructHandle(final MethodType type) {
-        return candidateWithLowestWeight(type, getConstructHandle(), constructSpecializations);
+        return candidateWithLowestWeight(type, getConstructHandle(), data.getConstructSpecializations());
     }
 
     /**
@@ -748,7 +533,7 @@
      * @return constructor handle
      */
     public final MethodHandle getConstructHandle() {
-        return constructHandle;
+        return data.getConstructor();
     }
 
     /**
@@ -756,8 +541,7 @@
      * @param constructHandle constructor handle
      */
     public final void setConstructHandle(final MethodHandle constructHandle) {
-        this.constructHandle = constructHandle;
-        this.constructSpecializations = null;
+        data.setConstructor(constructHandle);
     }
 
     /**
@@ -765,7 +549,7 @@
      * @return the name
      */
     public final String getName() {
-        return name;
+        return data.getName();
     }
 
     /**
@@ -775,7 +559,7 @@
      * @return true if this needs compilation
      */
     public final boolean needsCompilation() {
-        return invokeHandle == null;
+        return data.getInvoker() == null;
     }
 
     /**
@@ -783,7 +567,7 @@
      * @return token
      */
     public final long getToken() {
-        return token;
+        return data.getToken();
     }
 
     /**
@@ -907,7 +691,7 @@
         final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self
         MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor);
 
-        if (hasCalleeParameter()) {
+        if (data.needsCallee()) {
             handle = MH.foldArguments(handle, ALLOCATE);
         } else {
             handle = MH.filterArguments(handle, 0, ALLOCATE);
@@ -958,12 +742,12 @@
         MethodHandle boundHandle;
         MethodHandle guard = null;
 
-        if (hasCalleeParameter()) {
+        if (data.needsCallee()) {
             final MethodHandle callHandle = getBestSpecializedInvokeHandle(type);
 
             if(NashornCallSiteDescriptor.isScope(desc)) {
                 // (this, callee, args...) => (callee, args...) => (callee, [this], args...)
-                boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
+                boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
                 boundHandle = MH.dropArguments(boundHandle, 1, Object.class);
             } else {
                 // (this, callee, args...) permute => (callee, this, args...) which is what we get in
@@ -980,7 +764,7 @@
                 // For non-strict functions, check whether this-object is primitive type.
                 // If so add a to-object-wrapper argument filter.
                 // Else install a guard that will trigger a relink when the argument becomes primitive.
-                if (isNonStrictFunction()) {
+                if (needsWrappedThis()) {
                     if (isPrimitiveThis(request.getArguments()[1])) {
                         boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
                     } else {
@@ -992,7 +776,7 @@
             final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1));
 
             if(NashornCallSiteDescriptor.isScope(desc)) {
-                boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
+                boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
                 boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class);
             } else {
                 boundHandle = MH.dropArguments(callHandle, 0, Object.class);
@@ -1012,13 +796,13 @@
         MethodHandle methodHandle = getBestSpecializedInvokeHandle(type);
 
         if (bindName != null) {
-            if (hasCalleeParameter()) {
+            if (data.needsCallee()) {
                 methodHandle = MH.insertArguments(methodHandle, 1, this, bindName);
             } else {
                 methodHandle = MH.insertArguments(methodHandle, 1, bindName);
             }
         } else {
-            if (hasCalleeParameter()) {
+            if (data.needsCallee()) {
                 methodHandle = MH.insertArguments(methodHandle, 1, this);
             }
         }
@@ -1034,7 +818,7 @@
      * @return the converted this object
      */
     protected Object convertThisObject(final Object thiz) {
-        if (!(thiz instanceof ScriptObject) && isNonStrictFunction()) {
+        if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
             if (JSType.nullOrUndefined(thiz)) {
                 return Context.getGlobalTrusted();
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Wed Feb 06 10:31:58 2013 +0100
@@ -0,0 +1,442 @@
+/*
+ * 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 jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.parser.Token;
+import jdk.nashorn.internal.parser.TokenType;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
+
+/**
+ * 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 class ScriptFunctionData {
+
+    // 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;
+
+    /** 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;
+    /** Does this function need a callee argument? */
+    private final int    flags;
+
+    /** Reference to code for this method. */
+    private MethodHandle invoker;
+    /** Reference to code for this method when called to create "new" object */
+    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;
+    /** Generic constructor used in {@link ScriptFunction#construct(Object, Object...)}. */
+    private MethodHandle genericConstructor;
+    /** Specializations - see @SpecializedFunction */
+    private MethodHandle[] invokeSpecializations;
+    /** Specializations - see @SpecializedFunction */
+    private MethodHandle[] constructSpecializations;
+
+    /**
+     * Constructor
+     * @param fn the function node
+     * @param allocatorMap the allocator property map
+     */
+    public ScriptFunctionData(final FunctionNode fn, final PropertyMap allocatorMap) {
+
+        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);
+
+        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);
+    }
+
+    /**
+     * 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
+     */
+    public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
+        this.name        = name;
+        this.source      = null;
+        this.token       = 0;
+
+        final MethodType type     = methodHandle.type();
+        final int paramCount      = type.parameterCount();
+        final boolean isVarArg    = type.parameterType(paramCount - 1).isArray();
+        final boolean needsCallee = needsCallee(methodHandle);
+
+        this.flags = makeFlags(needsCallee, isVarArg, strict, builtin);
+        this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
+
+        if (needsCallee && !isVarArg) {
+            this.arity--;
+        }
+
+        if (isConstructor(methodHandle)) {
+            if (!isVarArg) {
+                this.arity--;    // 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 = 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] = MH.insertArguments(specs[i], 0, true);
+                }
+            }
+        } else {
+            this.invoker                  = methodHandle;
+            this.constructor              = methodHandle;
+            this.invokeSpecializations    = specs;
+            this.constructSpecializations = specs;
+        }
+    }
+
+    /**
+     * Get the arity of the function.
+     * @return the arity
+     */
+    public int getArity() {
+        return arity;
+    }
+
+    /**
+     * Set the arity of the function.
+     * @param arity the arity
+     */
+    public void setArity(int arity) {
+        this.arity = arity;
+    }
+
+    /**
+     * Get the function name.
+     * @return function name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Get the source of the function.
+     * @return the source
+     */
+    public Source getSource() {
+        return source;
+    }
+
+    /**
+     * 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
+     */
+    public 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();
+    }
+
+    /**
+     * Get the allocator property map.
+     * @return the allocator map
+     */
+    public PropertyMap getAllocatorMap() {
+        return allocatorMap;
+    }
+
+    /**
+     * Get the function's parse token.
+     * @return the token
+     */
+    public long getToken() {
+        return token;
+    }
+
+    /**
+     * Returns true if the function needs a callee argument.
+     * @return the needsCallee flag
+     */
+    public 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;
+    }
+
+    /**
+     * Returns true if this is a built-in function.
+     * @return the built-in flag
+     */
+    public boolean isBuiltin() {
+        return (flags & IS_BUILTIN) != 0;
+    }
+
+    /**
+     * Returns true if this is a var-arg function.
+     * @return the var-arg flag
+     */
+    public boolean isVarArg() {
+        return (flags & IS_VARARGS) != 0;
+    }
+
+    /**
+     * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
+     * according to ECMA 10.4.3.
+     * @return true if this argument must be an object
+     */
+    public boolean needsWrappedThis() {
+        return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
+    }
+
+    /**
+     * Get the method handle used to invoke this function.
+     * @return the invoke handle
+     */
+    public MethodHandle getInvoker() {
+        return invoker;
+    }
+
+    /**
+     * Get the method handle used to invoke this function as a constructor.
+     * @return the constructor handle
+     */
+    public MethodHandle getConstructor() {
+        return constructor;
+    }
+
+    /**
+     * Set the constructor method handle.
+     * @param constructor the constructor handle
+     */
+    public void setConstructor(MethodHandle constructor) {
+        this.constructor = constructor;
+        this.constructSpecializations = null;
+    }
+
+    /**
+     * Get the method handle used to allocate a new object for this constructor.
+     * @return the allocator handle
+     */
+    public MethodHandle getAllocator() {
+        return allocator;
+    }
+
+    /**
+     * Get an adapted version of the invoker handle that only uses {@code Object} as parameter and return types.
+     * @return the generic invoke handle
+     */
+    public MethodHandle getGenericInvoker() {
+        if (genericInvoker == null) {
+            assert invoker != null : "invoker is null";
+            genericInvoker = adaptMethodType(invoker);
+        }
+        return genericInvoker;
+    }
+
+    /**
+     * Get an adapted version of the constructor handle that only uses {@code Object} as parameter and return types.
+     * @return the generic constructor handle
+     */
+    public MethodHandle getGenericConstructor() {
+        if (genericConstructor == null) {
+            assert constructor != null : "constructor is null";
+            genericConstructor = adaptMethodType(constructor);
+        }
+        return genericConstructor;
+    }
+
+    /**
+     * Get the specialized invoke handles for this function.
+     * @return array of specialized invoke handles
+     */
+    public MethodHandle[] getInvokeSpecializations() {
+        return invokeSpecializations;
+    }
+
+    /**
+     * Get the specialized construct handles for this function.
+     * @return array of specialized construct handles
+     */
+    public MethodHandle[] getConstructSpecializations() {
+        return constructSpecializations;
+    }
+
+    /**
+     * Set the method handles for this function.
+     * @param invoker the invoker handle
+     * @param allocator the allocator handle
+     */
+    public void setMethodHandles(MethodHandle invoker, 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 = invoker;
+            this.allocator   = allocator;
+        }
+    }
+
+    /**
+     * 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
+     */
+    private static int makeFlags(final boolean needsCallee, final boolean isVarArg, final boolean isStrict, final boolean isBuiltin) {
+        int flags = 0;
+        if (needsCallee) {
+            flags |= HAS_CALLEE;
+        }
+        if (isVarArg) {
+            flags |= IS_VARARGS;
+        }
+        if (isStrict) {
+            flags |= IS_STRICT;
+        }
+        if (isBuiltin) {
+            flags |= IS_BUILTIN;
+        }
+        return flags;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Heuristic to figure out if the method handle has a callee argument. If it's type is either
+     * {@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
+     * @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) {
+            return false;
+        }
+        if(type.parameterType(0) == boolean.class) {
+            return len > 2 && type.parameterType(2) == ScriptFunction.class;
+        }
+        return len > 1 && type.parameterType(1) == ScriptFunction.class;
+    }
+
+    /**
+     * 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 second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
+     *   (callee) as an argument</li>
+     * </ul>
+     *
+     * @param handle the original method handle
+     * @return the new handle, conforming to the rules above.
+     */
+    private MethodHandle adaptMethodType(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(1, ScriptFunction.class);
+        }
+        return type.equals(newType) ? handle : handle.asType(newType);
+    }
+
+}