ScriptFunction.java revision 1000:0b7b3bd3cc04
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.runtime;
27
28import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
29import static jdk.nashorn.internal.lookup.Lookup.MH;
30import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
31import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
32import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
33
34import java.lang.invoke.MethodHandle;
35import java.lang.invoke.MethodHandles;
36import java.lang.invoke.MethodType;
37import java.lang.invoke.SwitchPoint;
38import java.util.Collections;
39
40import jdk.internal.dynalink.CallSiteDescriptor;
41import jdk.internal.dynalink.linker.GuardedInvocation;
42import jdk.internal.dynalink.linker.LinkRequest;
43import jdk.internal.dynalink.support.Guards;
44import jdk.nashorn.internal.codegen.ApplySpecialization;
45import jdk.nashorn.internal.codegen.CompilerConstants.Call;
46import jdk.nashorn.internal.objects.Global;
47import jdk.nashorn.internal.objects.NativeFunction;
48import jdk.nashorn.internal.runtime.ScriptFunctionData;
49import jdk.nashorn.internal.runtime.ScriptObject;
50import jdk.nashorn.internal.runtime.ScriptRuntime;
51import jdk.nashorn.internal.runtime.linker.Bootstrap;
52import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
53
54/**
55 * Runtime representation of a JavaScript function.
56 */
57public abstract class ScriptFunction extends ScriptObject {
58
59    /** Method handle for prototype getter for this ScriptFunction */
60    public static final MethodHandle G$PROTOTYPE = findOwnMH_S("G$prototype", Object.class, Object.class);
61
62    /** Method handle for prototype setter for this ScriptFunction */
63    public static final MethodHandle S$PROTOTYPE = findOwnMH_S("S$prototype", void.class, Object.class, Object.class);
64
65    /** Method handle for length getter for this ScriptFunction */
66    public static final MethodHandle G$LENGTH = findOwnMH_S("G$length", int.class, Object.class);
67
68    /** Method handle for name getter for this ScriptFunction */
69    public static final MethodHandle G$NAME = findOwnMH_S("G$name", Object.class, Object.class);
70
71    /** Method handle used for implementing sync() in mozilla_compat */
72    public static final MethodHandle INVOKE_SYNC = findOwnMH_S("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
73
74    /** Method handle for allocate function for this ScriptFunction */
75    static final MethodHandle ALLOCATE = findOwnMH_V("allocate", Object.class);
76
77    private static final MethodHandle WRAPFILTER = findOwnMH_S("wrapFilter", Object.class, Object.class);
78
79    private static final MethodHandle SCRIPTFUNCTION_GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class);
80
81    /** method handle to scope getter for this ScriptFunction */
82    public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
83
84    private static final MethodHandle IS_FUNCTION_MH  = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
85
86    private static final MethodHandle IS_APPLY_FUNCTION  = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class);
87
88    private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH_S("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
89
90    private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH_S("addZerothElement", Object[].class, Object[].class, Object.class);
91
92    private static final MethodHandle WRAP_THIS = MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, "wrapThis", MH.type(Object.class, Object.class));
93
94    /** The parent scope. */
95    private final ScriptObject scope;
96
97    private final ScriptFunctionData data;
98
99    /** The property map used for newly allocated object when function is used as constructor. */
100    protected PropertyMap allocatorMap;
101
102    /**
103     * Constructor
104     *
105     * @param name          function name
106     * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
107     * @param map           property map
108     * @param scope         scope
109     * @param specs         specialized version of this function - other method handles
110     * @param flags         {@link ScriptFunctionData} flags
111     */
112    protected ScriptFunction(
113            final String name,
114            final MethodHandle methodHandle,
115            final PropertyMap map,
116            final ScriptObject scope,
117            final MethodHandle[] specs,
118            final int flags) {
119
120        this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope);
121    }
122
123    /**
124     * Constructor
125     *
126     * @param data          static function data
127     * @param map           property map
128     * @param scope         scope
129     */
130    protected ScriptFunction(
131            final ScriptFunctionData data,
132            final PropertyMap map,
133            final ScriptObject scope) {
134
135        super(map);
136
137        if (Context.DEBUG) {
138            constructorCount++;
139        }
140
141        this.data  = data;
142        this.scope = scope;
143        this.allocatorMap = data.getAllocatorMap();
144    }
145
146    @Override
147    public String getClassName() {
148        return "Function";
149    }
150
151    /**
152     * ECMA 15.3.5.3 [[HasInstance]] (V)
153     * Step 3 if "prototype" value is not an Object, throw TypeError
154     */
155    @Override
156    public boolean isInstance(final ScriptObject instance) {
157        final Object basePrototype = getTargetFunction().getPrototype();
158        if (!(basePrototype instanceof ScriptObject)) {
159            throw typeError("prototype.not.an.object", ScriptRuntime.safeToString(getTargetFunction()), ScriptRuntime.safeToString(basePrototype));
160        }
161
162        for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
163            if (proto == basePrototype) {
164                return true;
165            }
166        }
167
168        return false;
169    }
170
171    /**
172     * Returns the target function for this function. If the function was not created using
173     * {@link #makeBoundFunction(Object, Object[])}, its target function is itself. If it is bound, its target function
174     * is the target function of the function it was made from (therefore, the target function is always the final,
175     * unbound recipient of the calls).
176     * @return the target function for this function.
177     */
178    protected ScriptFunction getTargetFunction() {
179        return this;
180    }
181
182    boolean isBoundFunction() {
183        return getTargetFunction() != this;
184    }
185
186    /**
187     * Set the arity of this ScriptFunction
188     * @param arity arity
189     */
190    public final void setArity(final int arity) {
191        data.setArity(arity);
192    }
193
194    /**
195     * Is this a ECMAScript 'use strict' function?
196     * @return true if function is in strict mode
197     */
198    public boolean isStrict() {
199        return data.isStrict();
200    }
201
202    /**
203     * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
204     * according to ECMA 10.4.3.
205     * @return true if this argument must be an object
206     */
207    public boolean needsWrappedThis() {
208        return data.needsWrappedThis();
209    }
210
211    private static boolean needsWrappedThis(final Object fn) {
212        return fn instanceof ScriptFunction ? ((ScriptFunction)fn).needsWrappedThis() : false;
213    }
214
215    /**
216     * Execute this script function.
217     * @param self  Target object.
218     * @param arguments  Call arguments.
219     * @return ScriptFunction result.
220     * @throws Throwable if there is an exception/error with the invocation or thrown from it
221     */
222    Object invoke(final Object self, final Object... arguments) throws Throwable {
223        if (Context.DEBUG) {
224            invokes++;
225        }
226        return data.invoke(this, self, arguments);
227    }
228
229    /**
230     * Execute this script function as a constructor.
231     * @param arguments  Call arguments.
232     * @return Newly constructed result.
233     * @throws Throwable if there is an exception/error with the invocation or thrown from it
234     */
235    Object construct(final Object... arguments) throws Throwable {
236        return data.construct(this, arguments);
237    }
238
239    /**
240     * Allocate function. Called from generated {@link ScriptObject} code
241     * for allocation as a factory method
242     *
243     * @return a new instance of the {@link ScriptObject} whose allocator this is
244     */
245    @SuppressWarnings("unused")
246    private Object allocate() {
247        if (Context.DEBUG) {
248            allocations++;
249        }
250
251        assert !isBoundFunction(); // allocate never invoked on bound functions
252
253        final ScriptObject object = data.allocate(allocatorMap);
254
255        if (object != null) {
256            final Object prototype = getPrototype();
257            if (prototype instanceof ScriptObject) {
258                object.setInitialProto((ScriptObject)prototype);
259            }
260
261            if (object.getProto() == null) {
262                object.setInitialProto(getObjectPrototype());
263            }
264        }
265
266        return object;
267    }
268
269    /**
270     * Return Object.prototype - used by "allocate"
271     * @return Object.prototype
272     */
273    protected abstract ScriptObject getObjectPrototype();
274
275    /**
276     * Creates a version of this function bound to a specific "self" and other arguments, as per
277     * {@code Function.prototype.bind} functionality in ECMAScript 5.1 section 15.3.4.5.
278     * @param self the self to bind to this function. Can be null (in which case, null is bound as this).
279     * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
280     * @return a function with the specified self and parameters bound.
281     */
282    protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
283        return makeBoundFunction(data.makeBoundFunctionData(this, self, args));
284    }
285
286    /**
287     * Create a version of this function as in {@link ScriptFunction#makeBoundFunction(Object, Object[])},
288     * but using a {@link ScriptFunctionData} for the bound data.
289     *
290     * @param boundData ScriptFuntionData for the bound function
291     * @return a function with the bindings performed according to the given data
292     */
293    protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData);
294
295    @Override
296    public final String safeToString() {
297        return toSource();
298    }
299
300    @Override
301    public String toString() {
302        return data.toString();
303    }
304
305    /**
306     * Get this function as a String containing its source code. If no source code
307     * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
308     * @return string representation of this function's source
309     */
310    public final String toSource() {
311        return data.toSource();
312    }
313
314    /**
315     * Get the prototype object for this function
316     * @return prototype
317     */
318    public abstract Object getPrototype();
319
320    /**
321     * Set the prototype object for this function
322     * @param prototype new prototype object
323     */
324    public abstract void setPrototype(Object prototype);
325
326    /**
327     * Create a function that invokes this function synchronized on {@code sync} or the self object
328     * of the invocation.
329     * @param sync the Object to synchronize on, or undefined
330     * @return synchronized function
331     */
332   public abstract ScriptFunction makeSynchronizedFunction(Object sync);
333
334    /**
335     * Return the invoke handle bound to a given ScriptObject self reference.
336     * If callee parameter is required result is rebound to this.
337     *
338     * @param self self reference
339     * @return bound invoke handle
340     */
341    public final MethodHandle getBoundInvokeHandle(final Object self) {
342        return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), self);
343    }
344
345    /**
346     * Bind the method handle to this {@code ScriptFunction} instance if it needs a callee parameter. If this function's
347     * method handles don't have a callee parameter, the handle is returned unchanged.
348     * @param methodHandle the method handle to potentially bind to this function instance.
349     * @return the potentially bound method handle
350     */
351    private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) {
352        return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle;
353
354    }
355
356    /**
357     * Get the name for this function
358     * @return the name
359     */
360    public final String getName() {
361        return data.getName();
362    }
363
364
365    /**
366     * Get the scope for this function
367     * @return the scope
368     */
369    public final ScriptObject getScope() {
370        return scope;
371    }
372
373    /**
374     * Prototype getter for this ScriptFunction - follows the naming convention
375     * used by Nasgen and the code generator
376     *
377     * @param self  self reference
378     * @return self's prototype
379     */
380    public static Object G$prototype(final Object self) {
381        return self instanceof ScriptFunction ?
382            ((ScriptFunction)self).getPrototype() :
383            UNDEFINED;
384    }
385
386    /**
387     * Prototype setter for this ScriptFunction - follows the naming convention
388     * used by Nasgen and the code generator
389     *
390     * @param self  self reference
391     * @param prototype prototype to set
392     */
393    public static void S$prototype(final Object self, final Object prototype) {
394        if (self instanceof ScriptFunction) {
395            ((ScriptFunction)self).setPrototype(prototype);
396        }
397    }
398
399    /**
400     * Length getter - ECMA 15.3.3.2: Function.length
401     * @param self self reference
402     * @return length
403     */
404    public static int G$length(final Object self) {
405        if (self instanceof ScriptFunction) {
406            return ((ScriptFunction)self).data.getArity();
407        }
408
409        return 0;
410    }
411
412    /**
413     * Name getter - ECMA Function.name
414     * @param self self refence
415     * @return the name, or undefined if none
416     */
417    public static Object G$name(final Object self) {
418        if (self instanceof ScriptFunction) {
419            return ((ScriptFunction)self).getName();
420        }
421
422        return UNDEFINED;
423    }
424
425    /**
426     * Get the prototype for this ScriptFunction
427     * @param constructor constructor
428     * @return prototype, or null if given constructor is not a ScriptFunction
429     */
430    public static ScriptObject getPrototype(final ScriptFunction constructor) {
431        if (constructor != null) {
432            final Object proto = constructor.getPrototype();
433            if (proto instanceof ScriptObject) {
434                return (ScriptObject)proto;
435            }
436        }
437
438        return null;
439    }
440
441    // These counters are updated only in debug mode.
442    private static int constructorCount;
443    private static int invokes;
444    private static int allocations;
445
446    /**
447     * @return the constructorCount
448     */
449    public static int getConstructorCount() {
450        return constructorCount;
451    }
452
453    /**
454     * @return the invokes
455     */
456    public static int getInvokes() {
457        return invokes;
458    }
459
460    /**
461     * @return the allocations
462     */
463    public static int getAllocations() {
464        return allocations;
465    }
466
467    @Override
468    protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
469        final MethodType type = desc.getMethodType();
470        assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
471        final CompiledFunction cf = data.getBestConstructor(type, scope);
472        final GuardedInvocation bestCtorInv = cf.createConstructorInvocation();
473        //TODO - ClassCastException
474        return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null);
475    }
476
477    @SuppressWarnings("unused")
478    private static Object wrapFilter(final Object obj) {
479        if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
480            return obj;
481        }
482        return Context.getGlobal().wrapAsObject(obj);
483    }
484
485
486    @SuppressWarnings("unused")
487    private static Object globalFilter(final Object object) {
488        // replace whatever we get with the current global object
489        return Context.getGlobal();
490    }
491
492    /**
493     * dyn:call call site signature: (callee, thiz, [args...])
494     * generated method signature:   (callee, thiz, [args...])
495     *
496     * cases:
497     * (a) method has callee parameter
498     *   (1) for local/scope calls, we just bind thiz and drop the second argument.
499     *   (2) for normal this-calls, we have to swap thiz and callee to get matching signatures.
500     * (b) method doesn't have callee parameter (builtin functions)
501     *   (3) for local/scope calls, bind thiz and drop both callee and thiz.
502     *   (4) for normal this-calls, drop callee.
503     *
504     * @return guarded invocation for call
505     */
506    @Override
507    protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
508        final MethodType type = desc.getMethodType();
509
510        final String  name       = getName();
511        final boolean isUnstable = request.isCallSiteUnstable();
512        final boolean scopeCall  = NashornCallSiteDescriptor.isScope(desc);
513        final boolean isCall     = !scopeCall && data.isBuiltin() && "call".equals(name);
514        final boolean isApply    = !scopeCall && data.isBuiltin() && "apply".equals(name);
515
516        final boolean isApplyOrCall = isCall | isApply;
517
518        if (isUnstable && !isApplyOrCall) {
519            //megamorphic - replace call with apply
520            final MethodHandle handle;
521            //ensure that the callsite is vararg so apply can consume it
522            if (type.parameterCount() == 3 && type.parameterType(2) == Object[].class) {
523                // Vararg call site
524                handle = ScriptRuntime.APPLY.methodHandle();
525            } else {
526                // (callee, this, args...) => (callee, this, args[])
527                handle = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2);
528            }
529
530            // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a
531            // generic "is this a ScriptFunction?" guard.
532            return new GuardedInvocation(
533                    handle,
534                    null,
535                    (SwitchPoint)null,
536                    ClassCastException.class);
537        }
538
539        MethodHandle boundHandle;
540        MethodHandle guard = null;
541
542        // Special handling of Function.apply and Function.call. Note we must be invoking
543        if (isApplyOrCall && !isUnstable) {
544            final Object[] args = request.getArguments();
545            if (Bootstrap.isCallable(args[1])) {
546                return createApplyOrCallCall(isApply, desc, request, args);
547            }
548        } //else just fall through and link as ordinary function or unstable apply
549
550        final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
551        final CompiledFunction cf = data.getBestInvoker(type, scope);
552        final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint);
553        final MethodHandle callHandle = bestInvoker.getInvocation();
554
555        if (data.needsCallee()) {
556            if (scopeCall && needsWrappedThis()) {
557                // (callee, this, args...) => (callee, [this], args...)
558                boundHandle = MH.filterArguments(callHandle, 1, SCRIPTFUNCTION_GLOBALFILTER);
559            } else {
560                // It's already (callee, this, args...), just what we need
561                boundHandle = callHandle;
562            }
563        } else if (data.isBuiltin() && "extend".equals(data.getName())) {
564            // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
565            // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
566            boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, type.parameterType(0), type.parameterType(1));
567        } else if (scopeCall && needsWrappedThis()) {
568            // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
569            // (this, args...) => ([this], args...)
570            boundHandle = MH.filterArguments(callHandle, 0, SCRIPTFUNCTION_GLOBALFILTER);
571            // ([this], args...) => ([callee], [this], args...)
572            boundHandle = MH.dropArguments(boundHandle, 0, type.parameterType(0));
573        } else {
574            // (this, args...) => ([callee], this, args...)
575            boundHandle = MH.dropArguments(callHandle, 0, type.parameterType(0));
576        }
577
578        // For non-strict functions, check whether this-object is primitive type.
579        // If so add a to-object-wrapper argument filter.
580        // Else install a guard that will trigger a relink when the argument becomes primitive.
581        if (!scopeCall && needsWrappedThis()) {
582            if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) {
583                boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
584            } else {
585                guard = getNonStrictFunctionGuard(this);
586            }
587        }
588
589        boundHandle = pairArguments(boundHandle, type);
590
591        return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this, cf.getFlags()) : guard, bestInvoker.getSwitchPoints(), null);
592    }
593
594    private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
595        final MethodType descType = desc.getMethodType();
596        final int paramCount = descType.parameterCount();
597        if(descType.parameterType(paramCount - 1).isArray()) {
598            // This is vararg invocation of apply or call. This can normally only happen when we do a recursive
599            // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate
600            // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader.
601            return createVarArgApplyOrCallCall(isApply, desc, request, args);
602        }
603
604        final boolean passesThis = paramCount > 2;
605        final boolean passesArgs = paramCount > 3;
606        final int realArgCount = passesArgs ? paramCount - 3 : 0;
607
608        final Object appliedFn = args[1];
609        final boolean appliedFnNeedsWrappedThis = needsWrappedThis(appliedFn);
610
611        //box call back to apply
612        CallSiteDescriptor appliedDesc = desc;
613        final SwitchPoint applyToCallSwitchPoint = Global.instance().getChangeCallback("apply");
614        //enough to change the proto switchPoint here
615
616        final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
617        final boolean isFailedApplyToCall = isApplyToCall && applyToCallSwitchPoint.hasBeenInvalidated();
618
619        // R(apply|call, ...) => R(...)
620        MethodType appliedType = descType.dropParameterTypes(0, 1);
621        if (!passesThis) {
622            // R() => R(this)
623            appliedType = appliedType.insertParameterTypes(1, Object.class);
624        } else if (appliedFnNeedsWrappedThis) {
625            appliedType = appliedType.changeParameterType(1, Object.class);
626        }
627
628        /*
629         * dropArgs is a synthetic method handle that contains any args that we need to
630         * get rid of that come after the arguments array in the apply case. We adapt
631         * the callsite to ask for 3 args only and then dropArguments on the method handle
632         * to make it fit the extraneous args.
633         */
634        MethodType dropArgs = MH.type(void.class);
635        if (isApply && !isFailedApplyToCall) {
636            final int pc = appliedType.parameterCount();
637            for (int i = 3; i < pc; i++) {
638                dropArgs = dropArgs.appendParameterTypes(appliedType.parameterType(i));
639            }
640            if (pc > 3) {
641                appliedType = appliedType.dropParameterTypes(3, pc);
642            }
643        }
644
645        if (isApply || isFailedApplyToCall) {
646            if (passesArgs) {
647                // R(this, args) => R(this, Object[])
648                appliedType = appliedType.changeParameterType(2, Object[].class);
649                // drop any extraneous arguments for the apply fail case
650                if (isFailedApplyToCall) {
651                    appliedType = appliedType.dropParameterTypes(3, paramCount - 1);
652                }
653            } else {
654                // R(this) => R(this, Object[])
655                appliedType = appliedType.insertParameterTypes(2, Object[].class);
656            }
657        }
658
659        appliedDesc = appliedDesc.changeMethodType(appliedType);
660
661        // Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation
662        final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()];
663        appliedArgs[0] = appliedFn;
664        appliedArgs[1] = passesThis ? appliedFnNeedsWrappedThis ? ScriptFunctionData.wrapThis(args[2]) : args[2] : ScriptRuntime.UNDEFINED;
665        if (isApply && !isFailedApplyToCall) {
666            appliedArgs[2] = passesArgs ? NativeFunction.toApplyArgs(args[3]) : ScriptRuntime.EMPTY_ARRAY;
667        } else {
668            if (passesArgs) {
669                if (isFailedApplyToCall) {
670                    final Object[] tmp = new Object[args.length - 3];
671                    System.arraycopy(args, 3, tmp, 0, tmp.length);
672                    appliedArgs[2] = NativeFunction.toApplyArgs(tmp);
673                } else {
674                    assert !isApply;
675                    System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
676                }
677            } else if (isFailedApplyToCall) {
678                appliedArgs[2] = ScriptRuntime.EMPTY_ARRAY;
679            }
680        }
681
682        // Ask the linker machinery for an invocation of the target function
683        final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs);
684        GuardedInvocation appliedInvocation;
685        try {
686            appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest);
687        } catch (final RuntimeException | Error e) {
688            throw e;
689        } catch (final Exception e) {
690            throw new RuntimeException(e);
691        }
692        assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage.
693
694        final Class<?> applyFnType = descType.parameterType(0);
695        MethodHandle inv = appliedInvocation.getInvocation(); //method handle from apply invocation. the applied function invocation
696
697        if (isApply && !isFailedApplyToCall) {
698            if (passesArgs) {
699                // Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it.
700                inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS);
701            } else {
702                // If the original call site doesn't pass argArray, pass in an empty array
703                inv = MH.insertArguments(inv, 2, (Object)ScriptRuntime.EMPTY_ARRAY);
704            }
705        }
706
707        if (isApplyToCall) {
708            if (isFailedApplyToCall) {
709                //take the real arguments that were passed to a call and force them into the apply instead
710                Context.getContextTrusted().getLogger(ApplySpecialization.class).info("Collection arguments to revert call to apply in " + appliedFn);
711                inv = MH.asCollector(inv, Object[].class, realArgCount);
712            } else {
713                appliedInvocation = appliedInvocation.addSwitchPoint(applyToCallSwitchPoint);
714            }
715        }
716
717        if (!passesThis) {
718            // If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed
719            inv = bindImplicitThis(appliedFn, inv);
720        } else if (appliedFnNeedsWrappedThis) {
721            // target function needs a wrapped this, so make sure we filter for that
722            inv = MH.filterArguments(inv, 1, WRAP_THIS);
723        }
724        inv = MH.dropArguments(inv, 0, applyFnType);
725
726        /*
727         * Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which
728         * is when we need to add arguments to the callsite to catch and ignore the synthetic
729         * extra args that someone has added to the command line.
730         */
731        for (int i = 0; i < dropArgs.parameterCount(); i++) {
732            inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i));
733        }
734
735        MethodHandle guard = appliedInvocation.getGuard();
736        // If the guard checks the value of "this" but we aren't passing thisArg, insert the default one
737        if (!passesThis && guard.type().parameterCount() > 1) {
738            guard = bindImplicitThis(appliedFn, guard);
739        }
740        final MethodType guardType = guard.type();
741
742        // We need to account for the dropped (apply|call) function argument.
743        guard = MH.dropArguments(guard, 0, descType.parameterType(0));
744        // Take the "isApplyFunction" guard, and bind it to this function.
745        MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this);
746        // Adapt the guard to receive all the arguments that the original guard does.
747        applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray());
748        // Fold the original function guard into our apply guard.
749        guard = MH.foldArguments(applyFnGuard, guard);
750
751        return appliedInvocation.replaceMethods(inv, guard);
752    }
753
754    /*
755     * This method is used for linking nested apply. Specialized apply and call linking will create a variable arity
756     * call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with
757     * Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method.
758     * This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back
759     * to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to
760     * invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity
761     * invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already
762     * solved by createApplyOrCallCall) non-vararg call site linking.
763     */
764    private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc,
765            final LinkRequest request, final Object[] args) {
766        final MethodType descType = desc.getMethodType();
767        final int paramCount = descType.parameterCount();
768        final Object[] varArgs = (Object[])args[paramCount - 1];
769        // -1 'cause we're not passing the vararg array itself
770        final int copiedArgCount = args.length - 1;
771        final int varArgCount = varArgs.length;
772
773        // Spread arguments for the delegate createApplyOrCallCall invocation.
774        final Object[] spreadArgs = new Object[copiedArgCount + varArgCount];
775        System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount);
776        System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount);
777
778        // Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and
779        // replace it with a list of Object.class.
780        final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes(
781                Collections.<Class<?>>nCopies(varArgCount, Object.class));
782        final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType);
783
784        // Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/
785        final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs);
786        final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs);
787
788        // Add spreader combinators to returned invocation and guard.
789        return spreadInvocation.replaceMethods(
790                // Use standard ScriptObject.pairArguments on the invocation
791                pairArguments(spreadInvocation.getInvocation(), descType),
792                // Use our specialized spreadGuardArguments on the guard (see below).
793                spreadGuardArguments(spreadInvocation.getGuard(), descType));
794    }
795
796    private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) {
797        final MethodType guardType = guard.type();
798        final int guardParamCount = guardType.parameterCount();
799        final int descParamCount = descType.parameterCount();
800        final int spreadCount = guardParamCount - descParamCount + 1;
801        if (spreadCount <= 0) {
802            // Guard doesn't dip into the varargs
803            return guard;
804        }
805
806        final MethodHandle arrayConvertingGuard;
807        // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply
808        // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail
809        // with ClassCastException of NativeArray to Object[].
810        if(guardType.parameterType(guardParamCount - 1).isArray()) {
811            arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS);
812        } else {
813            arrayConvertingGuard = guard;
814        }
815
816        return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount);
817    }
818
819    private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
820         final MethodHandle bound;
821         if(fn instanceof ScriptFunction && ((ScriptFunction)fn).needsWrappedThis()) {
822             bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER);
823         } else {
824             bound = mh;
825         }
826         return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED);
827     }
828
829    /**
830     * Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
831     *
832     * These don't want a callee parameter, so bind that. Name binding is optional.
833     */
834    MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
835        return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type);
836    }
837
838    private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
839        if (bindName == null) {
840            return methodHandle;
841        }
842
843        // if it is vararg method, we need to extend argument array with
844        // a new zeroth element that is set to bindName value.
845        final MethodType methodType = methodHandle.type();
846        final int parameterCount = methodType.parameterCount();
847        final boolean isVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
848
849        if (isVarArg) {
850            return MH.filterArguments(methodHandle, 1, MH.insertArguments(ADD_ZEROTH_ELEMENT, 1, bindName));
851        }
852        return MH.insertArguments(methodHandle, 1, bindName);
853    }
854
855    /**
856     * Get the guard that checks if a {@link ScriptFunction} is equal to
857     * a known ScriptFunction, using reference comparison
858     *
859     * @param function The ScriptFunction to check against. This will be bound to the guard method handle
860     *
861     * @return method handle for guard
862     */
863    private static MethodHandle getFunctionGuard(final ScriptFunction function, final int flags) {
864        assert function.data != null;
865        // Built-in functions have a 1-1 correspondence to their ScriptFunctionData, so we can use a cheaper identity
866        // comparison for them.
867        if (function.data.isBuiltin()) {
868            return Guards.getIdentityGuard(function);
869        }
870        return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
871    }
872
873    /**
874     * Get a guard that checks if a {@link ScriptFunction} is equal to
875     * a known ScriptFunction using reference comparison, and whether the type of
876     * the second argument (this-object) is not a JavaScript primitive type.
877     *
878     * @param function The ScriptFunction to check against. This will be bound to the guard method handle
879     *
880     * @return method handle for guard
881     */
882    private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) {
883        assert function.data != null;
884        return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data);
885    }
886
887    @SuppressWarnings("unused")
888    private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) {
889        return self instanceof ScriptFunction && ((ScriptFunction)self).data == data;
890    }
891
892    @SuppressWarnings("unused")
893    private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) {
894        return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject;
895    }
896
897    @SuppressWarnings("unused")
898    private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) {
899        // NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call()
900        return appliedFnCondition && self == expectedSelf;
901    }
902
903    @SuppressWarnings("unused")
904    private static Object[] addZerothElement(final Object[] args, final Object value) {
905        // extends input array with by adding new zeroth element
906        final Object[] src = args == null? ScriptRuntime.EMPTY_ARRAY : args;
907        final Object[] result = new Object[src.length + 1];
908        System.arraycopy(src, 0, result, 1, src.length);
909        result[0] = value;
910        return result;
911    }
912
913    @SuppressWarnings("unused")
914    private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args)
915            throws Throwable {
916        final Object syncObj = sync == UNDEFINED ? self : sync;
917        synchronized (syncObj) {
918            return func.invoke(self, args);
919        }
920    }
921
922    private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
923        return MH.findStatic(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
924    }
925
926    private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
927        return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
928    }
929}
930
931