--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Tue Feb 05 22:07:04 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Wed Feb 06 10:31:58 2013 +0100
@@ -30,6 +30,8 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+
+import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.Property;
@@ -37,7 +39,6 @@
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
-import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.linker.Lookup;
import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
@@ -46,109 +47,68 @@
* function objects -- to expose properties like "prototype", "length" etc.
*/
public class ScriptFunctionImpl extends ScriptFunction {
- // per-function object flags
- private static final int IS_STRICT = 0b0000_0001;
- private static final int IS_BUILTIN = 0b0000_0010;
- private static final int HAS_CALLEE = 0b0000_0100;
-
- // set this function to be a builtin function
- private void setIsBuiltin() {
- flags |= IS_BUILTIN;
- }
-
- // set this function to be a ECMAScript strict function
- private void setIsStrict() {
- flags |= IS_STRICT;
- }
private static final MethodHandle BOUND_FUNCTION = findOwnMH("boundFunction", Object.class, ScriptFunction.class, Object.class, Object[].class, Object.class, Object[].class);
private static final MethodHandle BOUND_CONSTRUCTOR = findOwnMH("boundConstructor", Object.class, ScriptFunction.class, Object[].class, Object.class, Object[].class);
private static final PropertyMap nasgenmap$;
- private int flags;
-
/**
- * Constructor
- *
- * Called by Nasgen generated code, no membercount, use the default map
- * Creates builtin functions only
+ * Constructor called by Nasgen generated code, no membercount, use the default map.
+ * Creates builtin functions only.
*
* @param name name of function
* @param invokeHandle handle for invocation
* @param specs specialized versions of this method, if available, null otherwise
*/
ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) {
- this(name, invokeHandle, nasgenmap$, specs);
- }
-
- /**
- * Constructor
- *
- * Called by Nasgen generated code, no membercount, use the default map
- * Creates builtin functions only
- *
- * @param name name of function
- * @param methodHandle handle for invocation
- * @param map initial property map
- * @param specs specialized versions of this method, if available, null otherwise
- */
- ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final PropertyMap map, final MethodHandle[] specs) {
- super(name, methodHandle, (nasgenmap$ == map) ? nasgenmap$ : map.addAll(nasgenmap$), null, null, 0, false, specs);
- this.setIsBuiltin();
+ super(name, invokeHandle, nasgenmap$, null, specs, false, true);
init();
}
/**
- * Constructor
+ * Constructor called by Nasgen generated code, no membercount, use the map passed as argument.
+ * Creates builtin functions only.
*
- * Called by Global.newScriptFunction (runtime)
+ * @param name name of function
+ * @param invokeHandle handle for invocation
+ * @param map initial property map
+ * @param specs specialized versions of this method, if available, null otherwise
+ */
+ ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs) {
+ super(name, invokeHandle, map.addAll(nasgenmap$), null, specs, false, true);
+ init();
+ }
+
+ /**
+ * Constructor called by Global.newScriptFunction (runtime).
*
* @param name name of function
* @param methodHandle handle for invocation
* @param scope scope object
+ * @param specs specialized versions of this method, if available, null otherwise
* @param strict are we in strict mode
- * @param specs specialized versions of this method, if available, null otherwise
+ * @param builtin is this a built-in function
*/
- ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final boolean strict, final MethodHandle[] specs) {
- super(name, methodHandle, getMap(strict), scope, specs);
- if (strict) {
- this.setIsStrict();
- }
+ ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
+ super(name, methodHandle, getMap(strict), scope, specs, strict, builtin);
init();
}
/**
- * Constructor
+ * Constructor called by (compiler) generated code for {@link ScriptObject}s.
+ * Code is generated by {@link FunctionObjectCreator}
*
- * Called by (compiler) generated code for {@link ScriptObject}s. Code is
- * generated by {@link FunctionObjectCreator}
- *
- * TODO this is a horrible constructor - can we do it with fewer args?
- *
- * @param name name of function
+ * @param data static function data
* @param methodHandle handle for invocation
* @param scope scope object
- * @param source source
- * @param token token
* @param allocator instance constructor for function
- * @param allocatorMap initial map that constructor will keep reference to for future instantiations
- * @param needCallee does the function use the {@code callee} variable
- * @param strict are we in strict mode
*/
- public ScriptFunctionImpl(
- final String name,
- final MethodHandle methodHandle,
- final ScriptObject scope,
- final Source source,
- final long token,
- final MethodHandle allocator,
- final PropertyMap allocatorMap,
- final boolean needCallee,
- final boolean strict) {
- super(name, methodHandle, getMap(strict), scope, source, token, allocator, allocatorMap, needCallee, null);
- if (strict) {
- this.setIsStrict();
+ public ScriptFunctionImpl(final ScriptFunctionData data, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle allocator) {
+ super(data, getMap(data.isStrict()), scope);
+ // Set method handles in script data
+ if (data.getInvoker() == null) {
+ data.setMethodHandles(methodHandle, allocator);
}
init();
}
@@ -167,11 +127,11 @@
static synchronized ScriptFunction getTypeErrorThrower() {
if (typeErrorThrower == null) {
//name handle
- final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, false, null);
+ final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false);
// clear constructor handle...
- func.constructHandle = null;
- func.prototype = UNDEFINED;
- typeErrorThrower = func;
+ func.setConstructHandle(null);
+ func.setPrototype(UNDEFINED);
+ typeErrorThrower = func;
}
return typeErrorThrower;
@@ -216,28 +176,8 @@
return new AnonymousFunction();
}
- @Override
- public final boolean isStrict() {
- return (flags & IS_STRICT) != 0;
- }
-
- @Override
- public final boolean hasCalleeParameter() {
- return (flags & HAS_CALLEE) != 0;
- }
-
- @Override
- protected void setHasCalleeParameter() {
- flags |= HAS_CALLEE;
- }
-
- @Override
- public final boolean isBuiltin() {
- return (flags & IS_BUILTIN) != 0;
- }
-
/**
- * Factory method for non-constructor functions
+ * Factory method for non-constructor built-in functions
*
* @param name function name
* @param methodHandle handle for invocation
@@ -246,9 +186,7 @@
* @return new ScriptFunction
*/
public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict) {
- final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, strict, specs);
-
- func.setIsBuiltin();
+ final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, strict, true);
func.setConstructHandle(null);
func.setPrototype(UNDEFINED);
@@ -256,7 +194,7 @@
}
/**
- * Factory method for non-constructor functions
+ * Factory method for non-constructor built-in functions
*
* @param name function name
* @param methodHandle handle for invocation
@@ -268,7 +206,7 @@
}
/**
- * Factory method for non-constructor functions
+ * Factory method for non-constructor built-in functions
*
* @param name function name
* @param methodHandle handle for invocation
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Feb 05 22:07:04 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Wed Feb 06 10:31:58 2013 +0100
@@ -37,7 +37,6 @@
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
-import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
@@ -70,144 +69,53 @@
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
- /** method handle to arity setter for this ScriptFunction */
- public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class);
/** method handle to scope getter for this ScriptFunction */
public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
/** Should specialized function and specialized constructors for the builtin be used if available? */
private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
- /** Name of function or null. */
- private final String name;
-
- /** Source of function. */
- private final Source source;
-
- /** Start position and length in source. */
- private final long token;
-
- /** Reference to code for this method. */
- private final MethodHandle invokeHandle;
-
- /** Reference to code for this method when called to create "new" object */
- protected MethodHandle constructHandle;
+ private final ScriptFunctionData data;
/** Reference to constructor prototype. */
protected Object prototype;
- /** Constructor to create a new instance. */
- private MethodHandle allocator;
-
- /** Map for new instance constructor. */
- private PropertyMap allocatorMap;
-
/** The parent scope. */
private final ScriptObject scope;
- /** Specializations - see @SpecializedFunction */
- private MethodHandle[] invokeSpecializations;
-
- /** Specializations - see @SpecializedFunction */
- private MethodHandle[] constructSpecializations;
-
- /** This field is either computed in constructor or set explicitly by calling setArity method. */
- private int arity;
-
/**
* Constructor
*
- * @param name function name
- * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
- * @param map property map
- * @param scope scope
- * @param specs specialized version of this function - other method handles
+ * @param name function name
+ * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
+ * @param map property map
+ * @param scope scope
+ * @param specs specialized version of this function - other method handles
+ *
*/
protected ScriptFunction(
final String name,
final MethodHandle methodHandle,
final PropertyMap map,
final ScriptObject scope,
- final MethodHandle[] specs) {
- this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs);
- }
+ final MethodHandle[] specs,
+ final boolean strict,
+ final boolean builtin) {
- /**
- * Heuristic to figure out if the method handle has a callee argument. If it's type is either
- * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
- * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
- * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
- * they also always receive a callee.
- * @param methodHandle the examined method handle
- * @return true if the method handle expects a callee, false otherwise
- */
- private static boolean needsCallee(MethodHandle methodHandle) {
- final MethodType type = methodHandle.type();
- final int len = type.parameterCount();
- if(len == 0) {
- return false;
- }
- if(type.parameterType(0) == boolean.class) {
- return len > 2 && type.parameterType(2) == ScriptFunction.class;
- }
- return len > 1 && type.parameterType(1) == ScriptFunction.class;
+ this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin), map, scope);
}
/**
* Constructor
*
- * @param name function name
- * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
+ * @param data static function data
* @param map property map
* @param scope scope
- * @param source the source
- * @param token token
- * @param allocator method handle to this function's allocator - see JO$ classes
- * @param allocatorMap property map to be used for all constructors
- * @param needsCallee does this method use the {@code callee} variable
- * @param specs specialized version of this function - other method handles
*/
protected ScriptFunction(
- final String name,
- final MethodHandle methodHandle,
+ final ScriptFunctionData data,
final PropertyMap map,
- final ScriptObject scope,
- final Source source,
- final long token,
- final MethodHandle allocator,
- final PropertyMap allocatorMap,
- final boolean needsCallee,
- final MethodHandle[] specs) {
-
- this(name, methodHandle, map, scope, source, token, needsCallee, specs);
-
- //this is the internal constructor
-
- this.allocator = allocator;
- this.allocatorMap = allocatorMap;
- }
-
- /**
- * Constructor
- *
- * @param name function name
- * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
- * @param map property map
- * @param scope scope
- * @param source the source
- * @param token token
- * @param needsCallee does this method use the {@code callee} variable
- * @param specs specialized version of this function - other method handles
- */
- protected ScriptFunction(
- final String name,
- final MethodHandle methodHandle,
- final PropertyMap map,
- final ScriptObject scope,
- final Source source,
- final long token,
- final boolean needsCallee,
- final MethodHandle[] specs) {
+ final ScriptObject scope) {
super(map);
@@ -215,89 +123,8 @@
constructorCount++;
}
- this.name = name;
- this.source = source;
- this.token = token;
+ this.data = data;
this.scope = scope;
- if(needsCallee) {
- setHasCalleeParameter();
- }
-
- final MethodType type = methodHandle.type();
- final int paramCount = type.parameterCount();
- final boolean isVarArg = type.parameterType(paramCount - 1).isArray();
-
- final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg));
-
- this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
-
- if (needsCallee && !isVarArg) {
- this.arity--;
- }
-
- if (scope != null) {
- this.invokeHandle = mh;
- this.constructHandle = mh;
- } else if (isConstructor(mh)) {
- if (!isVarArg) {
- this.arity--; // drop the boolean flag for arity
- }
- /*
- * We insert a boolean argument to tell if the method was invoked as
- * constructor or not if the method handle's first argument is boolean.
- */
- this.invokeHandle = MH.insertArguments(mh, 0, false);
- this.constructHandle = MH.insertArguments(mh, 0, true);
-
- if (specs != null) {
- this.invokeSpecializations = new MethodHandle[specs.length];
- this.constructSpecializations = new MethodHandle[specs.length];
- for (int i = 0; i < specs.length; i++) {
- this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false);
- this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
- }
- }
- } else {
- this.invokeHandle = mh;
- this.constructHandle = mh;
- this.invokeSpecializations = specs;
- this.constructSpecializations = specs;
- }
- }
-
- /**
- * Takes a method type, and returns a (potentially different method type) that the method handles used by
- * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and
- * {@link #construct(Object, Object...)}. The returned method type will be sure to return {@code Object}, and will
- * have all its parameters turned into {@code Object} as well, except for the following ones:
- * <ul>
- * <li>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor
- * invocation,</li>
- * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
- * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be
- * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li>
- * @param type the original type
- * @param hasCallee true if the function uses the callee argument
- * @param isVarArg if the function is a vararg
- * @return the new type, conforming to the rules above.
- */
- private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) {
- // Generify broadly
- MethodType newType = type.generic().changeReturnType(Object.class);
- if(isVarArg) {
- // Change back to vararg if we over-generified it
- newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
- }
- final boolean hasBoolean = type.parameterType(0) == boolean.class;
- if(hasBoolean) {
- // Restore the initial boolean argument
- newType = newType.changeParameterType(0, boolean.class);
- }
- if(hasCallee) {
- // Restore the ScriptFunction argument
- newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class);
- }
- return newType;
}
@Override
@@ -329,7 +156,7 @@
* @return arity
*/
public final int getArity() {
- return arity;
+ return data.getArity();
}
/**
@@ -337,27 +164,32 @@
* @param arity arity
*/
public final void setArity(final int arity) {
- this.arity = arity;
+ data.setArity(arity);
}
/**
* Is this a ECMAScript 'use strict' function?
* @return true if function is in strict mode
*/
- public abstract boolean isStrict();
+ public boolean isStrict() {
+ return data.isStrict();
+ }
/**
* Is this a ECMAScript built-in function (like parseInt, Array.isArray) ?
* @return true if built-in
*/
- public abstract boolean isBuiltin();
+ public boolean isBuiltin() {
+ return data.isBuiltin();
+ }
/**
- * Is this a non-strict and not-built-in script function?
- * @return true if neither strict nor built-in
+ * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
+ * according to ECMA 10.4.3.
+ * @return true if this argument must be an object
*/
- public boolean isNonStrictFunction() {
- return !isStrict() && !isBuiltin();
+ public boolean needsWrappedThis() {
+ return data.needsWrappedThis();
}
/**
@@ -372,43 +204,44 @@
invokes++;
}
+ final MethodHandle invoker = data.getGenericInvoker();
final Object selfObj = convertThisObject(self);
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
- if (isVarArg(invokeHandle)) {
- if (hasCalleeParameter()) {
- return invokeHandle.invokeExact(selfObj, this, args);
+ if (data.isVarArg()) {
+ if (data.needsCallee()) {
+ return invoker.invokeExact(selfObj, this, args);
}
- return invokeHandle.invokeExact(selfObj, args);
+ return invoker.invokeExact(selfObj, args);
}
- final int paramCount = invokeHandle.type().parameterCount();
- if (hasCalleeParameter()) {
+ final int paramCount = invoker.type().parameterCount();
+ if (data.needsCallee()) {
switch (paramCount) {
case 2:
- return invokeHandle.invokeExact(selfObj, this);
+ return invoker.invokeExact(selfObj, this);
case 3:
- return invokeHandle.invokeExact(selfObj, this, getArg(args, 0));
+ return invoker.invokeExact(selfObj, this, getArg(args, 0));
case 4:
- return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
+ return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
case 5:
- return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return invokeHandle.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
+ return invoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
}
}
switch (paramCount) {
case 1:
- return invokeHandle.invokeExact(selfObj);
+ return invoker.invokeExact(selfObj);
case 2:
- return invokeHandle.invokeExact(selfObj, getArg(args, 0));
+ return invoker.invokeExact(selfObj, getArg(args, 0));
case 3:
- return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
+ return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
case 4:
- return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return invokeHandle.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
+ return invoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
}
}
@@ -424,44 +257,45 @@
* @throws Throwable if there is an exception/error with the constructor invocation or thrown from it
*/
public Object construct(final Object self, final Object... args) throws Throwable {
- if (constructHandle == null) {
+ if (data.getConstructor() == null) {
typeError("not.a.constructor", ScriptRuntime.safeToString(this));
}
- if (isVarArg(constructHandle)) {
- if (hasCalleeParameter()) {
- return constructHandle.invokeExact(self, this, args);
+ final MethodHandle constructor = data.getGenericConstructor();
+ if (data.isVarArg()) {
+ if (data.needsCallee()) {
+ return constructor.invokeExact(self, this, args);
}
- return constructHandle.invokeExact(self, args);
+ return constructor.invokeExact(self, args);
}
- final int paramCount = constructHandle.type().parameterCount();
- if (hasCalleeParameter()) {
+ final int paramCount = constructor.type().parameterCount();
+ if (data.needsCallee()) {
switch (paramCount) {
case 2:
- return constructHandle.invokeExact(self, this);
+ return constructor.invokeExact(self, this);
case 3:
- return constructHandle.invokeExact(self, this, getArg(args, 0));
+ return constructor.invokeExact(self, this, getArg(args, 0));
case 4:
- return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
+ return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
case 5:
- return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return constructHandle.invokeWithArguments(withArguments(self, this, args));
+ return constructor.invokeWithArguments(withArguments(self, this, args));
}
}
switch(paramCount) {
case 1:
- return constructHandle.invokeExact(self);
+ return constructor.invokeExact(self);
case 2:
- return constructHandle.invokeExact(self, getArg(args, 0));
+ return constructor.invokeExact(self, getArg(args, 0));
case 3:
- return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1));
+ return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1));
case 4:
- return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return constructHandle.invokeWithArguments(withArguments(self, null, args));
+ return constructor.invokeWithArguments(withArguments(self, null, args));
}
}
@@ -509,9 +343,9 @@
ScriptObject object = null;
- if (allocator != null) {
+ if (data.getAllocator() != null) {
try {
- object = (ScriptObject)allocator.invokeExact(allocatorMap);
+ object = (ScriptObject)data.getAllocator().invokeExact(data.getAllocatorMap());
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
@@ -546,25 +380,6 @@
*/
public abstract ScriptFunction makeBoundFunction(Object self, Object[] args);
- /**
- * Test if a methodHandle refers to a constructor.
- * @param methodHandle MethodHandle to test.
- * @return True if method is a constructor.
- */
- private static boolean isConstructor(final MethodHandle methodHandle) {
- return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
- }
-
- /**
- * Test if a methodHandle refers to a variable argument method.
- * @param methodHandle MethodHandle to test.
- * @return True if variable arguments.
- */
- public boolean isVarArg(final MethodHandle methodHandle) {
- return hasCalleeParameter()
- ? methodHandle.type().parameterCount() == 3 && methodHandle.type().parameterType(2).isArray()
- : methodHandle.type().parameterCount() == 2 && methodHandle.type().parameterType(1).isArray();
- }
@Override
public final String safeToString() {
@@ -573,23 +388,7 @@
@Override
public String toString() {
- final StringBuilder sb = new StringBuilder();
-
- sb.append(super.toString())
- .append(" [ ")
- .append(invokeHandle)
- .append(", ")
- .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
-
- if (source != null) {
- sb.append(" @ ")
- .append(source.getName())
- .append(':')
- .append(source.getLine(Token.descPosition(token)));
- }
- sb.append(" ]");
-
- return sb.toString();
+ return data.toString();
}
/**
@@ -598,11 +397,7 @@
* @return string representation of this function's source
*/
public final String toSource() {
- if (source != null && token != 0) {
- return source.getString(Token.descPosition(token), Token.descLength(token));
- }
-
- return "function " + (name == null ? "" : name) + "() { [native code] }";
+ return data.toSource();
}
/**
@@ -696,7 +491,7 @@
* @return invoke method handle
*/
public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) {
- return candidateWithLowestWeight(type, getInvokeHandle(), invokeSpecializations);
+ return candidateWithLowestWeight(type, getInvokeHandle(), data.getInvokeSpecializations());
}
/**
@@ -706,7 +501,7 @@
* @return invokeHandle
*/
public final MethodHandle getInvokeHandle() {
- return invokeHandle;
+ return data.getInvoker();
}
/**
@@ -718,19 +513,9 @@
*/
public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
final MethodHandle bound = MH.bindTo(getInvokeHandle(), self);
- return hasCalleeParameter() ? MH.bindTo(bound, this) : bound;
+ return data.needsCallee() ? MH.bindTo(bound, this) : bound;
}
- /**
- * Check whether the ScriptFunction has callee parameter
- * @return true if callee parameter
- */
- protected abstract boolean hasCalleeParameter();
-
- /**
- * Flag ScriptFunction as needing a callee parameter
- */
- protected abstract void setHasCalleeParameter();
/**
* Get the construct handle - the most generic (and if no specializations are in place, only) constructor
@@ -740,7 +525,7 @@
* @return construct handle
*/
public final MethodHandle getConstructHandle(final MethodType type) {
- return candidateWithLowestWeight(type, getConstructHandle(), constructSpecializations);
+ return candidateWithLowestWeight(type, getConstructHandle(), data.getConstructSpecializations());
}
/**
@@ -748,7 +533,7 @@
* @return constructor handle
*/
public final MethodHandle getConstructHandle() {
- return constructHandle;
+ return data.getConstructor();
}
/**
@@ -756,8 +541,7 @@
* @param constructHandle constructor handle
*/
public final void setConstructHandle(final MethodHandle constructHandle) {
- this.constructHandle = constructHandle;
- this.constructSpecializations = null;
+ data.setConstructor(constructHandle);
}
/**
@@ -765,7 +549,7 @@
* @return the name
*/
public final String getName() {
- return name;
+ return data.getName();
}
/**
@@ -775,7 +559,7 @@
* @return true if this needs compilation
*/
public final boolean needsCompilation() {
- return invokeHandle == null;
+ return data.getInvoker() == null;
}
/**
@@ -783,7 +567,7 @@
* @return token
*/
public final long getToken() {
- return token;
+ return data.getToken();
}
/**
@@ -907,7 +691,7 @@
final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self
MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor);
- if (hasCalleeParameter()) {
+ if (data.needsCallee()) {
handle = MH.foldArguments(handle, ALLOCATE);
} else {
handle = MH.filterArguments(handle, 0, ALLOCATE);
@@ -958,12 +742,12 @@
MethodHandle boundHandle;
MethodHandle guard = null;
- if (hasCalleeParameter()) {
+ if (data.needsCallee()) {
final MethodHandle callHandle = getBestSpecializedInvokeHandle(type);
if(NashornCallSiteDescriptor.isScope(desc)) {
// (this, callee, args...) => (callee, args...) => (callee, [this], args...)
- boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
+ boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
boundHandle = MH.dropArguments(boundHandle, 1, Object.class);
} else {
// (this, callee, args...) permute => (callee, this, args...) which is what we get in
@@ -980,7 +764,7 @@
// For non-strict functions, check whether this-object is primitive type.
// If so add a to-object-wrapper argument filter.
// Else install a guard that will trigger a relink when the argument becomes primitive.
- if (isNonStrictFunction()) {
+ if (needsWrappedThis()) {
if (isPrimitiveThis(request.getArguments()[1])) {
boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
} else {
@@ -992,7 +776,7 @@
final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1));
if(NashornCallSiteDescriptor.isScope(desc)) {
- boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
+ boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class);
} else {
boundHandle = MH.dropArguments(callHandle, 0, Object.class);
@@ -1012,13 +796,13 @@
MethodHandle methodHandle = getBestSpecializedInvokeHandle(type);
if (bindName != null) {
- if (hasCalleeParameter()) {
+ if (data.needsCallee()) {
methodHandle = MH.insertArguments(methodHandle, 1, this, bindName);
} else {
methodHandle = MH.insertArguments(methodHandle, 1, bindName);
}
} else {
- if (hasCalleeParameter()) {
+ if (data.needsCallee()) {
methodHandle = MH.insertArguments(methodHandle, 1, this);
}
}
@@ -1034,7 +818,7 @@
* @return the converted this object
*/
protected Object convertThisObject(final Object thiz) {
- if (!(thiz instanceof ScriptObject) && isNonStrictFunction()) {
+ if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
if (JSType.nullOrUndefined(thiz)) {
return Context.getGlobalTrusted();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Feb 06 10:31:58 2013 +0100
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.parser.Token;
+import jdk.nashorn.internal.parser.TokenType;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
+
+/**
+ * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
+ * Instances of this class are created during codegen and stored in script classes'
+ * constants array to reduce function instantiation overhead during runtime.
+ */
+public class ScriptFunctionData {
+
+ // per-function object flags
+ private static final int IS_STRICT = 0b0000_0001;
+ private static final int IS_BUILTIN = 0b0000_0010;
+ private static final int HAS_CALLEE = 0b0000_0100;
+ private static final int IS_VARARGS = 0b0000_1000;
+
+ /** Name of the function or "" */
+ private final String name;
+ /** Source of this function, or null */
+ private final Source source;
+ /** Map for new instance constructor */
+ private PropertyMap allocatorMap;
+ /** Start position and length in source */
+ private final long token;
+ /** Number of expected arguments, either taken from FunctionNode or calculated from method handle signature*/
+ private int arity;
+ /** Does this function need a callee argument? */
+ private final int flags;
+
+ /** Reference to code for this method. */
+ private MethodHandle invoker;
+ /** Reference to code for this method when called to create "new" object */
+ private MethodHandle constructor;
+ /** Constructor to create a new instance. */
+ private MethodHandle allocator;
+ /** Generic invoker to used in {@link ScriptFunction#invoke(Object, Object...)}. */
+ private MethodHandle genericInvoker;
+ /** Generic constructor used in {@link ScriptFunction#construct(Object, Object...)}. */
+ private MethodHandle genericConstructor;
+ /** Specializations - see @SpecializedFunction */
+ private MethodHandle[] invokeSpecializations;
+ /** Specializations - see @SpecializedFunction */
+ private MethodHandle[] constructSpecializations;
+
+ /**
+ * Constructor
+ * @param fn the function node
+ * @param allocatorMap the allocator property map
+ */
+ public ScriptFunctionData(final FunctionNode fn, final PropertyMap allocatorMap) {
+
+ final long firstToken = fn.getFirstToken();
+ final long lastToken = fn.getLastToken();
+ final int position = Token.descPosition(firstToken);
+ final int length = Token.descPosition(lastToken) - position + Token.descLength(lastToken);
+
+ this.name = fn.isAnonymous() ? "" : fn.getIdent().getName();
+ this.source = fn.getSource();
+ this.allocatorMap = allocatorMap;
+ this.token = Token.toDesc(TokenType.FUNCTION, position, length);
+ this.arity = fn.getParameters().size();
+ this.flags = makeFlags(fn.needsCallee(), fn.isVarArg(), fn.isStrictMode(), false);
+ }
+
+ /**
+ * Constructor
+ * @param name the function name
+ * @param methodHandle the method handle
+ * @param specs array of specialized method handles
+ * @param strict strict flag
+ * @param builtin builtin flag
+ */
+ public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
+ this.name = name;
+ this.source = null;
+ this.token = 0;
+
+ final MethodType type = methodHandle.type();
+ final int paramCount = type.parameterCount();
+ final boolean isVarArg = type.parameterType(paramCount - 1).isArray();
+ final boolean needsCallee = needsCallee(methodHandle);
+
+ this.flags = makeFlags(needsCallee, isVarArg, strict, builtin);
+ this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
+
+ if (needsCallee && !isVarArg) {
+ this.arity--;
+ }
+
+ if (isConstructor(methodHandle)) {
+ if (!isVarArg) {
+ this.arity--; // drop the boolean flag for arity
+ }
+ /*
+ * We insert a boolean argument to tell if the method was invoked as
+ * constructor or not if the method handle's first argument is boolean.
+ */
+ this.invoker = MH.insertArguments(methodHandle, 0, false);
+ this.constructor = MH.insertArguments(methodHandle, 0, true);
+
+ if (specs != null) {
+ this.invokeSpecializations = new MethodHandle[specs.length];
+ this.constructSpecializations = new MethodHandle[specs.length];
+ for (int i = 0; i < specs.length; i++) {
+ this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false);
+ this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
+ }
+ }
+ } else {
+ this.invoker = methodHandle;
+ this.constructor = methodHandle;
+ this.invokeSpecializations = specs;
+ this.constructSpecializations = specs;
+ }
+ }
+
+ /**
+ * Get the arity of the function.
+ * @return the arity
+ */
+ public int getArity() {
+ return arity;
+ }
+
+ /**
+ * Set the arity of the function.
+ * @param arity the arity
+ */
+ public void setArity(int arity) {
+ this.arity = arity;
+ }
+
+ /**
+ * Get the function name.
+ * @return function name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the source of the function.
+ * @return the source
+ */
+ public Source getSource() {
+ return source;
+ }
+
+ /**
+ * Get this function as a String containing its source code. If no source code
+ * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
+ * @return string representation of this function's source
+ */
+ public String toSource() {
+ if (source != null && token != 0) {
+ return source.getString(Token.descPosition(token), Token.descLength(token));
+ }
+
+ return "function " + (name == null ? "" : name) + "() { [native code] }";
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(super.toString())
+ .append(" [ ")
+ .append(invoker)
+ .append(", ")
+ .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
+
+ if (source != null) {
+ sb.append(" @ ")
+ .append(source.getName())
+ .append(':')
+ .append(source.getLine(Token.descPosition(token)));
+ }
+ sb.append(" ]");
+
+ return sb.toString();
+ }
+
+ /**
+ * Get the allocator property map.
+ * @return the allocator map
+ */
+ public PropertyMap getAllocatorMap() {
+ return allocatorMap;
+ }
+
+ /**
+ * Get the function's parse token.
+ * @return the token
+ */
+ public long getToken() {
+ return token;
+ }
+
+ /**
+ * Returns true if the function needs a callee argument.
+ * @return the needsCallee flag
+ */
+ public boolean needsCallee() {
+ return (flags & HAS_CALLEE) != 0;
+ }
+
+ /**
+ * Returns true if this is a strict-mode function.
+ * @return the strict flag
+ */
+ public boolean isStrict() {
+ return (flags & IS_STRICT) != 0;
+ }
+
+ /**
+ * Returns true if this is a built-in function.
+ * @return the built-in flag
+ */
+ public boolean isBuiltin() {
+ return (flags & IS_BUILTIN) != 0;
+ }
+
+ /**
+ * Returns true if this is a var-arg function.
+ * @return the var-arg flag
+ */
+ public boolean isVarArg() {
+ return (flags & IS_VARARGS) != 0;
+ }
+
+ /**
+ * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
+ * according to ECMA 10.4.3.
+ * @return true if this argument must be an object
+ */
+ public boolean needsWrappedThis() {
+ return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
+ }
+
+ /**
+ * Get the method handle used to invoke this function.
+ * @return the invoke handle
+ */
+ public MethodHandle getInvoker() {
+ return invoker;
+ }
+
+ /**
+ * Get the method handle used to invoke this function as a constructor.
+ * @return the constructor handle
+ */
+ public MethodHandle getConstructor() {
+ return constructor;
+ }
+
+ /**
+ * Set the constructor method handle.
+ * @param constructor the constructor handle
+ */
+ public void setConstructor(MethodHandle constructor) {
+ this.constructor = constructor;
+ this.constructSpecializations = null;
+ }
+
+ /**
+ * Get the method handle used to allocate a new object for this constructor.
+ * @return the allocator handle
+ */
+ public MethodHandle getAllocator() {
+ return allocator;
+ }
+
+ /**
+ * Get an adapted version of the invoker handle that only uses {@code Object} as parameter and return types.
+ * @return the generic invoke handle
+ */
+ public MethodHandle getGenericInvoker() {
+ if (genericInvoker == null) {
+ assert invoker != null : "invoker is null";
+ genericInvoker = adaptMethodType(invoker);
+ }
+ return genericInvoker;
+ }
+
+ /**
+ * Get an adapted version of the constructor handle that only uses {@code Object} as parameter and return types.
+ * @return the generic constructor handle
+ */
+ public MethodHandle getGenericConstructor() {
+ if (genericConstructor == null) {
+ assert constructor != null : "constructor is null";
+ genericConstructor = adaptMethodType(constructor);
+ }
+ return genericConstructor;
+ }
+
+ /**
+ * Get the specialized invoke handles for this function.
+ * @return array of specialized invoke handles
+ */
+ public MethodHandle[] getInvokeSpecializations() {
+ return invokeSpecializations;
+ }
+
+ /**
+ * Get the specialized construct handles for this function.
+ * @return array of specialized construct handles
+ */
+ public MethodHandle[] getConstructSpecializations() {
+ return constructSpecializations;
+ }
+
+ /**
+ * Set the method handles for this function.
+ * @param invoker the invoker handle
+ * @param allocator the allocator handle
+ */
+ public void setMethodHandles(MethodHandle invoker, MethodHandle allocator) {
+ // We can't make method handle fields final because they're not available during codegen
+ // and they're set when first called, so we enforce set-once here.
+ if (this.invoker == null) {
+ this.invoker = invoker;
+ this.constructor = invoker;
+ this.allocator = allocator;
+ }
+ }
+
+ /**
+ * Convert boolean flags to int.
+ * @param needsCallee needs-callee flag
+ * @param isVarArg var-arg flag
+ * @param isStrict strict flag
+ * @param isBuiltin builtin flag
+ * @return int flags
+ */
+ private static int makeFlags(final boolean needsCallee, final boolean isVarArg, final boolean isStrict, final boolean isBuiltin) {
+ int flags = 0;
+ if (needsCallee) {
+ flags |= HAS_CALLEE;
+ }
+ if (isVarArg) {
+ flags |= IS_VARARGS;
+ }
+ if (isStrict) {
+ flags |= IS_STRICT;
+ }
+ if (isBuiltin) {
+ flags |= IS_BUILTIN;
+ }
+ return flags;
+ }
+
+ /**
+ * Test if a methodHandle refers to a constructor.
+ * @param methodHandle MethodHandle to test.
+ * @return True if method is a constructor.
+ */
+ private static boolean isConstructor(final MethodHandle methodHandle) {
+ return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
+ }
+
+ /**
+ * Heuristic to figure out if the method handle has a callee argument. If it's type is either
+ * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
+ * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
+ * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
+ * they also always receive a callee.
+ * @param methodHandle the examined method handle
+ * @return true if the method handle expects a callee, false otherwise
+ */
+ private static boolean needsCallee(MethodHandle methodHandle) {
+ final MethodType type = methodHandle.type();
+ final int len = type.parameterCount();
+ if(len == 0) {
+ return false;
+ }
+ if(type.parameterType(0) == boolean.class) {
+ return len > 2 && type.parameterType(2) == ScriptFunction.class;
+ }
+ return len > 1 && type.parameterType(1) == ScriptFunction.class;
+ }
+
+ /**
+ * Takes a method handle, and returns a potentially different method handle that can be used in
+ * {@link ScriptFunction#invoke(Object, Object...)} or {@link ScriptFunction#construct(Object, Object...)}.
+ * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
+ * {@code Object} as well, except for the following ones:
+ * <ul>
+ * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
+ * <li>the second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
+ * (callee) as an argument</li>
+ * </ul>
+ *
+ * @param handle the original method handle
+ * @return the new handle, conforming to the rules above.
+ */
+ private MethodHandle adaptMethodType(final MethodHandle handle) {
+ final MethodType type = handle.type();
+ MethodType newType = type.generic();
+ if (isVarArg()) {
+ newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
+ }
+ if (needsCallee()) {
+ newType = newType.changeParameterType(1, ScriptFunction.class);
+ }
+ return type.equals(newType) ? handle : handle.asType(newType);
+ }
+
+}