8007900: Function binding is inefficient
Reviewed-by: jlaskey, lagergren
This commit is contained in:
parent
b98a77d26c
commit
d50e3823e4
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -413,7 +413,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ScriptFunction newScriptFunction(final String name, final MethodHandle handle, final ScriptObject scope, final boolean strict) {
|
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
|
@Override
|
||||||
@ -1339,7 +1339,6 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
|||||||
|
|
||||||
// initialize global function properties
|
// initialize global function properties
|
||||||
this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL);
|
this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL);
|
||||||
((ScriptFunction)this.eval).setArity(1);
|
|
||||||
|
|
||||||
this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT);
|
this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT);
|
||||||
this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
|
this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
|
||||||
|
@ -56,8 +56,8 @@ public final class NativeStrictArguments extends ScriptObject {
|
|||||||
PropertyMap map = PropertyMap.newMap(NativeStrictArguments.class);
|
PropertyMap map = PropertyMap.newMap(NativeStrictArguments.class);
|
||||||
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH);
|
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH);
|
||||||
// In strict mode, the caller and callee properties should throw TypeError
|
// 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, "caller");
|
||||||
map = ScriptFunctionImpl.newThrowerProperty(map, "callee", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE);
|
map = ScriptFunctionImpl.newThrowerProperty(map, "callee");
|
||||||
nasgenmap$ = map;
|
nasgenmap$ = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,30 +26,27 @@
|
|||||||
package jdk.nashorn.internal.objects;
|
package jdk.nashorn.internal.objects;
|
||||||
|
|
||||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
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.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
|
||||||
|
|
||||||
import jdk.nashorn.internal.runtime.ScriptFunctionData;
|
|
||||||
import jdk.nashorn.internal.runtime.GlobalFunctions;
|
import jdk.nashorn.internal.runtime.GlobalFunctions;
|
||||||
import jdk.nashorn.internal.runtime.Property;
|
import jdk.nashorn.internal.runtime.Property;
|
||||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||||
|
import jdk.nashorn.internal.runtime.ScriptFunctionData;
|
||||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
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.Lookup;
|
||||||
import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concrete implementation of ScriptFunction. This sets correct map for the
|
* Concrete implementation of ScriptFunction. This sets correct map for the
|
||||||
* function objects -- to expose properties like "prototype", "length" etc.
|
* function objects -- to expose properties like "prototype", "length" etc.
|
||||||
*/
|
*/
|
||||||
public class ScriptFunctionImpl extends ScriptFunction {
|
public class ScriptFunctionImpl extends ScriptFunction {
|
||||||
|
// property map for strict mode functions
|
||||||
private static final MethodHandle BOUND_FUNCTION = findOwnMH("boundFunction", Object.class, ScriptFunction.class, Object.class, Object[].class, Object.class, Object[].class);
|
private static final PropertyMap strictmodemap$;
|
||||||
private static final MethodHandle BOUND_CONSTRUCTOR = findOwnMH("boundConstructor", Object.class, ScriptFunction.class, Object[].class, Object.class, Object[].class);
|
// property map for bound functions
|
||||||
|
private static final PropertyMap boundfunctionmap$;
|
||||||
|
// property map for non-strict, non-bound functions.
|
||||||
private static final PropertyMap nasgenmap$;
|
private static final PropertyMap nasgenmap$;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,7 +58,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
* @param specs specialized versions of this method, if available, null otherwise
|
* @param specs specialized versions of this method, if available, null otherwise
|
||||||
*/
|
*/
|
||||||
ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) {
|
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();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +72,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
* @param specs specialized versions of this method, if available, null otherwise
|
* @param specs specialized versions of this method, if available, null otherwise
|
||||||
*/
|
*/
|
||||||
ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs) {
|
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();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,9 +85,10 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
* @param specs specialized versions of this method, if available, null otherwise
|
* @param specs specialized versions of this method, if available, null otherwise
|
||||||
* @param strict are we in strict mode
|
* @param strict are we in strict mode
|
||||||
* @param builtin is this a built-in function
|
* @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) {
|
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);
|
super(name, methodHandle, getMap(strict), scope, specs, strict, builtin, isConstructor);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,9 +104,16 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
public ScriptFunctionImpl(final ScriptFunctionData data, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle allocator) {
|
public ScriptFunctionImpl(final ScriptFunctionData data, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle allocator) {
|
||||||
super(data, getMap(data.isStrict()), scope);
|
super(data, getMap(data.isStrict()), scope);
|
||||||
// Set method handles in script data
|
// 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();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +123,8 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null);
|
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);
|
map = Lookup.newProperty(map, "name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null);
|
||||||
nasgenmap$ = map;
|
nasgenmap$ = map;
|
||||||
|
strictmodemap$ = createStrictModeMap(nasgenmap$);
|
||||||
|
boundfunctionmap$ = createBoundFunctionMap(strictmodemap$);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function object representing TypeErrorThrower
|
// function object representing TypeErrorThrower
|
||||||
@ -126,9 +133,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
static synchronized ScriptFunction getTypeErrorThrower() {
|
static synchronized ScriptFunction getTypeErrorThrower() {
|
||||||
if (typeErrorThrower == null) {
|
if (typeErrorThrower == null) {
|
||||||
//name handle
|
//name handle
|
||||||
final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false);
|
final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false, false);
|
||||||
// clear constructor handle...
|
|
||||||
func.setConstructHandle(null);
|
|
||||||
func.setPrototype(UNDEFINED);
|
func.setPrototype(UNDEFINED);
|
||||||
typeErrorThrower = func;
|
typeErrorThrower = func;
|
||||||
}
|
}
|
||||||
@ -137,28 +142,24 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add a new property that throws TypeError on get as well as set
|
// 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) {
|
static synchronized PropertyMap newThrowerProperty(final PropertyMap map, final String name) {
|
||||||
return map.newProperty(name, flags, -1, Lookup.TYPE_ERROR_THROWER_GETTER, Lookup.TYPE_ERROR_THROWER_SETTER);
|
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 createStrictModeMap(final PropertyMap functionMap) {
|
||||||
private static PropertyMap strictmodemap$;
|
return newThrowerProperty(newThrowerProperty(functionMap, "arguments"), "caller");
|
||||||
|
}
|
||||||
|
|
||||||
// Choose the map based on strict mode!
|
// Choose the map based on strict mode!
|
||||||
private static PropertyMap getMap(final boolean strict) {
|
private static PropertyMap getMap(final boolean strict) {
|
||||||
if (strict) {
|
return strict ? strictmodemap$ : nasgenmap$;
|
||||||
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 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
|
// Instance of this class is used as global anonymous function which
|
||||||
@ -175,23 +176,6 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
return new AnonymousFunction();
|
return new AnonymousFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory method for non-constructor built-in functions
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
func.setPrototype(UNDEFINED);
|
|
||||||
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method for non-constructor built-in functions
|
* Factory method for non-constructor built-in functions
|
||||||
*
|
*
|
||||||
@ -201,7 +185,10 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
* @return new ScriptFunction
|
* @return new ScriptFunction
|
||||||
*/
|
*/
|
||||||
static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
|
static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
|
||||||
return makeFunction(name, methodHandle, specs, false);
|
final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, false, true, false);
|
||||||
|
func.setPrototype(UNDEFINED);
|
||||||
|
|
||||||
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -216,57 +203,27 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to create a bound function. See also
|
* Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we
|
||||||
* {@link NativeFunction#bind(Object, Object...)} method implementation.
|
* 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 thiz this reference to bind
|
* @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
|
||||||
* @param args arguments to bind
|
* @return a function with the specified self and parameters bound.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected ScriptFunction makeBoundFunction(final Object thiz, final Object[] args) {
|
protected ScriptFunction makeBoundFunction(Object self, Object[] args) {
|
||||||
Object[] allArgs = args;
|
return super.makeBoundFunction(self, args);
|
||||||
|
|
||||||
if (allArgs == null) {
|
|
||||||
allArgs = ScriptRuntime.EMPTY_ARRAY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final Object boundThiz = convertThisObject(thiz);
|
/**
|
||||||
final MethodHandle boundMethod = MH.insertArguments(BOUND_FUNCTION, 0, this, boundThiz, allArgs);
|
* This method is used to create a bound function based on this function.
|
||||||
final ScriptFunction boundFunc = makeFunction("", boundMethod, null, true);
|
*
|
||||||
|
* @param data the {@code ScriptFunctionData} specifying the functions immutable portion.
|
||||||
MethodHandle consHandle = this.getConstructHandle();
|
* @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.
|
||||||
if (consHandle != null) {
|
*/
|
||||||
consHandle = MH.insertArguments(BOUND_CONSTRUCTOR, 0, this, allArgs);
|
@Override
|
||||||
}
|
protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) {
|
||||||
|
return new BoundScriptFunctionImpl(data, getTargetFunction());
|
||||||
boundFunc.setConstructHandle(consHandle);
|
|
||||||
int newArity = this.getArity();
|
|
||||||
if (newArity != -1) {
|
|
||||||
newArity -= Math.min(newArity, allArgs.length);
|
|
||||||
}
|
|
||||||
boundFunc.setArity(newArity);
|
|
||||||
|
|
||||||
return boundFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return Object.prototype - used by "allocate"
|
// return Object.prototype - used by "allocate"
|
||||||
@ -288,12 +245,4 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
|||||||
setUserAccessors("caller", func, func);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package jdk.nashorn.internal.runtime;
|
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;
|
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
|
||||||
|
|
||||||
@ -36,10 +36,10 @@ public final class ArgumentSetter {
|
|||||||
private ArgumentSetter() {}
|
private ArgumentSetter() {}
|
||||||
|
|
||||||
/** Method handle for setting a function argument at a given index in an arguments object. Used from generated bytecode */
|
/** 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 */
|
/** 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);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -308,7 +308,7 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
|
|||||||
*
|
*
|
||||||
* @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found.
|
* @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);
|
PropertyMap newMap = checkHistory(property);
|
||||||
final String key = property.getKey();
|
final String key = property.getKey();
|
||||||
|
|
||||||
|
@ -34,13 +34,10 @@ import java.lang.invoke.MethodHandle;
|
|||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
|
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.objects.annotations.SpecializedFunction;
|
||||||
import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
|
import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
|
||||||
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
|
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
|
||||||
import jdk.nashorn.internal.runtime.linker.NashornGuards;
|
import jdk.nashorn.internal.runtime.linker.NashornGuards;
|
||||||
import jdk.nashorn.internal.runtime.options.Options;
|
|
||||||
import org.dynalang.dynalink.CallSiteDescriptor;
|
import org.dynalang.dynalink.CallSiteDescriptor;
|
||||||
import org.dynalang.dynalink.linker.GuardedInvocation;
|
import org.dynalang.dynalink.linker.GuardedInvocation;
|
||||||
import org.dynalang.dynalink.linker.LinkRequest;
|
import org.dynalang.dynalink.linker.LinkRequest;
|
||||||
@ -63,18 +60,13 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
|
public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
|
||||||
|
|
||||||
/** Method handle for allocate function for this ScriptFunction */
|
/** Method handle for allocate function for this ScriptFunction */
|
||||||
public static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
|
static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
|
||||||
|
|
||||||
private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
|
|
||||||
|
|
||||||
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
|
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
|
||||||
|
|
||||||
/** method handle to scope getter for this ScriptFunction */
|
/** method handle to scope getter for this ScriptFunction */
|
||||||
public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
|
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;
|
private final ScriptFunctionData data;
|
||||||
|
|
||||||
/** Reference to constructor prototype. */
|
/** Reference to constructor prototype. */
|
||||||
@ -100,9 +92,10 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
final ScriptObject scope,
|
final ScriptObject scope,
|
||||||
final MethodHandle[] specs,
|
final MethodHandle[] specs,
|
||||||
final boolean strict,
|
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 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isInstance(final ScriptObject instance) {
|
public boolean isInstance(final ScriptObject instance) {
|
||||||
if (!(prototype instanceof ScriptObject)) {
|
final Object basePrototype = getTargetFunction().getPrototype();
|
||||||
typeError("prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype));
|
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()) {
|
for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
|
||||||
if (proto == prototype) {
|
if (proto == basePrototype) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,11 +146,18 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the arity of this ScriptFunction
|
* Returns the target function for this function. If the function was not created using
|
||||||
* @return arity
|
* {@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() {
|
protected ScriptFunction getTargetFunction() {
|
||||||
return data.getArity();
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isBoundFunction() {
|
||||||
|
return getTargetFunction() != this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -175,14 +176,6 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
return data.isStrict();
|
return data.isStrict();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
|
||||||
* according to ECMA 10.4.3.
|
* according to ECMA 10.4.3.
|
||||||
@ -203,126 +196,7 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
if (Context.DEBUG) {
|
if (Context.DEBUG) {
|
||||||
invokes++;
|
invokes++;
|
||||||
}
|
}
|
||||||
|
return data.invoke(this, self, arguments);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -331,26 +205,14 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
*
|
*
|
||||||
* @return a new instance of the {@link ScriptObject} whose allocator this is
|
* @return a new instance of the {@link ScriptObject} whose allocator this is
|
||||||
*/
|
*/
|
||||||
public Object allocate() {
|
@SuppressWarnings("unused")
|
||||||
|
private Object allocate() {
|
||||||
if (Context.DEBUG) {
|
if (Context.DEBUG) {
|
||||||
allocations++;
|
allocations++;
|
||||||
}
|
}
|
||||||
|
assert !isBoundFunction(); // allocate never invoked on bound functions
|
||||||
|
|
||||||
if (getConstructHandle() == null) {
|
final ScriptObject object = data.allocate();
|
||||||
typeError("not.a.constructor", ScriptRuntime.safeToString(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptObject object = null;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (object != null) {
|
if (object != null) {
|
||||||
if (prototype instanceof ScriptObject) {
|
if (prototype instanceof ScriptObject) {
|
||||||
@ -372,13 +234,17 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
protected abstract ScriptObject getObjectPrototype();
|
protected abstract ScriptObject getObjectPrototype();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a version of this function bound to a specific "self" and other argumentss
|
* Creates a version of this function bound to a specific "self" and other arguments, as per
|
||||||
* @param self the self to bind the function to
|
* {@code Function.prototype.bind} functionality in ECMAScript 5.1 section 15.3.4.5.
|
||||||
* @param args other arguments (beside self) to bind the function to
|
* @param self the self to bind to this function. Can be null (in which case, null is bound as this).
|
||||||
* @return the bound function
|
* @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
|
@Override
|
||||||
public final String safeToString() {
|
public final String safeToString() {
|
||||||
@ -417,80 +283,13 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
return prototype;
|
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
|
* Return the most appropriate invoke handle if there are specializations
|
||||||
* @param type most specific method type to look for invocation with
|
* @param type most specific method type to look for invocation with
|
||||||
* @return invoke method handle
|
* @return invoke method handle
|
||||||
*/
|
*/
|
||||||
public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) {
|
private final MethodHandle getBestInvoker(final MethodType type) {
|
||||||
return candidateWithLowestWeight(type, getInvokeHandle(), data.getInvokeSpecializations());
|
return data.getBestInvoker(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -524,33 +323,6 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
return data.needsCallee() ? MH.bindTo(methodHandle, this) : methodHandle;
|
return data.needsCallee() ? MH.bindTo(methodHandle, this) : methodHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* Get the name for this function
|
||||||
* @return the name
|
* @return the name
|
||||||
@ -569,14 +341,6 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
return data.getInvoker() == null;
|
return data.getInvoker() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get token for this function
|
|
||||||
* @return token
|
|
||||||
*/
|
|
||||||
public final long getToken() {
|
|
||||||
return data.getToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the scope for this function
|
* Get the scope for this function
|
||||||
* @return the scope
|
* @return the scope
|
||||||
@ -618,7 +382,7 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
*/
|
*/
|
||||||
public static int G$length(final Object self) {
|
public static int G$length(final Object self) {
|
||||||
if (self instanceof ScriptFunction) {
|
if (self instanceof ScriptFunction) {
|
||||||
return ((ScriptFunction)self).getArity();
|
return ((ScriptFunction)self).data.getArity();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -681,81 +445,13 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
|
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();
|
final MethodType type = desc.getMethodType();
|
||||||
|
return new GuardedInvocation(pairArguments(data.getBestConstructor(type), type), null, NashornGuards.getFunctionGuard(this));
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static Object wrapFilter(final Object obj) {
|
private static Object wrapFilter(final Object obj) {
|
||||||
if (obj instanceof ScriptObject || !isPrimitiveThis(obj)) {
|
if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj);
|
return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj);
|
||||||
@ -792,7 +488,7 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
MethodHandle guard = null;
|
MethodHandle guard = null;
|
||||||
|
|
||||||
if (data.needsCallee()) {
|
if (data.needsCallee()) {
|
||||||
final MethodHandle callHandle = getBestSpecializedInvokeHandle(type);
|
final MethodHandle callHandle = getBestInvoker(type);
|
||||||
|
|
||||||
if (NashornCallSiteDescriptor.isScope(desc)) {
|
if (NashornCallSiteDescriptor.isScope(desc)) {
|
||||||
// Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
|
// Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
|
||||||
@ -808,7 +504,7 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
// If so add a to-object-wrapper argument filter.
|
// If so add a to-object-wrapper argument filter.
|
||||||
// Else install a guard that will trigger a relink when the argument becomes primitive.
|
// Else install a guard that will trigger a relink when the argument becomes primitive.
|
||||||
if (needsWrappedThis()) {
|
if (needsWrappedThis()) {
|
||||||
if (isPrimitiveThis(request.getArguments()[1])) {
|
if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) {
|
||||||
boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
|
boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
|
||||||
} else {
|
} else {
|
||||||
guard = NashornGuards.getNonStrictFunctionGuard(this);
|
guard = NashornGuards.getNonStrictFunctionGuard(this);
|
||||||
@ -816,7 +512,7 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1));
|
final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1));
|
||||||
|
|
||||||
if (NashornCallSiteDescriptor.isScope(desc)) {
|
if (NashornCallSiteDescriptor.isScope(desc)) {
|
||||||
// Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
|
// Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
|
||||||
@ -840,39 +536,13 @@ public abstract class ScriptFunction extends ScriptObject {
|
|||||||
* These don't want a callee parameter, so bind that. Name binding is optional.
|
* These don't want a callee parameter, so bind that. Name binding is optional.
|
||||||
*/
|
*/
|
||||||
MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
|
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) {
|
private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
|
||||||
return bindName == null ? methodHandle : MH.insertArguments(methodHandle, 1, 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) {
|
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||||
final Class<?> own = ScriptFunction.class;
|
final Class<?> own = ScriptFunction.class;
|
||||||
final MethodType mt = MH.type(rtype, types);
|
final MethodType mt = MH.type(rtype, types);
|
||||||
|
@ -25,9 +25,12 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.runtime;
|
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 static jdk.nashorn.internal.runtime.linker.Lookup.MH;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import jdk.nashorn.internal.ir.FunctionNode;
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
import jdk.nashorn.internal.parser.Token;
|
import jdk.nashorn.internal.parser.Token;
|
||||||
@ -39,12 +42,15 @@ import jdk.nashorn.internal.parser.TokenType;
|
|||||||
* constants array to reduce function instantiation overhead during runtime.
|
* constants array to reduce function instantiation overhead during runtime.
|
||||||
*/
|
*/
|
||||||
public final class ScriptFunctionData {
|
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
|
// per-function object flags
|
||||||
private static final int IS_STRICT = 0b0000_0001;
|
private static final int IS_STRICT = 0b0000_0001;
|
||||||
private static final int IS_BUILTIN = 0b0000_0010;
|
private static final int IS_BUILTIN = 0b0000_0010;
|
||||||
private static final int HAS_CALLEE = 0b0000_0100;
|
private static final int HAS_CALLEE = 0b0000_0100;
|
||||||
private static final int IS_VARARGS = 0b0000_1000;
|
private static final int IS_VARARGS = 0b0000_1000;
|
||||||
|
private static final int IS_CONSTRUCTOR = 0b0001_0000;
|
||||||
|
|
||||||
/** Name of the function or "" */
|
/** Name of the function or "" */
|
||||||
private final String name;
|
private final String name;
|
||||||
@ -56,22 +62,21 @@ public final class ScriptFunctionData {
|
|||||||
private final long token;
|
private final long token;
|
||||||
/** Number of expected arguments, either taken from FunctionNode or calculated from method handle signature*/
|
/** Number of expected arguments, either taken from FunctionNode or calculated from method handle signature*/
|
||||||
private int arity;
|
private int arity;
|
||||||
/** Does this function need a callee argument? */
|
|
||||||
private final int flags;
|
private final int flags;
|
||||||
|
|
||||||
/** Reference to code for this method. */
|
/** Reference to code for this method. */
|
||||||
private MethodHandle invoker;
|
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;
|
private MethodHandle constructor;
|
||||||
/** Constructor to create a new instance. */
|
/** Constructor to create a new instance. */
|
||||||
private MethodHandle allocator;
|
private MethodHandle allocator;
|
||||||
/** Generic invoker to used in {@link ScriptFunction#invoke(Object, Object...)}. */
|
/** Generic invoker to used in {@link ScriptFunction#invoke(Object, Object...)}. */
|
||||||
private MethodHandle genericInvoker;
|
private MethodHandle genericInvoker;
|
||||||
/** Generic constructor used in {@link ScriptFunction#construct(Object, Object...)}. */
|
|
||||||
private MethodHandle genericConstructor;
|
|
||||||
/** Specializations - see @SpecializedFunction */
|
/** Specializations - see @SpecializedFunction */
|
||||||
private MethodHandle[] invokeSpecializations;
|
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;
|
private MethodHandle[] constructSpecializations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,7 +96,7 @@ public final class ScriptFunctionData {
|
|||||||
this.allocatorMap = allocatorMap;
|
this.allocatorMap = allocatorMap;
|
||||||
this.token = Token.toDesc(TokenType.FUNCTION, position, length);
|
this.token = Token.toDesc(TokenType.FUNCTION, position, length);
|
||||||
this.arity = fn.getParameters().size();
|
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 @@ public final class ScriptFunctionData {
|
|||||||
* @param strict strict flag
|
* @param strict strict flag
|
||||||
* @param builtin builtin flag
|
* @param builtin builtin flag
|
||||||
*/
|
*/
|
||||||
public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
|
public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) {
|
||||||
this.name = name;
|
this(name, null, 0L, methodHandle, specs, strict, builtin, isConstructor);
|
||||||
this.source = null;
|
}
|
||||||
this.token = 0;
|
|
||||||
|
|
||||||
final MethodType type = methodHandle.type();
|
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) {
|
||||||
final int paramCount = type.parameterCount();
|
this.name = name;
|
||||||
final boolean isVarArg = type.parameterType(paramCount - 1).isArray();
|
this.source = source;
|
||||||
|
this.token = token;
|
||||||
|
|
||||||
|
final boolean isVarArg = isVarArg(methodHandle);
|
||||||
final boolean needsCallee = needsCallee(methodHandle);
|
final boolean needsCallee = needsCallee(methodHandle);
|
||||||
|
|
||||||
this.flags = makeFlags(needsCallee, isVarArg, strict, builtin);
|
this.flags = makeFlags(needsCallee, isVarArg, strict, builtin, isConstructor);
|
||||||
this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
|
int lArity = isVarArg ? -1 : methodHandle.type().parameterCount() - 1; //drop the self param for arity
|
||||||
|
|
||||||
if (needsCallee && !isVarArg) {
|
if (needsCallee && !isVarArg) {
|
||||||
this.arity--;
|
lArity--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isConstructor(methodHandle)) {
|
if (isConstructor(methodHandle)) {
|
||||||
|
assert isConstructor;
|
||||||
if (!isVarArg) {
|
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
|
* We insert a boolean argument to tell if the method was invoked as constructor or not if the method
|
||||||
* constructor or not if the method handle's first argument is boolean.
|
* handle's first argument is boolean.
|
||||||
*/
|
*/
|
||||||
this.invoker = MH.insertArguments(methodHandle, 0, false);
|
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) {
|
if (specs != null) {
|
||||||
this.invokeSpecializations = new MethodHandle[specs.length];
|
this.invokeSpecializations = new MethodHandle[specs.length];
|
||||||
this.constructSpecializations = new MethodHandle[specs.length];
|
this.constructSpecializations = new MethodHandle[specs.length];
|
||||||
for (int i = 0; i < specs.length; i++) {
|
for (int i = 0; i < specs.length; i++) {
|
||||||
this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false);
|
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 {
|
} else {
|
||||||
this.invoker = methodHandle;
|
this.invoker = methodHandle;
|
||||||
this.constructor = adaptConstructor(methodHandle);
|
this.constructor = null; // delay composition of the constructor
|
||||||
this.invokeSpecializations = specs;
|
this.invokeSpecializations = specs;
|
||||||
this.constructSpecializations = specs;
|
this.constructSpecializations = null; // delay composition of the constructors
|
||||||
}
|
}
|
||||||
|
this.arity = lArity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the arity of the function.
|
* Get the arity of the function.
|
||||||
* @return the arity
|
* @return the arity
|
||||||
*/
|
*/
|
||||||
public int getArity() {
|
int getArity() {
|
||||||
return arity;
|
return arity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +167,7 @@ public final class ScriptFunctionData {
|
|||||||
* Set the arity of the function.
|
* Set the arity of the function.
|
||||||
* @param arity the arity
|
* @param arity the arity
|
||||||
*/
|
*/
|
||||||
public void setArity(int arity) {
|
void setArity(int arity) {
|
||||||
this.arity = arity;
|
this.arity = arity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,24 +175,16 @@ public final class ScriptFunctionData {
|
|||||||
* Get the function name.
|
* Get the function name.
|
||||||
* @return function name
|
* @return function name
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
String getName() {
|
||||||
return name;
|
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
|
* 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]}
|
* exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
|
||||||
* @return string representation of this function's source
|
* @return string representation of this function's source
|
||||||
*/
|
*/
|
||||||
public String toSource() {
|
String toSource() {
|
||||||
if (source != null && token != 0) {
|
if (source != null && token != 0) {
|
||||||
return source.getString(Token.descPosition(token), Token.descLength(token));
|
return source.getString(Token.descPosition(token), Token.descLength(token));
|
||||||
}
|
}
|
||||||
@ -212,27 +213,11 @@ public final class ScriptFunctionData {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the allocator property map.
|
|
||||||
* @return the allocator map
|
|
||||||
*/
|
|
||||||
public PropertyMap getAllocatorMap() {
|
|
||||||
return allocatorMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the function's parse token.
|
|
||||||
* @return the token
|
|
||||||
*/
|
|
||||||
public long getToken() {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the function needs a callee argument.
|
* Returns true if the function needs a callee argument.
|
||||||
* @return the needsCallee flag
|
* @return the needsCallee flag
|
||||||
*/
|
*/
|
||||||
public boolean needsCallee() {
|
boolean needsCallee() {
|
||||||
return (flags & HAS_CALLEE) != 0;
|
return (flags & HAS_CALLEE) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,15 +233,23 @@ public final class ScriptFunctionData {
|
|||||||
* Returns true if this is a built-in function.
|
* Returns true if this is a built-in function.
|
||||||
* @return the built-in flag
|
* @return the built-in flag
|
||||||
*/
|
*/
|
||||||
public boolean isBuiltin() {
|
private boolean isBuiltin() {
|
||||||
return (flags & IS_BUILTIN) != 0;
|
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.
|
* Returns true if this is a var-arg function.
|
||||||
* @return the var-arg flag
|
* @return the var-arg flag
|
||||||
*/
|
*/
|
||||||
public boolean isVarArg() {
|
private boolean isVarArg() {
|
||||||
return (flags & IS_VARARGS) != 0;
|
return (flags & IS_VARARGS) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +258,7 @@ public final class ScriptFunctionData {
|
|||||||
* according to ECMA 10.4.3.
|
* according to ECMA 10.4.3.
|
||||||
* @return true if this argument must be an object
|
* @return true if this argument must be an object
|
||||||
*/
|
*/
|
||||||
public boolean needsWrappedThis() {
|
boolean needsWrappedThis() {
|
||||||
return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
|
return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,72 +266,164 @@ public final class ScriptFunctionData {
|
|||||||
* Get the method handle used to invoke this function.
|
* Get the method handle used to invoke this function.
|
||||||
* @return the invoke handle
|
* @return the invoke handle
|
||||||
*/
|
*/
|
||||||
public MethodHandle getInvoker() {
|
MethodHandle getInvoker() {
|
||||||
return invoker;
|
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.
|
* Get the method handle used to invoke this function as a constructor.
|
||||||
* @return the constructor handle
|
* @return the constructor handle
|
||||||
*/
|
*/
|
||||||
public MethodHandle getConstructor() {
|
private MethodHandle getConstructor() {
|
||||||
|
if (constructor == null) {
|
||||||
|
constructor = composeConstructor(invoker);
|
||||||
|
}
|
||||||
|
|
||||||
return constructor;
|
return constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
MethodHandle getBestConstructor(MethodType descType) {
|
||||||
* Set the constructor method handle.
|
if (!isConstructor()) {
|
||||||
* @param constructor the constructor handle
|
typeError("not.a.constructor", toSource());
|
||||||
*/
|
}
|
||||||
public void setConstructor(MethodHandle constructor) {
|
return SpecializedMethodChooser.candidateWithLowestWeight(descType, getConstructor(), getConstructSpecializations());
|
||||||
this.constructor = constructor;
|
|
||||||
this.constructSpecializations = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private MethodHandle composeConstructor(MethodHandle ctor) {
|
||||||
* Get the method handle used to allocate a new object for this constructor.
|
// If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
|
||||||
* @return the allocator handle
|
// "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
|
||||||
public MethodHandle getAllocator() {
|
// always returns Object.
|
||||||
return allocator;
|
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.
|
* Get an adapted version of the invoker handle that only uses {@code Object} as parameter and return types.
|
||||||
* @return the generic invoke handle
|
* @return the generic invoke handle
|
||||||
*/
|
*/
|
||||||
public MethodHandle getGenericInvoker() {
|
private MethodHandle getGenericInvoker() {
|
||||||
if (genericInvoker == null) {
|
if (genericInvoker == null) {
|
||||||
assert invoker != null : "invoker is null";
|
assert invoker != null : "invoker is null";
|
||||||
genericInvoker = adaptMethodType(invoker);
|
genericInvoker = makeGenericMethod(invoker);
|
||||||
}
|
}
|
||||||
return genericInvoker;
|
return genericInvoker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an adapted version of the constructor handle that only uses {@code Object} as parameter and return types.
|
* Execute this script function.
|
||||||
* @return the generic constructor handle
|
* @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() {
|
Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
|
||||||
if (genericConstructor == null) {
|
final MethodHandle genInvoker = getGenericInvoker();
|
||||||
assert constructor != null : "constructor is null";
|
final Object selfObj = convertThisObject(self);
|
||||||
genericConstructor = adaptMethodType(constructor);
|
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
|
||||||
|
|
||||||
|
if (isVarArg()) {
|
||||||
|
if (needsCallee()) {
|
||||||
|
return genInvoker.invokeExact(fn, selfObj, args);
|
||||||
}
|
}
|
||||||
return genericConstructor;
|
return genInvoker.invokeExact(selfObj, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
final int paramCount = genInvoker.type().parameterCount();
|
||||||
* Get the specialized invoke handles for this function.
|
if (needsCallee()) {
|
||||||
* @return array of specialized invoke handles
|
switch (paramCount) {
|
||||||
*/
|
case 2:
|
||||||
public MethodHandle[] getInvokeSpecializations() {
|
return genInvoker.invokeExact(fn, selfObj);
|
||||||
return invokeSpecializations;
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
* Get the specialized construct handles for this function.
|
||||||
* @return array of specialized construct handles
|
* @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;
|
return constructSpecializations;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,11 +437,227 @@ public final class ScriptFunctionData {
|
|||||||
// and they're set when first called, so we enforce set-once here.
|
// and they're set when first called, so we enforce set-once here.
|
||||||
if (this.invoker == null) {
|
if (this.invoker == null) {
|
||||||
this.invoker = invoker;
|
this.invoker = invoker;
|
||||||
this.constructor = adaptConstructor(invoker);
|
this.constructor = null; // delay constructor composition
|
||||||
this.allocator = allocator;
|
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.
|
* Convert boolean flags to int.
|
||||||
* @param needsCallee needs-callee flag
|
* @param needsCallee needs-callee flag
|
||||||
@ -365,7 +666,7 @@ public final class ScriptFunctionData {
|
|||||||
* @param isBuiltin builtin flag
|
* @param isBuiltin builtin flag
|
||||||
* @return int flags
|
* @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;
|
int flags = 0;
|
||||||
if (needsCallee) {
|
if (needsCallee) {
|
||||||
flags |= HAS_CALLEE;
|
flags |= HAS_CALLEE;
|
||||||
@ -379,6 +680,10 @@ public final class ScriptFunctionData {
|
|||||||
if (isBuiltin) {
|
if (isBuiltin) {
|
||||||
flags |= IS_BUILTIN;
|
flags |= IS_BUILTIN;
|
||||||
}
|
}
|
||||||
|
if (isConstructor) {
|
||||||
|
flags |= IS_CONSTRUCTOR;
|
||||||
|
}
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,6 +717,11 @@ public final class ScriptFunctionData {
|
|||||||
return type.parameterType(0) == ScriptFunction.class;
|
return type.parameterType(0) == ScriptFunction.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isVarArg(MethodHandle methodHandle) {
|
||||||
|
final MethodType type = methodHandle.type();
|
||||||
|
return type.parameterType(type.parameterCount() - 1).isArray();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a method handle, and returns a potentially different method handle that can be used in
|
* 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...)}.
|
* {@link ScriptFunction#invoke(Object, Object...)} or {@link ScriptFunction#construct(Object, Object...)}.
|
||||||
@ -419,14 +729,14 @@ public final class ScriptFunctionData {
|
|||||||
* {@code Object} as well, except for the following ones:
|
* {@code Object} as well, except for the following ones:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
|
* <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
|
* <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
|
||||||
* (callee) as an argument</li>
|
* (callee) as an argument.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param handle the original method handle
|
* @param handle the original method handle
|
||||||
* @return the new handle, conforming to the rules above.
|
* @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();
|
final MethodType type = handle.type();
|
||||||
MethodType newType = type.generic();
|
MethodType newType = type.generic();
|
||||||
if (isVarArg()) {
|
if (isVarArg()) {
|
||||||
@ -438,17 +748,6 @@ public final class ScriptFunctionData {
|
|||||||
return type.equals(newType) ? handle : handle.asType(newType);
|
return type.equals(newType) ? handle : handle.asType(newType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* 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.
|
* {@code Object}, the handle is returned unchanged.
|
||||||
@ -458,4 +757,56 @@ public final class ScriptFunctionData {
|
|||||||
private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
|
private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
|
||||||
return MH.asType(mh, mh.type().changeReturnType(Object.class));
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -321,24 +321,6 @@ public final class ScriptRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* Generic implementation of ECMA 9.12 - SameValue algorithm
|
||||||
*
|
*
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
94
nashorn/test/script/basic/funcbind2.js
Normal file
94
nashorn/test/script/basic/funcbind2.js
Normal file
@ -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"))();
|
42
nashorn/test/script/basic/funcbind2.js.EXPECTED
Normal file
42
nashorn/test/script/basic/funcbind2.js.EXPECTED
Normal file
@ -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
|
59
nashorn/test/script/basic/funcbind3.js
Normal file
59
nashorn/test/script/basic/funcbind3.js
Normal file
@ -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')")
|
15
nashorn/test/script/basic/funcbind3.js.EXPECTED
Normal file
15
nashorn/test/script/basic/funcbind3.js.EXPECTED
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user