src/jdk/nashorn/internal/runtime/ScriptFunction.java
Print this page
@@ -32,10 +32,11 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
+import jdk.nashorn.internal.codegen.ScriptFunctionData;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
@@ -68,12 +69,10 @@
private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
- /** method handle to arity setter for this ScriptFunction */
- public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class);
/** method handle to scope getter for this ScriptFunction */
public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
/** Should specialized function and specialized constructors for the builtin be used if available? */
private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
@@ -86,14 +85,20 @@
/** Start position and length in source. */
private final long token;
/** Reference to code for this method. */
- private final MethodHandle invokeHandle;
+ private final MethodHandle invoker;
/** Reference to code for this method when called to create "new" object */
- protected MethodHandle constructHandle;
+ protected MethodHandle constructor;
+
+ /** Generic invoker to used in {@link #invoke(Object, Object...)}. */
+ private MethodHandle genericInvoker;
+
+ /** Generic constructor used in {@link #construct(Object, Object...)}. */
+ private MethodHandle genericConstructor;
/** Reference to constructor prototype. */
protected Object prototype;
/** Constructor to create a new instance. */
@@ -127,11 +132,11 @@
final String name,
final MethodHandle methodHandle,
final PropertyMap map,
final ScriptObject scope,
final MethodHandle[] specs) {
- this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs);
+ this(name, methodHandle, map, scope, needsCallee(methodHandle), specs);
}
/**
* Heuristic to figure out if the method handle has a callee argument. If it's type is either
* {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
@@ -154,152 +159,138 @@
}
/**
* Constructor
*
- * @param name function name
+ * @param data static function data
* @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
* @param map property map
* @param scope scope
- * @param source the source
- * @param token token
* @param allocator method handle to this function's allocator - see JO$ classes
- * @param allocatorMap property map to be used for all constructors
- * @param needsCallee does this method use the {@code callee} variable
- * @param specs specialized version of this function - other method handles
*/
protected ScriptFunction(
- final String name,
+ final ScriptFunctionData data,
final MethodHandle methodHandle,
final PropertyMap map,
final ScriptObject scope,
- final Source source,
- final long token,
- final MethodHandle allocator,
- final PropertyMap allocatorMap,
- final boolean needsCallee,
- final MethodHandle[] specs) {
+ final MethodHandle allocator) {
+
+ super(map);
+
+ if (Context.DEBUG) {
+ constructorCount++;
+ }
- this(name, methodHandle, map, scope, source, token, needsCallee, specs);
+ this.name = data.getName();
+ this.source = data.getSource();
+ this.token = data.getToken();
+ this.arity = data.getArity();
+ this.scope = scope;
- //this is the internal constructor
+ if(data.needsCallee()) {
+ setHasCalleeParameter();
+ }
+ this.invoker = methodHandle;
+ this.constructor = methodHandle;
this.allocator = allocator;
- this.allocatorMap = allocatorMap;
+ this.allocatorMap = data.getAllocatorMap();
}
/**
* Constructor
*
* @param name function name
* @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
* @param map property map
* @param scope scope
- * @param source the source
- * @param token token
* @param needsCallee does this method use the {@code callee} variable
* @param specs specialized version of this function - other method handles
*/
protected ScriptFunction(
final String name,
final MethodHandle methodHandle,
final PropertyMap map,
final ScriptObject scope,
- final Source source,
- final long token,
final boolean needsCallee,
final MethodHandle[] specs) {
super(map);
if (Context.DEBUG) {
constructorCount++;
}
this.name = name;
- this.source = source;
- this.token = token;
+ this.source = null;
+ this.token = 0;
this.scope = scope;
if(needsCallee) {
setHasCalleeParameter();
}
final MethodType type = methodHandle.type();
final int paramCount = type.parameterCount();
final boolean isVarArg = type.parameterType(paramCount - 1).isArray();
- final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg));
-
this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
if (needsCallee && !isVarArg) {
this.arity--;
}
- if (scope != null) {
- this.invokeHandle = mh;
- this.constructHandle = mh;
- } else if (isConstructor(mh)) {
+ if (isConstructor(methodHandle)) {
if (!isVarArg) {
this.arity--; // drop the boolean flag for arity
}
/*
* We insert a boolean argument to tell if the method was invoked as
* constructor or not if the method handle's first argument is boolean.
*/
- this.invokeHandle = MH.insertArguments(mh, 0, false);
- this.constructHandle = MH.insertArguments(mh, 0, true);
+ this.invoker = MH.insertArguments(methodHandle, 0, false);
+ this.constructor = MH.insertArguments(methodHandle, 0, true);
if (specs != null) {
this.invokeSpecializations = new MethodHandle[specs.length];
this.constructSpecializations = new MethodHandle[specs.length];
for (int i = 0; i < specs.length; i++) {
this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false);
this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
}
}
} else {
- this.invokeHandle = mh;
- this.constructHandle = mh;
+ this.invoker = methodHandle;
+ this.constructor = methodHandle;
this.invokeSpecializations = specs;
this.constructSpecializations = specs;
}
}
/**
- * Takes a method type, and returns a (potentially different method type) that the method handles used by
- * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and
- * {@link #construct(Object, Object...)}. The returned method type will be sure to return {@code Object}, and will
- * have all its parameters turned into {@code Object} as well, except for the following ones:
+ * Takes a method handle, and returns a potentially different method handle that can be used in
+ * {@link #invoke(Object, Object...)} or {@link #construct(Object, Object...)}. The returned method handle
+ * will be sure to return {@code Object}, and will have all its parameters turned into {@code Object} as well,
+ * except for the following ones:
* <ul>
- * <li>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor
- * invocation,</li>
* <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
- * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be
- * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li>
- * @param type the original type
- * @param hasCallee true if the function uses the callee argument
- * @param isVarArg if the function is a vararg
- * @return the new type, conforming to the rules above.
- */
- private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) {
- // Generify broadly
- MethodType newType = type.generic().changeReturnType(Object.class);
- if(isVarArg) {
- // Change back to vararg if we over-generified it
+ * <li>the second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
+ * (callee) as an argument</li>
+ * </ul>
+ *
+ * @param handle the original method handle
+ * @return the new handle, conforming to the rules above.
+ */
+ private MethodHandle adaptMethodType(final MethodHandle handle) {
+ final MethodType type = handle.type();
+ MethodType newType = type.generic();
+ if (isVarArg(handle)) {
newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
}
- final boolean hasBoolean = type.parameterType(0) == boolean.class;
- if(hasBoolean) {
- // Restore the initial boolean argument
- newType = newType.changeParameterType(0, boolean.class);
- }
- if(hasCallee) {
- // Restore the ScriptFunction argument
- newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class);
+ if (hasCalleeParameter()) {
+ newType = newType.changeParameterType(1, ScriptFunction.class);
}
- return newType;
+ return type.equals(newType) ? handle : handle.asType(newType);
}
@Override
public String getClassName() {
return "Function";
@@ -370,47 +361,51 @@
public Object invoke(final Object self, final Object... arguments) throws Throwable {
if (Context.DEBUG) {
invokes++;
}
+ if (genericInvoker == null) {
+ genericInvoker = adaptMethodType(invoker);
+ }
+
final Object selfObj = convertThisObject(self);
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
- if (isVarArg(invokeHandle)) {
+ if (isVarArg(genericInvoker)) {
if (hasCalleeParameter()) {
- return invokeHandle.invokeExact(selfObj, this, args);
+ return genericInvoker.invokeExact(selfObj, this, args);
}
- return invokeHandle.invokeExact(selfObj, args);
+ return genericInvoker.invokeExact(selfObj, args);
}
- final int paramCount = invokeHandle.type().parameterCount();
+ final int paramCount = genericInvoker.type().parameterCount();
if (hasCalleeParameter()) {
switch (paramCount) {
case 2:
- return invokeHandle.invokeExact(selfObj, this);
+ return genericInvoker.invokeExact(selfObj, this);
case 3:
- return invokeHandle.invokeExact(selfObj, this, getArg(args, 0));
+ return genericInvoker.invokeExact(selfObj, this, getArg(args, 0));
case 4:
- return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
+ return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
case 5:
- return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return invokeHandle.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
+ return genericInvoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
}
}
switch (paramCount) {
case 1:
- return invokeHandle.invokeExact(selfObj);
+ return genericInvoker.invokeExact(selfObj);
case 2:
- return invokeHandle.invokeExact(selfObj, getArg(args, 0));
+ return genericInvoker.invokeExact(selfObj, getArg(args, 0));
case 3:
- return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
+ return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
case 4:
- return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return invokeHandle.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
+ return genericInvoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
}
}
private static Object getArg(final Object[] args, final int i) {
return i < args.length ? args[i] : UNDEFINED;
@@ -422,48 +417,51 @@
* @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 (constructHandle == null) {
+ if (constructor == null) {
typeError("not.a.constructor", ScriptRuntime.safeToString(this));
}
- if (isVarArg(constructHandle)) {
+ if (genericConstructor == null) {
+ genericConstructor = adaptMethodType(constructor);
+ }
+ if (isVarArg(genericConstructor)) {
if (hasCalleeParameter()) {
- return constructHandle.invokeExact(self, this, args);
+ return genericConstructor.invokeExact(self, this, args);
}
- return constructHandle.invokeExact(self, args);
+ return genericConstructor.invokeExact(self, args);
}
- final int paramCount = constructHandle.type().parameterCount();
+ final int paramCount = genericConstructor.type().parameterCount();
if (hasCalleeParameter()) {
switch (paramCount) {
case 2:
- return constructHandle.invokeExact(self, this);
+ return genericConstructor.invokeExact(self, this);
case 3:
- return constructHandle.invokeExact(self, this, getArg(args, 0));
+ return genericConstructor.invokeExact(self, this, getArg(args, 0));
case 4:
- return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
+ return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
case 5:
- return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return constructHandle.invokeWithArguments(withArguments(self, this, args));
+ return genericConstructor.invokeWithArguments(withArguments(self, this, args));
}
}
switch(paramCount) {
case 1:
- return constructHandle.invokeExact(self);
+ return genericConstructor.invokeExact(self);
case 2:
- return constructHandle.invokeExact(self, getArg(args, 0));
+ return genericConstructor.invokeExact(self, getArg(args, 0));
case 3:
- return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1));
+ return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1));
case 4:
- return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return constructHandle.invokeWithArguments(withArguments(self, null, args));
+ return genericConstructor.invokeWithArguments(withArguments(self, null, args));
}
}
private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) {
return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function
@@ -575,11 +573,11 @@
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append(super.toString())
.append(" [ ")
- .append(invokeHandle)
+ .append(invoker)
.append(", ")
.append((name == null || name.isEmpty()) ? "<anonymous>" : name);
if (source != null) {
sb.append(" @ ")
@@ -704,11 +702,11 @@
* method handle for this ScriptFunction
* @see SpecializedFunction
* @return invokeHandle
*/
public final MethodHandle getInvokeHandle() {
- return invokeHandle;
+ return invoker;
}
/**
* Return the invoke handle bound to a given ScriptObject self reference.
* If callee parameter is required result is rebound to this.
@@ -738,19 +736,19 @@
/**
* Get a method handle to the constructor for this function
* @return constructor handle
*/
public final MethodHandle getConstructHandle() {
- return constructHandle;
+ return constructor;
}
/**
* Set a method handle to the constructor for this function
* @param constructHandle constructor handle
*/
public final void setConstructHandle(final MethodHandle constructHandle) {
- this.constructHandle = constructHandle;
+ this.constructor = constructHandle;
this.constructSpecializations = null;
}
/**
* Get the name for this function
@@ -765,11 +763,11 @@
* null checking invokeHandle
*
* @return true if this needs compilation
*/
public final boolean needsCompilation() {
- return invokeHandle == null;
+ return invoker == null;
}
/**
* Get token for this function
* @return token