8007900: Function binding is inefficient
authorattila
Tue, 12 Feb 2013 12:47:51 +0100
changeset 16228 df28320aa080
parent 16227 1bafb74d17b2
child 16229 cab69ca3490d
8007900: Function binding is inefficient Reviewed-by: jlaskey, lagergren
nashorn/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java
nashorn/src/jdk/nashorn/internal/objects/Global.java
nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java
nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
nashorn/src/jdk/nashorn/internal/runtime/ArgumentSetter.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
nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
nashorn/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java
nashorn/test/script/basic/funcbind2.js
nashorn/test/script/basic/funcbind2.js.EXPECTED
nashorn/test/script/basic/funcbind3.js
nashorn/test/script/basic/funcbind3.js.EXPECTED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java	Tue Feb 12 12:47:51 2013 +0100
@@ -0,0 +1,51 @@
+/*
+ * 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.objects;
+
+import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptFunctionData;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * A {@code ScriptFunctionImpl} subclass for functions created using {@code Function.prototype.bind}. Such functions
+ * must track their {@code [[TargetFunction]] property for purposes of correctly implementing {@code [[HasInstance]]};
+ * see {@link ScriptFunction#isInstance(ScriptObject)}.
+ */
+class BoundScriptFunctionImpl extends ScriptFunctionImpl {
+    private final ScriptFunction targetFunction;
+
+    BoundScriptFunctionImpl(ScriptFunctionData data, ScriptFunction targetFunction) {
+        super(data);
+        this.prototype = ScriptRuntime.UNDEFINED;
+        this.targetFunction = targetFunction;
+    }
+
+    @Override
+    protected ScriptFunction getTargetFunction() {
+        return targetFunction;
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java	Tue Feb 12 13:55:05 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java	Tue Feb 12 12:47:51 2013 +0100
@@ -413,7 +413,7 @@
 
     @Override
     public ScriptFunction newScriptFunction(final String name, final MethodHandle handle, final ScriptObject scope, final boolean strict) {
-        return new ScriptFunctionImpl(name, handle, scope, null, strict, false);
+        return new ScriptFunctionImpl(name, handle, scope, null, strict, false, true);
     }
 
     @Override
@@ -1339,7 +1339,6 @@
 
         // initialize global function properties
         this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL);
-        ((ScriptFunction)this.eval).setArity(1);
 
         this.parseInt           = ScriptFunctionImpl.makeFunction("parseInt",   GlobalFunctions.PARSEINT);
         this.parseFloat         = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java	Tue Feb 12 13:55:05 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java	Tue Feb 12 12:47:51 2013 +0100
@@ -56,8 +56,8 @@
         PropertyMap map = PropertyMap.newMap(NativeStrictArguments.class);
         map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH);
         // In strict mode, the caller and callee properties should throw TypeError
-        map = ScriptFunctionImpl.newThrowerProperty(map, "caller", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE);
-        map = ScriptFunctionImpl.newThrowerProperty(map, "callee", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE);
+        map = ScriptFunctionImpl.newThrowerProperty(map, "caller");
+        map = ScriptFunctionImpl.newThrowerProperty(map, "callee");
         nasgenmap$ = map;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Tue Feb 12 13:55:05 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Tue Feb 12 12:47:51 2013 +0100
@@ -26,30 +26,27 @@
 package jdk.nashorn.internal.objects;
 
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
 
 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;
 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.ScriptRuntime;
 import jdk.nashorn.internal.runtime.linker.Lookup;
-import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
 
 /**
  * Concrete implementation of ScriptFunction. This sets correct map for the
  * function objects -- to expose properties like "prototype", "length" etc.
  */
 public class ScriptFunctionImpl extends ScriptFunction {
-
-    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);
-
+    // property map for strict mode functions
+    private static final PropertyMap strictmodemap$;
+    // property map for bound functions
+    private static final PropertyMap boundfunctionmap$;
+    // property map for non-strict, non-bound functions.
     private static final PropertyMap nasgenmap$;
 
     /**
@@ -61,7 +58,7 @@
      * @param specs specialized versions of this method, if available, null otherwise
      */
     ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) {
-        super(name, invokeHandle, nasgenmap$, null, specs, false, true);
+        super(name, invokeHandle, nasgenmap$, null, specs, false, true, true);
         init();
     }
 
@@ -75,7 +72,7 @@
      * @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);
+        super(name, invokeHandle, map.addAll(nasgenmap$), null, specs, false, true, true);
         init();
     }
 
@@ -88,9 +85,10 @@
      * @param specs specialized versions of this method, if available, null otherwise
      * @param strict are we in strict mode
      * @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) {
-        super(name, methodHandle, getMap(strict), scope, specs, strict, builtin);
+    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);
         init();
     }
 
@@ -106,9 +104,16 @@
     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);
-        }
+        data.setMethodHandles(methodHandle, allocator);
+        init();
+    }
+
+    /**
+     * Only invoked internally from {@link BoundScriptFunctionImpl} constructor.
+     * @param data the script function data for the bound function.
+     */
+    ScriptFunctionImpl(final ScriptFunctionData data) {
+        super(data, boundfunctionmap$, null);
         init();
     }
 
@@ -118,6 +123,8 @@
         map = Lookup.newProperty(map, "length",    Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null);
         map = Lookup.newProperty(map, "name",      Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null);
         nasgenmap$ = map;
+        strictmodemap$ = createStrictModeMap(nasgenmap$);
+        boundfunctionmap$ = createBoundFunctionMap(strictmodemap$);
     }
 
     // function object representing TypeErrorThrower
@@ -126,9 +133,7 @@
     static synchronized ScriptFunction getTypeErrorThrower() {
         if (typeErrorThrower == null) {
             //name handle
-            final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false);
-            // clear constructor handle...
-            func.setConstructHandle(null);
+            final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false, false);
             func.setPrototype(UNDEFINED);
             typeErrorThrower = func;
         }
@@ -137,28 +142,24 @@
     }
 
     // add a new property that throws TypeError on get as well as set
-    static synchronized PropertyMap newThrowerProperty(final PropertyMap map, final String name, final int flags) {
-        return map.newProperty(name, flags, -1, Lookup.TYPE_ERROR_THROWER_GETTER, Lookup.TYPE_ERROR_THROWER_SETTER);
+    static synchronized PropertyMap newThrowerProperty(final PropertyMap map, final String name) {
+        return map.newProperty(name, Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, -1,
+                Lookup.TYPE_ERROR_THROWER_GETTER, Lookup.TYPE_ERROR_THROWER_SETTER);
     }
 
-    // property map for strict mode functions - lazily initialized
-    private static PropertyMap strictmodemap$;
+    private static PropertyMap createStrictModeMap(final PropertyMap functionMap) {
+        return newThrowerProperty(newThrowerProperty(functionMap, "arguments"), "caller");
+    }
 
     // Choose the map based on strict mode!
     private static PropertyMap getMap(final boolean strict) {
-        if (strict) {
-            synchronized (ScriptFunctionImpl.class) {
-                if (strictmodemap$ == null) {
-                    // In strict mode, the following properties should throw TypeError
-                    strictmodemap$ = nasgenmap$;
-                    strictmodemap$ = newThrowerProperty(strictmodemap$, "arguments", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE);
-                    strictmodemap$ = newThrowerProperty(strictmodemap$, "caller",    Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE);
-                }
-            }
-            return strictmodemap$;
-        }
+        return strict ? strictmodemap$ : nasgenmap$;
+    }
 
-        return nasgenmap$;
+    private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) {
+        // Bond function map is same as strict function map, but additionally lacks the "prototype" property, see
+        // ECMAScript 5.1 section 15.3.4.5
+        return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype"));
     }
 
     // Instance of this class is used as global anonymous function which
@@ -181,12 +182,10 @@
      * @param name   function name
      * @param methodHandle handle for invocation
      * @param specs  specialized versions of function if available, null otherwise
-     * @param strict are we in strict mode
      * @return new ScriptFunction
      */
-    static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict) {
-        final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, strict, true);
-        func.setConstructHandle(null);
+    static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
+        final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, false, true, false);
         func.setPrototype(UNDEFINED);
 
         return func;
@@ -197,18 +196,6 @@
      *
      * @param name   function name
      * @param methodHandle handle for invocation
-     * @param specs  specialized versions of function if available, null otherwise
-     * @return new ScriptFunction
-     */
-    static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
-        return makeFunction(name, methodHandle, specs, false);
-    }
-
-    /**
-     * Factory method for non-constructor built-in functions
-     *
-     * @param name   function name
-     * @param methodHandle handle for invocation
      * @return new ScriptFunction
      */
     static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle) {
@@ -216,57 +203,27 @@
     }
 
     /**
-     * This method is used to create a bound function. See also
-     * {@link NativeFunction#bind(Object, Object...)} method implementation.
-     *
-     * @param thiz this reference to bind
-     * @param args arguments to bind
+     * Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we
+     * can expose it to methods in this package.
+     * @param self the self to bind to this function. Can be null (in which case, null is bound as this).
+     * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
+     * @return a function with the specified self and parameters bound.
      */
     @Override
-    protected ScriptFunction makeBoundFunction(final Object thiz, final Object[] args) {
-        Object[] allArgs = args;
-
-        if (allArgs == null) {
-            allArgs = ScriptRuntime.EMPTY_ARRAY;
-        }
-
-        final Object boundThiz = convertThisObject(thiz);
-        final MethodHandle   boundMethod = MH.insertArguments(BOUND_FUNCTION, 0, this, boundThiz, allArgs);
-        final ScriptFunction boundFunc   = makeFunction("", boundMethod, null, true);
-
-        MethodHandle consHandle  = this.getConstructHandle();
-
-        if (consHandle != null) {
-            consHandle = MH.insertArguments(BOUND_CONSTRUCTOR, 0, this, allArgs);
-        }
-
-        boundFunc.setConstructHandle(consHandle);
-        int newArity = this.getArity();
-        if (newArity != -1) {
-            newArity -= Math.min(newArity, allArgs.length);
-        }
-        boundFunc.setArity(newArity);
-
-        return boundFunc;
+    protected ScriptFunction makeBoundFunction(Object self, Object[] args) {
+        return super.makeBoundFunction(self, args);
     }
 
-    @SuppressWarnings("unused")
-    private static Object boundFunction(final ScriptFunction wrapped, final Object boundThiz, final Object[] boundArgs, final Object thiz, final Object[] args) {
-        final Object[] allArgs = new Object[boundArgs.length + args.length];
-
-        System.arraycopy(boundArgs, 0, allArgs, 0, boundArgs.length);
-        System.arraycopy(args, 0, allArgs, boundArgs.length, args.length);
-
-        return ScriptRuntime.apply(wrapped, boundThiz, allArgs);
-    }
-
-    @SuppressWarnings("unused")
-    private static Object boundConstructor(final ScriptFunction wrapped, final Object[] boundArgs, final Object thiz, final Object[] args) {
-        final Object[] allArgs = new Object[boundArgs.length + args.length];
-        System.arraycopy(boundArgs, 0, allArgs, 0, boundArgs.length);
-        System.arraycopy(args, 0, allArgs, boundArgs.length, args.length);
-
-        return ScriptRuntime.construct(wrapped, allArgs);
+    /**
+     * This method is used to create a bound function based on this function.
+     *
+     * @param data the {@code ScriptFunctionData} specifying the functions immutable portion.
+     * @return a function initialized from the specified data. Its parent scope will be set to null, therefore the
+     * passed in data should not expect a callee.
+     */
+    @Override
+    protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) {
+        return new BoundScriptFunctionImpl(data, getTargetFunction());
     }
 
     // return Object.prototype - used by "allocate"
@@ -288,12 +245,4 @@
             setUserAccessors("caller", func, func);
         }
     }
-
-    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        try {
-            return MethodHandles.lookup().findStatic(ScriptFunctionImpl.class, name, MH.type(rtype, types));
-        } catch (final NoSuchMethodException | IllegalAccessException e) {
-            throw new MethodHandleFactory.LookupException(e);
-        }
-    }
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/ArgumentSetter.java	Tue Feb 12 13:55:05 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ArgumentSetter.java	Tue Feb 12 12:47:51 2013 +0100
@@ -24,7 +24,7 @@
  */
 package jdk.nashorn.internal.runtime;
 
-import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
+import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
 
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
 
@@ -36,10 +36,10 @@
     private ArgumentSetter() {}
 
     /** Method handle for setting a function argument at a given index in an arguments object. Used from generated bytecode */
-    public static final Call SET_ARGUMENT      = staticCall(ArgumentSetter.class, "setArgument", void.class, Object.class, ScriptObject.class, int.class);
+    public static final Call SET_ARGUMENT      = staticCallNoLookup(ArgumentSetter.class, "setArgument", void.class, Object.class, ScriptObject.class, int.class);
 
     /** Method handle for setting a function argument at a given index in an arguments array. Used from generated bytecode */
-    public static final Call SET_ARRAY_ELEMENT = staticCall(ArgumentSetter.class, "setArrayElement", void.class, Object.class, Object[].class, int.class);
+    public static final Call SET_ARRAY_ELEMENT = staticCallNoLookup(ArgumentSetter.class, "setArrayElement", void.class, Object.class, Object[].class, int.class);
 
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java	Tue Feb 12 13:55:05 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java	Tue Feb 12 12:47:51 2013 +0100
@@ -308,7 +308,7 @@
      *
      * @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found.
      */
-    PropertyMap deleteProperty(final Property property) {
+    public PropertyMap deleteProperty(final Property property) {
         PropertyMap newMap = checkHistory(property);
         final String key = property.getKey();
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Feb 12 13:55:05 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Feb 12 12:47:51 2013 +0100
@@ -34,13 +34,10 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
-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.runtime.linker.MethodHandleFactory;
 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 import jdk.nashorn.internal.runtime.linker.NashornGuards;
-import jdk.nashorn.internal.runtime.options.Options;
 import org.dynalang.dynalink.CallSiteDescriptor;
 import org.dynalang.dynalink.linker.GuardedInvocation;
 import org.dynalang.dynalink.linker.LinkRequest;
@@ -63,18 +60,13 @@
     public static final MethodHandle G$NAME       = findOwnMH("G$name",       Object.class, Object.class);
 
     /** Method handle for allocate function for this ScriptFunction */
-    public static final MethodHandle ALLOCATE     = findOwnMH("allocate", Object.class);
-
-    private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
+    static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
 
     private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.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");
-
     private final ScriptFunctionData data;
 
     /** Reference to constructor prototype. */
@@ -100,9 +92,10 @@
             final ScriptObject scope,
             final MethodHandle[] specs,
             final boolean strict,
-            final boolean builtin) {
+            final boolean builtin,
+            final boolean isConstructor) {
 
-        this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin), map, scope);
+        this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope);
     }
 
     /**
@@ -138,12 +131,13 @@
      */
     @Override
     public boolean isInstance(final ScriptObject instance) {
-        if (!(prototype instanceof ScriptObject)) {
-            typeError("prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype));
+        final Object basePrototype = getTargetFunction().getPrototype();
+        if (!(basePrototype instanceof ScriptObject)) {
+            typeError("prototype.not.an.object", ScriptRuntime.safeToString(getTargetFunction()), ScriptRuntime.safeToString(basePrototype));
         }
 
         for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
-            if (proto == prototype) {
+            if (proto == basePrototype) {
                 return true;
             }
         }
@@ -152,11 +146,18 @@
     }
 
     /**
-     * Get the arity of this ScriptFunction
-     * @return arity
+     * Returns the target function for this function. If the function was not created using
+     * {@link #makeBoundFunction(Object, Object[])}, its target function is itself. If it is bound, its target function
+     * is the target function of the function it was made from (therefore, the target function is always the final,
+     * unbound recipient of the calls).
+     * @return the target function for this function.
      */
-    public final int getArity() {
-        return data.getArity();
+    protected ScriptFunction getTargetFunction() {
+        return this;
+    }
+
+    boolean isBoundFunction() {
+        return getTargetFunction() != this;
     }
 
     /**
@@ -176,14 +177,6 @@
     }
 
     /**
-     * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ?
-     * @return true if built-in
-     */
-    public boolean isBuiltin() {
-        return data.isBuiltin();
-    }
-
-    /**
      * 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
@@ -203,126 +196,7 @@
         if (Context.DEBUG) {
             invokes++;
         }
-
-        final MethodHandle invoker = data.getGenericInvoker();
-        final Object selfObj = convertThisObject(self);
-        final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
-
-        if (data.isVarArg()) {
-            if (data.needsCallee()) {
-                return invoker.invokeExact(this, selfObj, args);
-            }
-            return invoker.invokeExact(selfObj, args);
-        }
-
-        final int paramCount = invoker.type().parameterCount();
-        if (data.needsCallee()) {
-            switch (paramCount) {
-            case 2:
-                return invoker.invokeExact(this, selfObj);
-            case 3:
-                return invoker.invokeExact(this, selfObj, getArg(args, 0));
-            case 4:
-                return invoker.invokeExact(this, selfObj, getArg(args, 0), getArg(args, 1));
-            case 5:
-                return invoker.invokeExact(this, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
-            default:
-                return invoker.invokeWithArguments(withArguments(selfObj, paramCount, args));
-            }
-        }
-
-        switch (paramCount) {
-        case 1:
-            return invoker.invokeExact(selfObj);
-        case 2:
-            return invoker.invokeExact(selfObj, getArg(args, 0));
-        case 3:
-            return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
-        case 4:
-            return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
-        default:
-            return invoker.invokeWithArguments(withArguments(selfObj, paramCount, args));
-        }
-    }
-
-    private static Object getArg(final Object[] args, final int i) {
-        return i < args.length ? args[i] : UNDEFINED;
-    }
-
-    /**
-     * Construct new object using this constructor.
-     * @param self  Target object.
-     * @param args  Call arguments.
-     * @return ScriptFunction result.
-     * @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 (data.getConstructor() == null) {
-            typeError("not.a.constructor", ScriptRuntime.safeToString(this));
-        }
-
-        final MethodHandle constructor = data.getGenericConstructor();
-        if (data.isVarArg()) {
-            if (data.needsCallee()) {
-                return constructor.invokeExact(this, self, args);
-            }
-            return constructor.invokeExact(self, args);
-        }
-
-        final int paramCount = constructor.type().parameterCount();
-        if (data.needsCallee()) {
-            switch (paramCount) {
-            case 2:
-                return constructor.invokeExact(this, self);
-            case 3:
-                return constructor.invokeExact(this, self, getArg(args, 0));
-            case 4:
-                return constructor.invokeExact(this, self, getArg(args, 0), getArg(args, 1));
-            case 5:
-                return constructor.invokeExact(this, self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
-            default:
-                return constructor.invokeWithArguments(withArguments(self, args));
-            }
-        }
-
-        switch(paramCount) {
-        case 1:
-            return constructor.invokeExact(self);
-        case 2:
-            return constructor.invokeExact(self, getArg(args, 0));
-        case 3:
-            return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1));
-        case 4:
-            return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
-        default:
-            return constructor.invokeWithArguments(withArguments(self, args));
-        }
-    }
-
-    private Object[] withArguments(final Object self, final Object[] args) {
-        return withArguments(self, args.length + (data.needsCallee() ? 2 : 1), args);
-    }
-
-    private Object[] withArguments(final Object self, final int argCount, final Object[] args) {
-        final Object[] finalArgs = new Object[argCount];
-
-        int nextArg = 0;
-        if (data.needsCallee()) {
-            finalArgs[nextArg++] = this;
-        }
-        finalArgs[nextArg++] = self;
-
-        // Don't add more args that there is argCount in the handle (including self and callee).
-        for (int i = 0; i < args.length && nextArg < argCount;) {
-            finalArgs[nextArg++] = args[i++];
-        }
-
-        // If we have fewer args than argCount, pad with undefined.
-        while (nextArg < argCount) {
-            finalArgs[nextArg++] = UNDEFINED;
-        }
-
-        return finalArgs;
+        return data.invoke(this, self, arguments);
     }
 
     /**
@@ -331,26 +205,14 @@
      *
      * @return a new instance of the {@link ScriptObject} whose allocator this is
      */
-    public Object allocate() {
+    @SuppressWarnings("unused")
+    private Object allocate() {
         if (Context.DEBUG) {
             allocations++;
         }
-
-        if (getConstructHandle() == null) {
-            typeError("not.a.constructor", ScriptRuntime.safeToString(this));
-        }
-
-        ScriptObject object = null;
+        assert !isBoundFunction(); // allocate never invoked on bound functions
 
-        if (data.getAllocator() != null) {
-            try {
-                object = (ScriptObject)data.getAllocator().invokeExact(data.getAllocatorMap());
-            } catch (final RuntimeException | Error e) {
-                throw e;
-            } catch (final Throwable t) {
-                throw new RuntimeException(t);
-            }
-        }
+        final ScriptObject object = data.allocate();
 
         if (object != null) {
             if (prototype instanceof ScriptObject) {
@@ -372,13 +234,17 @@
     protected abstract ScriptObject getObjectPrototype();
 
     /**
-     * Creates a version of this function bound to a specific "self" and other argumentss
-     * @param self the self to bind the function to
-     * @param args other arguments (beside self) to bind the function to
-     * @return the bound function
+     * Creates a version of this function bound to a specific "self" and other arguments, as per
+     * {@code Function.prototype.bind} functionality in ECMAScript 5.1 section 15.3.4.5.
+     * @param self the self to bind to this function. Can be null (in which case, null is bound as this).
+     * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
+     * @return a function with the specified self and parameters bound.
      */
-    protected abstract ScriptFunction makeBoundFunction(Object self, Object[] args);
+    protected ScriptFunction makeBoundFunction(Object self, Object[] args) {
+        return makeBoundFunction(data.makeBoundFunctionData(this, self, args));
+    }
 
+    protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData);
 
     @Override
     public final String safeToString() {
@@ -417,80 +283,13 @@
         return prototype;
     }
 
-    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;
-    }
-
-    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 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;
-            }
-        }
-
-        if (DISABLE_SPECIALIZATION && candidate != initialCandidate) {
-            Context.err("### Specializing builtin " + getName() + " -> " + candidate + "?");
-        }
-
-        return candidate;
-    }
-
     /**
      * 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 getBestSpecializedInvokeHandle(final MethodType type) {
-        return candidateWithLowestWeight(type, getInvokeHandle(), data.getInvokeSpecializations());
+    private final MethodHandle getBestInvoker(final MethodType type) {
+        return data.getBestInvoker(type);
     }
 
     /**
@@ -525,33 +324,6 @@
     }
 
     /**
-     * Get the construct handle - the most generic (and if no specializations are in place, only) constructor
-     * method handle for this ScriptFunction
-     * @see SpecializedConstructor
-     * @param type type for wanted constructor
-     * @return construct handle
-     */
-    private final MethodHandle getConstructHandle(final MethodType type) {
-        return candidateWithLowestWeight(type, getConstructHandle(), data.getConstructSpecializations());
-    }
-
-    /**
-     * Get a method handle to the constructor for this function
-     * @return constructor handle
-     */
-    public final MethodHandle getConstructHandle() {
-        return data.getConstructor();
-    }
-
-    /**
-     * Set a method handle to the constructor for this function.
-     * @param constructHandle constructor handle. Can be null to prevent the function from being used as a constructor.
-     */
-    public final void setConstructHandle(final MethodHandle constructHandle) {
-        data.setConstructor(constructHandle);
-    }
-
-    /**
      * Get the name for this function
      * @return the name
      */
@@ -570,14 +342,6 @@
     }
 
     /**
-     * Get token for this function
-     * @return token
-     */
-    public final long getToken() {
-        return data.getToken();
-    }
-
-    /**
      * Get the scope for this function
      * @return the scope
      */
@@ -618,7 +382,7 @@
      */
     public static int G$length(final Object self) {
         if (self instanceof ScriptFunction) {
-            return ((ScriptFunction)self).getArity();
+            return ((ScriptFunction)self).data.getArity();
         }
 
         return 0;
@@ -681,81 +445,13 @@
 
     @Override
     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
-        // Call site type is (callee, args...) - dyn:new doesn't specify a "this", it's our job to allocate it
         final MethodType type = desc.getMethodType();
-
-        // Constructor arguments are either (callee, this, args...) or (this, args...), depending on whether it needs a
-        // callee or not.
-        MethodHandle constructor = getConstructHandle(type);
-
-        if (constructor == null) {
-            typeError("not.a.constructor", ScriptRuntime.safeToString(this));
-            return null;
-        }
-
-        // 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.
-        constructor = swapCalleeAndThis(constructor);
-
-        final MethodType ctorType = constructor.type();
-        // Drop constructor "this", so it's also captured as "allocation" parameter of newFilter after we fold the
-        // constructor into newFilter.
-        // (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...)
-        MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor);
-
-        // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
-        if (data.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...
-            handle = MH.foldArguments(handle, ALLOCATE);
-        } else {
-            // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
-            // (this, args...) filter (callee) => (callee, args...)
-            handle = MH.filterArguments(handle, 0, ALLOCATE);
-        }
-
-        final MethodHandle filterIn = MH.asType(pairArguments(handle, type), type);
-        return new GuardedInvocation(filterIn, null, NashornGuards.getFunctionGuard(this));
-    }
-
-    /**
-     * 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 (!data.needsCallee()) {
-            return 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);
-    }
-
-    @SuppressWarnings("unused")
-    private static Object newFilter(final Object result, final Object allocation) {
-        return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
+        return new GuardedInvocation(pairArguments(data.getBestConstructor(type), type), null, NashornGuards.getFunctionGuard(this));
     }
 
     @SuppressWarnings("unused")
     private static Object wrapFilter(final Object obj) {
-        if (obj instanceof ScriptObject || !isPrimitiveThis(obj)) {
+        if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
             return obj;
         }
         return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj);
@@ -792,7 +488,7 @@
         MethodHandle guard = null;
 
         if (data.needsCallee()) {
-            final MethodHandle callHandle = getBestSpecializedInvokeHandle(type);
+            final MethodHandle callHandle = getBestInvoker(type);
 
             if (NashornCallSiteDescriptor.isScope(desc)) {
                 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
@@ -808,7 +504,7 @@
                 // If so add a to-object-wrapper argument filter.
                 // Else install a guard that will trigger a relink when the argument becomes primitive.
                 if (needsWrappedThis()) {
-                    if (isPrimitiveThis(request.getArguments()[1])) {
+                    if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) {
                         boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
                     } else {
                         guard = NashornGuards.getNonStrictFunctionGuard(this);
@@ -816,7 +512,7 @@
                 }
             }
         } else {
-            final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1));
+            final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1));
 
             if (NashornCallSiteDescriptor.isScope(desc)) {
                 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
@@ -840,39 +536,13 @@
      * 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(getBestSpecializedInvokeHandle(type)), bindName), type);
+        return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type)), bindName), type);
     }
 
     private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
         return bindName == null ? methodHandle : MH.insertArguments(methodHandle, 1, bindName);
     }
 
-    /**
-     * Convert this argument for non-strict functions according to ES 10.4.3
-     *
-     * @param thiz the this argument
-     *
-     * @return the converted this object
-     */
-    protected 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;
-    }
-
-    private static boolean isPrimitiveThis(Object obj) {
-        return obj instanceof String || obj instanceof ConsString ||
-               obj instanceof Number || obj instanceof Boolean;
-    }
-
     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 Feb 12 13:55:05 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Tue Feb 12 12:47:51 2013 +0100
@@ -25,9 +25,12 @@
 
 package jdk.nashorn.internal.runtime;
 
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
 
 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;
@@ -39,12 +42,15 @@
  * 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_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;
 
     /** Name of the function or "" */
     private final String name;
@@ -56,22 +62,21 @@
     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 */
+    /** 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;
-    /** Generic constructor used in {@link ScriptFunction#construct(Object, Object...)}. */
-    private MethodHandle genericConstructor;
     /** Specializations - see @SpecializedFunction */
     private MethodHandle[] invokeSpecializations;
-    /** Specializations - see @SpecializedFunction */
+    /** Specializations - see @SpecializedFunction. Same restrictions as for {@link #constructor} apply; only populate
+     * with method handles returned from {@link #composeConstructor(MethodHandle)}. */
     private MethodHandle[] constructSpecializations;
 
     /**
@@ -91,7 +96,7 @@
         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);
+        this.flags        = makeFlags(fn.needsCallee(), fn.isVarArg(), fn.isStrictMode(), false, true);
     }
 
     /**
@@ -102,55 +107,59 @@
      * @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;
+    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);
+    }
 
-        final MethodType type     = methodHandle.type();
-        final int paramCount      = type.parameterCount();
-        final boolean isVarArg    = type.parameterType(paramCount - 1).isArray();
+    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);
-        this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
+        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) {
-            this.arity--;
+            lArity--;
         }
 
         if (isConstructor(methodHandle)) {
+            assert isConstructor;
             if (!isVarArg) {
-                this.arity--;    // drop the boolean flag for arity
+                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.
+             * 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 = adaptConstructor(MH.insertArguments(methodHandle, 0, true));
+            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] = adaptConstructor(MH.insertArguments(specs[i], 0, true));
+                    this.constructSpecializations[i] = composeConstructor(MH.insertArguments(specs[i], 0, true));
                 }
             }
         } else {
             this.invoker                  = methodHandle;
-            this.constructor              = adaptConstructor(methodHandle);
+            this.constructor              = null; // delay composition of the constructor
             this.invokeSpecializations    = specs;
-            this.constructSpecializations = specs;
+            this.constructSpecializations = null; // delay composition of the constructors
         }
+        this.arity = lArity;
     }
 
     /**
      * Get the arity of the function.
      * @return the arity
      */
-    public int getArity() {
+    int getArity() {
         return arity;
     }
 
@@ -158,7 +167,7 @@
      * Set the arity of the function.
      * @param arity the arity
      */
-    public void setArity(int arity) {
+    void setArity(int arity) {
         this.arity = arity;
     }
 
@@ -166,24 +175,16 @@
      * Get the function name.
      * @return function name
      */
-    public String getName() {
+    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() {
+    String toSource() {
         if (source != null && token != 0) {
             return source.getString(Token.descPosition(token), Token.descLength(token));
         }
@@ -213,26 +214,10 @@
     }
 
     /**
-     * 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() {
+    boolean needsCallee() {
         return (flags & HAS_CALLEE) != 0;
     }
 
@@ -248,15 +233,23 @@
      * Returns true if this is a built-in function.
      * @return the built-in flag
      */
-    public boolean isBuiltin() {
+    private boolean isBuiltin() {
         return (flags & IS_BUILTIN) != 0;
     }
 
     /**
+     * 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
      */
-    public boolean isVarArg() {
+    private boolean isVarArg() {
         return (flags & IS_VARARGS) != 0;
     }
 
@@ -265,7 +258,7 @@
      * according to ECMA 10.4.3.
      * @return true if this argument must be an object
      */
-    public boolean needsWrappedThis() {
+    boolean needsWrappedThis() {
         return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
     }
 
@@ -273,72 +266,164 @@
      * Get the method handle used to invoke this function.
      * @return the invoke handle
      */
-    public MethodHandle getInvoker() {
+    MethodHandle getInvoker() {
         return invoker;
     }
 
+    MethodHandle getBestInvoker(final MethodType type) {
+        return SpecializedMethodChooser.candidateWithLowestWeight(type, invoker, invokeSpecializations);
+    }
+
     /**
      * Get the method handle used to invoke this function as a constructor.
      * @return the constructor handle
      */
-    public MethodHandle getConstructor() {
+    private MethodHandle getConstructor() {
+        if (constructor == null) {
+            constructor = composeConstructor(invoker);
+        }
+
         return constructor;
     }
 
-    /**
-     * Set the constructor method handle.
-     * @param constructor the constructor handle
-     */
-    public void setConstructor(MethodHandle constructor) {
-        this.constructor = constructor;
-        this.constructSpecializations = null;
+    MethodHandle getBestConstructor(MethodType descType) {
+        if (!isConstructor()) {
+            typeError("not.a.constructor", toSource());
+        }
+        return SpecializedMethodChooser.candidateWithLowestWeight(descType, getConstructor(), getConstructSpecializations());
     }
 
-    /**
-     * Get the method handle used to allocate a new object for this constructor.
-     * @return the allocator handle
-     */
-    public MethodHandle getAllocator() {
-        return allocator;
+    private MethodHandle composeConstructor(MethodHandle ctor) {
+        // 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));
+
+        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()) {
+            // ...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
      */
-    public MethodHandle getGenericInvoker() {
+    private MethodHandle getGenericInvoker() {
         if (genericInvoker == null) {
             assert invoker != null : "invoker is null";
-            genericInvoker = adaptMethodType(invoker);
+            genericInvoker = makeGenericMethod(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
+     * 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
      */
-    public MethodHandle getGenericConstructor() {
-        if (genericConstructor == null) {
-            assert constructor != null : "constructor is null";
-            genericConstructor = adaptMethodType(constructor);
+    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;
+
+        if (isVarArg()) {
+            if (needsCallee()) {
+                return genInvoker.invokeExact(fn, selfObj, args);
+            }
+            return genInvoker.invokeExact(selfObj, args);
         }
-        return genericConstructor;
+
+        final int paramCount = genInvoker.type().parameterCount();
+        if (needsCallee()) {
+            switch (paramCount) {
+            case 2:
+                return genInvoker.invokeExact(fn, selfObj);
+            case 3:
+                return genInvoker.invokeExact(fn, selfObj, getArg(args, 0));
+            case 4:
+                return genInvoker.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));
+            default:
+                return genInvoker.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
+            }
+        }
+
+        switch (paramCount) {
+        case 1:
+            return genInvoker.invokeExact(selfObj);
+        case 2:
+            return genInvoker.invokeExact(selfObj, getArg(args, 0));
+        case 3:
+            return genInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
+        case 4:
+            return genInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+        default:
+            return genInvoker.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
+        }
     }
 
-    /**
-     * Get the specialized invoke handles for this function.
-     * @return array of specialized invoke handles
-     */
-    public MethodHandle[] getInvokeSpecializations() {
-        return invokeSpecializations;
+    private static Object getArg(final Object[] args, final int i) {
+        return i < args.length ? args[i] : UNDEFINED;
+    }
+
+    private 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;
+            finalArgs[nextArg++] = fn;
+        } else {
+            assert fn == null;
+        }
+        finalArgs[nextArg++] = self;
+
+        // Don't add more args that there is argCount in the handle (including self and callee).
+        for (int i = 0; i < args.length && nextArg < argCount;) {
+            finalArgs[nextArg++] = args[i++];
+        }
+
+        // If we have fewer args than argCount, pad with undefined.
+        while (nextArg < argCount) {
+            finalArgs[nextArg++] = UNDEFINED;
+        }
+
+        return finalArgs;
     }
 
     /**
      * Get the specialized construct handles for this function.
      * @return array of specialized construct handles
      */
-    public MethodHandle[] getConstructSpecializations() {
+    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;
     }
 
@@ -352,12 +437,228 @@
         // and they're set when first called, so we enforce set-once here.
         if (this.invoker == null) {
             this.invoker     = invoker;
-            this.constructor = adaptConstructor(invoker);
+            this.constructor = null; // delay constructor composition
             this.allocator   = allocator;
         }
     }
 
     /**
+     * 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) {
+        assert args != null;
+        assert args.length > 0;
+        return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args));
+    }
+
+    /**
      * Convert boolean flags to int.
      * @param needsCallee needs-callee flag
      * @param isVarArg var-arg flag
@@ -365,7 +666,7 @@
      * @param isBuiltin builtin flag
      * @return int flags
      */
-    private static int makeFlags(final boolean needsCallee, final boolean isVarArg, final boolean isStrict, final boolean isBuiltin) {
+    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;
@@ -379,6 +680,10 @@
         if (isBuiltin) {
             flags |= IS_BUILTIN;
         }
+        if (isConstructor) {
+            flags |= IS_CONSTRUCTOR;
+        }
+
         return flags;
     }
 
@@ -412,6 +717,11 @@
         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...)}.
@@ -419,14 +729,14 @@
      * {@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>
+     *   <li>the first 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) {
+    private MethodHandle makeGenericMethod(final MethodHandle handle) {
         final MethodType type = handle.type();
         MethodType newType = type.generic();
         if (isVarArg()) {
@@ -439,17 +749,6 @@
     }
 
     /**
-     * Adapts a method handle to conform to requirements of a constructor. Right now this consists of making sure its
-     * return value is {@code Object}. We might consider moving the caller-this argument swap here too from
-     * {@link ScriptFunction#findNewMethod(org.dynalang.dynalink.CallSiteDescriptor)}.
-     * @param ctorHandle the constructor method handle
-     * @return adapted constructor method handle
-     */
-    private static MethodHandle adaptConstructor(final MethodHandle ctorHandle) {
-        return changeReturnTypeToObject(ctorHandle);
-    }
-
-    /**
      * 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
@@ -458,4 +757,56 @@
     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;
+        }
+        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);
+    }
+
+    @SuppressWarnings("unused")
+    private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
+        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) {
+            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;
+    }
+
+    @SuppressWarnings("unused")
+    private static Object newFilter(final Object result, final Object allocation) {
+        return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
+    }
+
+    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/ScriptRuntime.java	Tue Feb 12 13:55:05 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Tue Feb 12 12:47:51 2013 +0100
@@ -322,24 +322,6 @@
     }
 
     /**
-     * Constructor new object using given constructor function
-     * @param target ScriptFunction object.
-     * @param args   Constructor arguments.
-     * @return newly constructed object.
-     */
-    public static Object construct(final ScriptFunction target, final Object... args) {
-        try {
-            final ScriptObject allocation = (ScriptObject)target.allocate();
-            final Object result = target.construct(allocation, args);
-            return result instanceof ScriptObject ? result : allocation;
-        } catch (final RuntimeException | Error e) {
-            throw e;
-        } catch (final Throwable t) {
-            throw new RuntimeException(t);
-        }
-    }
-
-    /**
      * Generic implementation of ECMA 9.12 - SameValue algorithm
      *
      * @param x first value to compare
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java	Tue Feb 12 12:47:51 2013 +0100
@@ -0,0 +1,99 @@
+/*
+ * 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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/funcbind2.js	Tue Feb 12 12:47:51 2013 +0100
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test the functionality of Function.prototype.bind.
+ *
+ * @test
+ * @run
+ */
+
+function f(a, b) {
+    print("f: this=" + this + ", a=" + a + ", b=" + b);
+}
+function v(a, b) {
+    print("v: this=" + this + ", a=" + a + ", b=" + b + ", c=" + arguments[2]);
+}
+
+(f.bind(null))();
+(v.bind(null))();
+
+var boundThis = "boundThis";
+(f.bind(boundThis))();
+(v.bind(boundThis))();
+
+(f.bind(boundThis))("a0");
+(v.bind(boundThis))("a1");
+
+(f.bind(boundThis, "a2"))();
+(v.bind(boundThis, "a3"))();
+
+(f.bind(boundThis, "a4"))("b4");
+(v.bind(boundThis, "a5"))("b5");
+
+(f.bind(boundThis, "a6", "b6"))();
+(v.bind(boundThis, "a7", "b7"))();
+
+(f.bind(boundThis, "a8", "b8"))("c8"); // invoking with extra args after all were bound!
+(v.bind(boundThis, "a9", "b9"))("c9");
+
+(f.bind(boundThis, "a10", "b10", "c10"))(); // binding more args than it receives!
+(v.bind(boundThis, "a11", "b11", "c11"))();
+
+print("\nTest constructors\n");
+
+new (f.bind(boundThis))();
+new (v.bind(boundThis))();
+
+new (f.bind(boundThis))("a0");
+new (v.bind(boundThis))("a1");
+
+new (f.bind(boundThis, "a2"))();
+new (v.bind(boundThis, "a3"))();
+
+new (f.bind(boundThis, "a4"))("b4");
+new (v.bind(boundThis, "a5"))("b5");
+
+new (f.bind(boundThis, "a6", "b6"))();
+new (v.bind(boundThis, "a7", "b7"))();
+
+new (f.bind(boundThis, "a8", "b8"))("c8");
+new (v.bind(boundThis, "a9", "b9"))("c9");
+
+new (f.bind(boundThis, "a10", "b10", "c10"))();
+new (v.bind(boundThis, "a11", "b11", "c11"))();
+
+print("\nTest double binding\n");
+
+(f.bind(boundThis).bind("thisIsIgnored"))();
+new (f.bind("thisIsIgnored").bind("thisIsIgnoredToo"))();
+new (f.bind("thisIsIgnored", "a12").bind("thisIsIgnoredToo"))();
+
+(v.bind(boundThis).bind("thisIsIgnored"))();
+new (v.bind("thisIsIgnored").bind("thisIsIgnoredToo"))();
+new (v.bind("thisIsIgnored", "a13").bind("thisIsIgnoredToo"))();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/funcbind2.js.EXPECTED	Tue Feb 12 12:47:51 2013 +0100
@@ -0,0 +1,42 @@
+f: this=[object global], a=undefined, b=undefined
+v: this=[object global], a=undefined, b=undefined, c=undefined
+f: this=boundThis, a=undefined, b=undefined
+v: this=boundThis, a=undefined, b=undefined, c=undefined
+f: this=boundThis, a=a0, b=undefined
+v: this=boundThis, a=a1, b=undefined, c=undefined
+f: this=boundThis, a=a2, b=undefined
+v: this=boundThis, a=a3, b=undefined, c=undefined
+f: this=boundThis, a=a4, b=b4
+v: this=boundThis, a=a5, b=b5, c=undefined
+f: this=boundThis, a=a6, b=b6
+v: this=boundThis, a=a7, b=b7, c=undefined
+f: this=boundThis, a=a8, b=b8
+v: this=boundThis, a=a9, b=b9, c=c9
+f: this=boundThis, a=a10, b=b10
+v: this=boundThis, a=a11, b=b11, c=c11
+
+Test constructors
+
+f: this=[object Object], a=undefined, b=undefined
+v: this=[object Object], a=undefined, b=undefined, c=undefined
+f: this=[object Object], a=a0, b=undefined
+v: this=[object Object], a=a1, b=undefined, c=undefined
+f: this=[object Object], a=a2, b=undefined
+v: this=[object Object], a=a3, b=undefined, c=undefined
+f: this=[object Object], a=a4, b=b4
+v: this=[object Object], a=a5, b=b5, c=undefined
+f: this=[object Object], a=a6, b=b6
+v: this=[object Object], a=a7, b=b7, c=undefined
+f: this=[object Object], a=a8, b=b8
+v: this=[object Object], a=a9, b=b9, c=c9
+f: this=[object Object], a=a10, b=b10
+v: this=[object Object], a=a11, b=b11, c=c11
+
+Test double binding
+
+f: this=boundThis, a=undefined, b=undefined
+f: this=[object Object], a=undefined, b=undefined
+f: this=[object Object], a=a12, b=undefined
+v: this=boundThis, a=undefined, b=undefined, c=undefined
+v: this=[object Object], a=undefined, b=undefined, c=undefined
+v: this=[object Object], a=a13, b=undefined, c=undefined
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/funcbind3.js	Tue Feb 12 12:47:51 2013 +0100
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test the assumptions about bound function prototypes
+ *
+ * @test
+ * @run
+ */
+
+function printAssumption(__x__) {
+  print(__x__ + ": " + eval(__x__));
+}
+
+function f() { }
+
+var b = f.bind(null)
+
+var x = new f()
+var y = new b()
+
+printAssumption("x instanceof f")
+printAssumption("x instanceof b")
+printAssumption("y instanceof f")
+printAssumption("y instanceof b")
+
+print("\nChanging prototype\n");
+
+f.prototype=new Object()
+
+printAssumption("x instanceof f")
+printAssumption("x instanceof b")
+printAssumption("y instanceof f")
+printAssumption("y instanceof b")
+
+print("\Bound function prototype\n");
+
+printAssumption("f.hasOwnProperty('prototype')")
+printAssumption("b.hasOwnProperty('prototype')")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/funcbind3.js.EXPECTED	Tue Feb 12 12:47:51 2013 +0100
@@ -0,0 +1,15 @@
+x instanceof f: true
+x instanceof b: true
+y instanceof f: true
+y instanceof b: true
+
+Changing prototype
+
+x instanceof f: false
+x instanceof b: false
+y instanceof f: false
+y instanceof b: false
+Bound function prototype
+
+f.hasOwnProperty('prototype'): true
+b.hasOwnProperty('prototype'): false