# HG changeset patch # User attila # Date 1360669671 -3600 # Node ID df28320aa0809e2e04a528a48892012ce5b9ae89 # Parent 1bafb74d17b274469085dad757b5f8e52a7aef39 8007900: Function binding is inefficient Reviewed-by: jlaskey, lagergren diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java --- /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; + } +} diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/objects/Global.java --- 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); diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java --- 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; } diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java --- 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); - } - } } diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/runtime/ArgumentSetter.java --- 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); /** diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java --- 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(); diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java --- 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); diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java --- 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: * * * @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)); + } + } diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java --- 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 diff -r 1bafb74d17b2 -r df28320aa080 nashorn/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java --- /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; + } +} diff -r 1bafb74d17b2 -r df28320aa080 nashorn/test/script/basic/funcbind2.js --- /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"))(); diff -r 1bafb74d17b2 -r df28320aa080 nashorn/test/script/basic/funcbind2.js.EXPECTED --- /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 diff -r 1bafb74d17b2 -r df28320aa080 nashorn/test/script/basic/funcbind3.js --- /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')") diff -r 1bafb74d17b2 -r df28320aa080 nashorn/test/script/basic/funcbind3.js.EXPECTED --- /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