aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/runtime/ScriptFunction.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/ScriptFunction.java')
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunction.java518
1 files changed, 437 insertions, 81 deletions
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
index 3d8b4ff2..d999c118 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
@@ -29,18 +29,30 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.support.Guards;
+import jdk.nashorn.internal.codegen.ApplySpecialization;
+import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.objects.NativeFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
-import jdk.nashorn.internal.runtime.linker.NashornGuards;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
/**
* Runtime representation of a JavaScript function.
@@ -48,35 +60,39 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards;
public abstract class ScriptFunction extends ScriptObject {
/** Method handle for prototype getter for this ScriptFunction */
- public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class);
+ public static final MethodHandle G$PROTOTYPE = findOwnMH_S("G$prototype", Object.class, Object.class);
/** Method handle for prototype setter for this ScriptFunction */
- public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class);
+ public static final MethodHandle S$PROTOTYPE = findOwnMH_S("S$prototype", void.class, Object.class, Object.class);
/** Method handle for length getter for this ScriptFunction */
- public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class);
+ public static final MethodHandle G$LENGTH = findOwnMH_S("G$length", int.class, Object.class);
/** Method handle for name getter for this ScriptFunction */
- public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
+ public static final MethodHandle G$NAME = findOwnMH_S("G$name", Object.class, Object.class);
/** Method handle used for implementing sync() in mozilla_compat */
- public static final MethodHandle INVOKE_SYNC = findOwnMH("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
+ public static final MethodHandle INVOKE_SYNC = findOwnMH_S("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
/** Method handle for allocate function for this ScriptFunction */
- static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
+ static final MethodHandle ALLOCATE = findOwnMH_V("allocate", Object.class);
- private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
+ private static final MethodHandle WRAPFILTER = findOwnMH_S("wrapFilter", Object.class, Object.class);
- private static final MethodHandle GLOBALFILTER = findOwnMH("globalFilter", Object.class, Object.class);
+ private static final MethodHandle SCRIPTFUNCTION_GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class);
/** method handle to scope getter for this ScriptFunction */
public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
- private static final MethodHandle IS_FUNCTION_MH = findOwnMH("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
+ private static final MethodHandle IS_FUNCTION_MH = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
+
+ private static final MethodHandle IS_APPLY_FUNCTION = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class);
- private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
+ private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH_S("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
- private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH("addZerothElement", Object[].class, Object[].class, Object.class);
+ private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH_S("addZerothElement", Object[].class, Object[].class, Object.class);
+
+ private static final MethodHandle WRAP_THIS = MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, "wrapThis", MH.type(Object.class, Object.class));
/** The parent scope. */
private final ScriptObject scope;
@@ -101,7 +117,7 @@ public abstract class ScriptFunction extends ScriptObject {
final MethodHandle methodHandle,
final PropertyMap map,
final ScriptObject scope,
- final MethodHandle[] specs,
+ final Specialization[] specs,
final int flags) {
this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope);
@@ -195,6 +211,10 @@ public abstract class ScriptFunction extends ScriptObject {
return data.needsWrappedThis();
}
+ private static boolean needsWrappedThis(final Object fn) {
+ return fn instanceof ScriptFunction ? ((ScriptFunction)fn).needsWrappedThis() : false;
+ }
+
/**
* Execute this script function.
* @param self Target object.
@@ -230,12 +250,13 @@ public abstract class ScriptFunction extends ScriptObject {
if (Context.DEBUG) {
allocations++;
}
+
assert !isBoundFunction(); // allocate never invoked on bound functions
final ScriptObject object = data.allocate(allocatorMap);
if (object != null) {
- Object prototype = getPrototype();
+ final Object prototype = getPrototype();
if (prototype instanceof ScriptObject) {
object.setInitialProto((ScriptObject)prototype);
}
@@ -311,26 +332,7 @@ public abstract class ScriptFunction extends ScriptObject {
* @param sync the Object to synchronize on, or undefined
* @return synchronized function
*/
- public abstract ScriptFunction makeSynchronizedFunction(Object sync);
-
- /**
- * Return the most appropriate invoke handle if there are specializations
- * @param type most specific method type to look for invocation with
- * @param args args for trampoline invocation
- * @return invoke method handle
- */
- private MethodHandle getBestInvoker(final MethodType type, final Object[] args) {
- return data.getBestInvoker(type, args);
- }
-
- /**
- * Return the most appropriate invoke handle if there are specializations
- * @param type most specific method type to look for invocation with
- * @return invoke method handle
- */
- public MethodHandle getBestInvoker(final MethodType type) {
- return getBestInvoker(type, null);
- }
+ public abstract ScriptFunction makeSynchronizedFunction(Object sync);
/**
* Return the invoke handle bound to a given ScriptObject self reference.
@@ -340,7 +342,7 @@ public abstract class ScriptFunction extends ScriptObject {
* @return bound invoke handle
*/
public final MethodHandle getBoundInvokeHandle(final Object self) {
- return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self);
+ return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), self);
}
/**
@@ -379,7 +381,7 @@ public abstract class ScriptFunction extends ScriptObject {
* @return self's prototype
*/
public static Object G$prototype(final Object self) {
- return (self instanceof ScriptFunction) ?
+ return self instanceof ScriptFunction ?
((ScriptFunction)self).getPrototype() :
UNDEFINED;
}
@@ -428,9 +430,9 @@ public abstract class ScriptFunction extends ScriptObject {
* @param constructor constructor
* @return prototype, or null if given constructor is not a ScriptFunction
*/
- public static ScriptObject getPrototype(final Object constructor) {
- if (constructor instanceof ScriptFunction) {
- final Object proto = ((ScriptFunction)constructor).getPrototype();
+ public static ScriptObject getPrototype(final ScriptFunction constructor) {
+ if (constructor != null) {
+ final Object proto = constructor.getPrototype();
if (proto instanceof ScriptObject) {
return (ScriptObject)proto;
}
@@ -466,12 +468,15 @@ public abstract class ScriptFunction extends ScriptObject {
}
@Override
- protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
+ protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final MethodType type = desc.getMethodType();
- return new GuardedInvocation(pairArguments(data.getBestConstructor(type.changeParameterType(0, ScriptFunction.class), null), type), null, getFunctionGuard(this));
+ assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
+ final CompiledFunction cf = data.getBestConstructor(type, scope, CompiledFunction.NO_FUNCTIONS);
+ final GuardedInvocation bestCtorInv = cf.createConstructorInvocation();
+ //TODO - ClassCastException
+ return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null);
}
- @SuppressWarnings("unused")
private static Object wrapFilter(final Object obj) {
if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
return obj;
@@ -487,6 +492,35 @@ public abstract class ScriptFunction extends ScriptObject {
}
/**
+ * Some receivers are primitive, in that case, according to the Spec we create a new
+ * native object per callsite with the wrap filter. We can only apply optimistic builtins
+ * if there is no per instance state saved for these wrapped objects (e.g. currently NativeStrings),
+ * otherwise we can't create optimistic versions
+ *
+ * @param self receiver
+ * @param linkLogicClass linkLogicClass, or null if no link logic exists
+ * @return link logic instance, or null if one could not be constructed for this receiver
+ */
+ private static LinkLogic getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass) {
+ if (linkLogicClass == null) {
+ return LinkLogic.EMPTY_INSTANCE; //always OK to link this, specialization but without special linking logic
+ }
+
+ if (!Context.getContextTrusted().getEnv()._optimistic_types) {
+ return null; //if optimistic types are off, optimistic builtins are too
+ }
+
+ final Object wrappedSelf = wrapFilter(self);
+ if (wrappedSelf instanceof OptimisticBuiltins) {
+ if (wrappedSelf != self && ((OptimisticBuiltins)wrappedSelf).hasPerInstanceAssumptions()) {
+ return null; //pessimistic - we created a wrapped object different from the primitive, but the assumptions have instance state
+ }
+ return ((OptimisticBuiltins)wrappedSelf).getLinkLogic(linkLogicClass);
+ }
+ return null;
+ }
+
+ /**
* dyn:call call site signature: (callee, thiz, [args...])
* generated method signature: (callee, thiz, [args...])
*
@@ -497,51 +531,114 @@ public abstract class ScriptFunction extends ScriptObject {
* (b) method doesn't have callee parameter (builtin functions)
* (3) for local/scope calls, bind thiz and drop both callee and thiz.
* (4) for normal this-calls, drop callee.
+ *
+ * @return guarded invocation for call
*/
@Override
protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final MethodType type = desc.getMethodType();
- final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
- if (request.isCallSiteUnstable()) {
- // (callee, this, args...) => (callee, this, args[])
- final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2);
+ final String name = getName();
+ final boolean isUnstable = request.isCallSiteUnstable();
+ final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
+ final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name);
+ final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name);
+
+ final boolean isApplyOrCall = isCall | isApply;
+
+ if (isUnstable && !isApplyOrCall) {
+ //megamorphic - replace call with apply
+ final MethodHandle handle;
+ //ensure that the callsite is vararg so apply can consume it
+ if (type.parameterCount() == 3 && type.parameterType(2) == Object[].class) {
+ // Vararg call site
+ handle = ScriptRuntime.APPLY.methodHandle();
+ } else {
+ // (callee, this, args...) => (callee, this, args[])
+ handle = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2);
+ }
// If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a
// generic "is this a ScriptFunction?" guard.
- return new GuardedInvocation(collector, ScriptFunction.class.isAssignableFrom(desc.getMethodType().parameterType(0))
- ? null : NashornGuards.getScriptFunctionGuard());
+ return new GuardedInvocation(
+ handle,
+ null,
+ (SwitchPoint)null,
+ ClassCastException.class);
}
MethodHandle boundHandle;
MethodHandle guard = null;
+ // Special handling of Function.apply and Function.call. Note we must be invoking
+ if (isApplyOrCall && !isUnstable) {
+ final Object[] args = request.getArguments();
+ if (Bootstrap.isCallable(args[1])) {
+ return createApplyOrCallCall(isApply, desc, request, args);
+ }
+ } //else just fall through and link as ordinary function or unstable apply
+
+ int programPoint = INVALID_PROGRAM_POINT;
+ if (NashornCallSiteDescriptor.isOptimistic(desc)) {
+ programPoint = NashornCallSiteDescriptor.getProgramPoint(desc);
+ }
+
+ CompiledFunction cf = data.getBestInvoker(type, scope, CompiledFunction.NO_FUNCTIONS);
+ final Object self = request.getArguments()[1];
+ final Collection<CompiledFunction> forbidden = new HashSet<>();
+
+ //check for special fast versions of the compiled function
+ final List<SwitchPoint> sps = new ArrayList<>();
+ Class<? extends Throwable> exceptionGuard = null;
+
+ while (cf.isSpecialization()) {
+ final Class<? extends LinkLogic> linkLogicClass = cf.getLinkLogicClass();
+ //if linklogic is null, we can always link with the standard mechanism, it's still a specialization
+ final LinkLogic linkLogic = getLinkLogic(self, linkLogicClass);
+
+ if (linkLogic != null && linkLogic.checkLinkable(self, desc, request)) {
+ final DebugLogger log = Context.getContextTrusted().getLogger(Compiler.class);
+
+ if (log.isEnabled()) {
+ log.info("Linking optimistic builtin function: '", name, "' args=", Arrays.toString(request.getArguments()), " desc=", desc);
+ }
+
+ exceptionGuard = linkLogic.getRelinkException();
+
+ break;
+ }
+
+ //could not link this specialization because link check failed
+ forbidden.add(cf);
+ final CompiledFunction oldCf = cf;
+ cf = data.getBestInvoker(type, scope, forbidden);
+ assert oldCf != cf;
+ }
+
+ final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint);
+ final MethodHandle callHandle = bestInvoker.getInvocation();
+
if (data.needsCallee()) {
- final MethodHandle callHandle = getBestInvoker(type, request.getArguments());
if (scopeCall && needsWrappedThis()) {
- // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
// (callee, this, args...) => (callee, [this], args...)
- boundHandle = MH.filterArguments(callHandle, 1, GLOBALFILTER);
+ boundHandle = MH.filterArguments(callHandle, 1, SCRIPTFUNCTION_GLOBALFILTER);
} else {
// It's already (callee, this, args...), just what we need
boundHandle = callHandle;
}
+ } else if (data.isBuiltin() && "extend".equals(data.getName())) {
+ // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
+ // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
+ boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, type.parameterType(0), type.parameterType(1));
+ } else if (scopeCall && needsWrappedThis()) {
+ // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
+ // (this, args...) => ([this], args...)
+ boundHandle = MH.filterArguments(callHandle, 0, SCRIPTFUNCTION_GLOBALFILTER);
+ // ([this], args...) => ([callee], [this], args...)
+ boundHandle = MH.dropArguments(boundHandle, 0, type.parameterType(0));
} else {
- final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1), request.getArguments());
- if (data.isBuiltin() && "extend".equals(data.getName())) {
- // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
- // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
- boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, Object.class, Object.class);
- } else if (scopeCall && needsWrappedThis()) {
- // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
- // (this, args...) => ([this], args...)
- boundHandle = MH.filterArguments(callHandle, 0, GLOBALFILTER);
- // ([this], args...) => ([callee], [this], args...)
- boundHandle = MH.dropArguments(boundHandle, 0, Object.class);
- } else {
- // (this, args...) => ([callee], this, args...)
- boundHandle = MH.dropArguments(callHandle, 0, Object.class);
- }
+ // (this, args...) => ([callee], this, args...)
+ boundHandle = MH.dropArguments(callHandle, 0, type.parameterType(0));
}
// For non-strict functions, check whether this-object is primitive type.
@@ -557,8 +654,257 @@ public abstract class ScriptFunction extends ScriptObject {
boundHandle = pairArguments(boundHandle, type);
- return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this) : guard);
- }
+ if (bestInvoker.getSwitchPoints() != null) {
+ sps.addAll(Arrays.asList(bestInvoker.getSwitchPoints()));
+ }
+ final SwitchPoint[] spsArray = sps.isEmpty() ? null : sps.toArray(new SwitchPoint[sps.size()]);
+
+ return new GuardedInvocation(
+ boundHandle,
+ guard == null ?
+ getFunctionGuard(
+ this,
+ cf.getFlags()) :
+ guard,
+ spsArray,
+ exceptionGuard);
+ }
+
+ private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
+ final MethodType descType = desc.getMethodType();
+ final int paramCount = descType.parameterCount();
+ if(descType.parameterType(paramCount - 1).isArray()) {
+ // This is vararg invocation of apply or call. This can normally only happen when we do a recursive
+ // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate
+ // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader.
+ return createVarArgApplyOrCallCall(isApply, desc, request, args);
+ }
+
+ final boolean passesThis = paramCount > 2;
+ final boolean passesArgs = paramCount > 3;
+ final int realArgCount = passesArgs ? paramCount - 3 : 0;
+
+ final Object appliedFn = args[1];
+ final boolean appliedFnNeedsWrappedThis = needsWrappedThis(appliedFn);
+
+ //box call back to apply
+ CallSiteDescriptor appliedDesc = desc;
+ final SwitchPoint applyToCallSwitchPoint = Global.getBuiltinFunctionApplySwitchPoint();
+ //enough to change the proto switchPoint here
+
+ final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
+ final boolean isFailedApplyToCall = isApplyToCall && applyToCallSwitchPoint.hasBeenInvalidated();
+
+ // R(apply|call, ...) => R(...)
+ MethodType appliedType = descType.dropParameterTypes(0, 1);
+ if (!passesThis) {
+ // R() => R(this)
+ appliedType = appliedType.insertParameterTypes(1, Object.class);
+ } else if (appliedFnNeedsWrappedThis) {
+ appliedType = appliedType.changeParameterType(1, Object.class);
+ }
+
+ /*
+ * dropArgs is a synthetic method handle that contains any args that we need to
+ * get rid of that come after the arguments array in the apply case. We adapt
+ * the callsite to ask for 3 args only and then dropArguments on the method handle
+ * to make it fit the extraneous args.
+ */
+ MethodType dropArgs = MH.type(void.class);
+ if (isApply && !isFailedApplyToCall) {
+ final int pc = appliedType.parameterCount();
+ for (int i = 3; i < pc; i++) {
+ dropArgs = dropArgs.appendParameterTypes(appliedType.parameterType(i));
+ }
+ if (pc > 3) {
+ appliedType = appliedType.dropParameterTypes(3, pc);
+ }
+ }
+
+ if (isApply || isFailedApplyToCall) {
+ if (passesArgs) {
+ // R(this, args) => R(this, Object[])
+ appliedType = appliedType.changeParameterType(2, Object[].class);
+ // drop any extraneous arguments for the apply fail case
+ if (isFailedApplyToCall) {
+ appliedType = appliedType.dropParameterTypes(3, paramCount - 1);
+ }
+ } else {
+ // R(this) => R(this, Object[])
+ appliedType = appliedType.insertParameterTypes(2, Object[].class);
+ }
+ }
+
+ appliedDesc = appliedDesc.changeMethodType(appliedType); //no extra args
+
+ // Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation
+ final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()];
+ appliedArgs[0] = appliedFn;
+ appliedArgs[1] = passesThis ? appliedFnNeedsWrappedThis ? ScriptFunctionData.wrapThis(args[2]) : args[2] : ScriptRuntime.UNDEFINED;
+ if (isApply && !isFailedApplyToCall) {
+ appliedArgs[2] = passesArgs ? NativeFunction.toApplyArgs(args[3]) : ScriptRuntime.EMPTY_ARRAY;
+ } else {
+ if (passesArgs) {
+ if (isFailedApplyToCall) {
+ final Object[] tmp = new Object[args.length - 3];
+ System.arraycopy(args, 3, tmp, 0, tmp.length);
+ appliedArgs[2] = NativeFunction.toApplyArgs(tmp);
+ } else {
+ assert !isApply;
+ System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
+ }
+ } else if (isFailedApplyToCall) {
+ appliedArgs[2] = ScriptRuntime.EMPTY_ARRAY;
+ }
+ }
+
+ // Ask the linker machinery for an invocation of the target function
+ final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs);
+
+ GuardedInvocation appliedInvocation;
+ try {
+ appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest);
+ } catch (final RuntimeException | Error e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new RuntimeException(e);
+ }
+ assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage.
+
+ final Class<?> applyFnType = descType.parameterType(0);
+ MethodHandle inv = appliedInvocation.getInvocation(); //method handle from apply invocation. the applied function invocation
+
+ if (isApply && !isFailedApplyToCall) {
+ if (passesArgs) {
+ // Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it.
+ inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS);
+ } else {
+ // If the original call site doesn't pass argArray, pass in an empty array
+ inv = MH.insertArguments(inv, 2, (Object)ScriptRuntime.EMPTY_ARRAY);
+ }
+ }
+
+ if (isApplyToCall) {
+ if (isFailedApplyToCall) {
+ //take the real arguments that were passed to a call and force them into the apply instead
+ Context.getContextTrusted().getLogger(ApplySpecialization.class).info("Collection arguments to revert call to apply in " + appliedFn);
+ inv = MH.asCollector(inv, Object[].class, realArgCount);
+ } else {
+ appliedInvocation = appliedInvocation.addSwitchPoint(applyToCallSwitchPoint);
+ }
+ }
+
+ if (!passesThis) {
+ // If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed
+ inv = bindImplicitThis(appliedFn, inv);
+ } else if (appliedFnNeedsWrappedThis) {
+ // target function needs a wrapped this, so make sure we filter for that
+ inv = MH.filterArguments(inv, 1, WRAP_THIS);
+ }
+ inv = MH.dropArguments(inv, 0, applyFnType);
+
+ /*
+ * Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which
+ * is when we need to add arguments to the callsite to catch and ignore the synthetic
+ * extra args that someone has added to the command line.
+ */
+ for (int i = 0; i < dropArgs.parameterCount(); i++) {
+ inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i));
+ }
+
+ MethodHandle guard = appliedInvocation.getGuard();
+ // If the guard checks the value of "this" but we aren't passing thisArg, insert the default one
+ if (!passesThis && guard.type().parameterCount() > 1) {
+ guard = bindImplicitThis(appliedFn, guard);
+ }
+ final MethodType guardType = guard.type();
+
+ // We need to account for the dropped (apply|call) function argument.
+ guard = MH.dropArguments(guard, 0, descType.parameterType(0));
+ // Take the "isApplyFunction" guard, and bind it to this function.
+ MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); //TODO replace this with switchpoint
+ // Adapt the guard to receive all the arguments that the original guard does.
+ applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray());
+ // Fold the original function guard into our apply guard.
+ guard = MH.foldArguments(applyFnGuard, guard);
+
+ return appliedInvocation.replaceMethods(inv, guard);
+ }
+
+ /*
+ * This method is used for linking nested apply. Specialized apply and call linking will create a variable arity
+ * call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with
+ * Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method.
+ * This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back
+ * to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to
+ * invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity
+ * invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already
+ * solved by createApplyOrCallCall) non-vararg call site linking.
+ */
+ private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc,
+ final LinkRequest request, final Object[] args) {
+ final MethodType descType = desc.getMethodType();
+ final int paramCount = descType.parameterCount();
+ final Object[] varArgs = (Object[])args[paramCount - 1];
+ // -1 'cause we're not passing the vararg array itself
+ final int copiedArgCount = args.length - 1;
+ final int varArgCount = varArgs.length;
+
+ // Spread arguments for the delegate createApplyOrCallCall invocation.
+ final Object[] spreadArgs = new Object[copiedArgCount + varArgCount];
+ System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount);
+ System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount);
+
+ // Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and
+ // replace it with a list of Object.class.
+ final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes(
+ Collections.<Class<?>>nCopies(varArgCount, Object.class));
+ final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType);
+
+ // Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/
+ final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs);
+ final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs);
+
+ // Add spreader combinators to returned invocation and guard.
+ return spreadInvocation.replaceMethods(
+ // Use standard ScriptObject.pairArguments on the invocation
+ pairArguments(spreadInvocation.getInvocation(), descType),
+ // Use our specialized spreadGuardArguments on the guard (see below).
+ spreadGuardArguments(spreadInvocation.getGuard(), descType));
+ }
+
+ private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) {
+ final MethodType guardType = guard.type();
+ final int guardParamCount = guardType.parameterCount();
+ final int descParamCount = descType.parameterCount();
+ final int spreadCount = guardParamCount - descParamCount + 1;
+ if (spreadCount <= 0) {
+ // Guard doesn't dip into the varargs
+ return guard;
+ }
+
+ final MethodHandle arrayConvertingGuard;
+ // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply
+ // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail
+ // with ClassCastException of NativeArray to Object[].
+ if(guardType.parameterType(guardParamCount - 1).isArray()) {
+ arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS);
+ } else {
+ arrayConvertingGuard = guard;
+ }
+
+ return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount);
+ }
+
+ private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
+ final MethodHandle bound;
+ if(fn instanceof ScriptFunction && ((ScriptFunction)fn).needsWrappedThis()) {
+ bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER);
+ } else {
+ bound = mh;
+ }
+ return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED);
+ }
/**
* Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
@@ -566,7 +912,7 @@ public abstract class ScriptFunction extends ScriptObject {
* These don't want a callee parameter, so bind that. Name binding is optional.
*/
MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
- return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type, null)), bindName), type);
+ return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type);
}
private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
@@ -594,8 +940,13 @@ public abstract class ScriptFunction extends ScriptObject {
*
* @return method handle for guard
*/
- private static MethodHandle getFunctionGuard(final ScriptFunction function) {
+ private static MethodHandle getFunctionGuard(final ScriptFunction function, final int flags) {
assert function.data != null;
+ // Built-in functions have a 1-1 correspondence to their ScriptFunctionData, so we can use a cheaper identity
+ // comparison for them.
+ if (function.data.isBuiltin()) {
+ return Guards.getIdentityGuard(function);
+ }
return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
}
@@ -623,10 +974,17 @@ public abstract class ScriptFunction extends ScriptObject {
return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject;
}
+ //TODO this can probably be removed given that we have builtin switchpoints in the context
+ @SuppressWarnings("unused")
+ private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) {
+ // NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call()
+ return appliedFnCondition && self == expectedSelf;
+ }
+
@SuppressWarnings("unused")
private static Object[] addZerothElement(final Object[] args, final Object value) {
// extends input array with by adding new zeroth element
- final Object[] src = (args == null)? ScriptRuntime.EMPTY_ARRAY : args;
+ final Object[] src = args == null? ScriptRuntime.EMPTY_ARRAY : args;
final Object[] result = new Object[src.length + 1];
System.arraycopy(src, 0, result, 1, src.length);
result[0] = value;
@@ -642,14 +1000,12 @@ public abstract class ScriptFunction extends ScriptObject {
}
}
- private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
- final Class<?> own = ScriptFunction.class;
- final MethodType mt = MH.type(rtype, types);
- try {
- return MH.findStatic(MethodHandles.lookup(), own, name, mt);
- } catch (final MethodHandleFactory.LookupException e) {
- return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
- }
+ private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
+ return MH.findStatic(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
+ }
+
+ private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
+ return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
}
}