ScriptFunction.java revision 1481:e6bb9489faac
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 */
25package jdk.nashorn.internal.runtime;
26
27import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
28import static jdk.nashorn.internal.lookup.Lookup.MH;
29import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
30import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
31import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
32import java.lang.invoke.MethodHandle;
33import java.lang.invoke.MethodHandles;
34import java.lang.invoke.MethodHandles.Lookup;
35import java.lang.invoke.MethodType;
36import java.lang.invoke.SwitchPoint;
37import java.security.AccessControlContext;
38import java.security.AccessController;
39import java.security.PrivilegedAction;
40import java.util.ArrayList;
41import java.util.Arrays;
42import java.util.Collection;
43import java.util.Collections;
44import java.util.HashSet;
45import java.util.List;
46import java.util.concurrent.atomic.LongAdder;
47import jdk.internal.dynalink.CallSiteDescriptor;
48import jdk.internal.dynalink.linker.GuardedInvocation;
49import jdk.internal.dynalink.linker.LinkRequest;
50import jdk.internal.dynalink.linker.support.Guards;
51import jdk.nashorn.internal.codegen.ApplySpecialization;
52import jdk.nashorn.internal.codegen.Compiler;
53import jdk.nashorn.internal.codegen.CompilerConstants.Call;
54import jdk.nashorn.internal.objects.Global;
55import jdk.nashorn.internal.objects.NativeFunction;
56import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
57import jdk.nashorn.internal.runtime.linker.Bootstrap;
58import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
59import jdk.nashorn.internal.runtime.logging.DebugLogger;
60
61/**
62 * Runtime representation of a JavaScript function. This class has only private
63 * and protected constructors. There are no *public* constructors - but only
64 * factory methods that follow the naming pattern "createXYZ".
65 */
66public class ScriptFunction extends ScriptObject {
67
68    /**
69     * Method handle for prototype getter for this ScriptFunction
70     */
71    public static final MethodHandle G$PROTOTYPE = findOwnMH_S("G$prototype", Object.class, Object.class);
72
73    /**
74     * Method handle for prototype setter for this ScriptFunction
75     */
76    public static final MethodHandle S$PROTOTYPE = findOwnMH_S("S$prototype", void.class, Object.class, Object.class);
77
78    /**
79     * Method handle for length getter for this ScriptFunction
80     */
81    public static final MethodHandle G$LENGTH = findOwnMH_S("G$length", int.class, Object.class);
82
83    /**
84     * Method handle for name getter for this ScriptFunction
85     */
86    public static final MethodHandle G$NAME = findOwnMH_S("G$name", Object.class, Object.class);
87
88    /**
89     * Method handle used for implementing sync() in mozilla_compat
90     */
91    public static final MethodHandle INVOKE_SYNC = findOwnMH_S("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
92
93    /**
94     * Method handle for allocate function for this ScriptFunction
95     */
96    static final MethodHandle ALLOCATE = findOwnMH_V("allocate", Object.class);
97
98    private static final MethodHandle WRAPFILTER = findOwnMH_S("wrapFilter", Object.class, Object.class);
99
100    private static final MethodHandle SCRIPTFUNCTION_GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class);
101
102    /**
103     * method handle to scope getter for this ScriptFunction
104     */
105    public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
106
107    private static final MethodHandle IS_FUNCTION_MH = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
108
109    private static final MethodHandle IS_APPLY_FUNCTION = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class);
110
111    private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH_S("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
112
113    private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH_S("addZerothElement", Object[].class, Object[].class, Object.class);
114
115    private static final MethodHandle WRAP_THIS = MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, "wrapThis", MH.type(Object.class, Object.class));
116
117    // various property maps used for different kinds of functions
118    // property map for anonymous function that serves as Function.prototype
119    private static final PropertyMap anonmap$;
120    // property map for strict mode functions
121    private static final PropertyMap strictmodemap$;
122    // property map for bound functions
123    private static final PropertyMap boundfunctionmap$;
124    // property map for non-strict, non-bound functions.
125    private static final PropertyMap map$;
126
127    // Marker object for lazily initialized prototype object
128    private static final Object LAZY_PROTOTYPE = new Object();
129
130    private static final AccessControlContext GET_LOOKUP_PERMISSION_CONTEXT =
131            AccessControlContextFactory.createAccessControlContext(CallSiteDescriptor.GET_LOOKUP_PERMISSION);
132
133    private static PropertyMap createStrictModeMap(final PropertyMap map) {
134        final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE;
135        PropertyMap newMap = map;
136        // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors.
137        newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags));
138        newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags));
139        return newMap;
140    }
141
142    private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) {
143        // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see
144        // ECMAScript 5.1 section 15.3.4.5
145        return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype"));
146    }
147
148    static {
149        anonmap$ = PropertyMap.newMap();
150        final ArrayList<Property> properties = new ArrayList<>(3);
151        properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE));
152        properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null));
153        properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null));
154        map$ = PropertyMap.newMap(properties);
155        strictmodemap$ = createStrictModeMap(map$);
156        boundfunctionmap$ = createBoundFunctionMap(strictmodemap$);
157    }
158
159    private static boolean isStrict(final int flags) {
160        return (flags & ScriptFunctionData.IS_STRICT) != 0;
161    }
162
163    // Choose the map based on strict mode!
164    private static PropertyMap getMap(final boolean strict) {
165        return strict ? strictmodemap$ : map$;
166    }
167
168    /**
169     * The parent scope.
170     */
171    private final ScriptObject scope;
172
173    private final ScriptFunctionData data;
174
175    /**
176     * The property map used for newly allocated object when function is used as
177     * constructor.
178     */
179    protected PropertyMap allocatorMap;
180
181    /**
182     * Reference to constructor prototype.
183     */
184    protected Object prototype;
185
186    /**
187     * Constructor
188     *
189     * @param data static function data
190     * @param map property map
191     * @param scope scope
192     */
193    private ScriptFunction(
194            final ScriptFunctionData data,
195            final PropertyMap map,
196            final ScriptObject scope,
197            final Global global) {
198
199        super(map);
200
201        if (Context.DEBUG) {
202            constructorCount.increment();
203        }
204
205        this.data = data;
206        this.scope = scope;
207        this.setInitialProto(global.getFunctionPrototype());
208        this.prototype = LAZY_PROTOTYPE;
209
210        // We have to fill user accessor functions late as these are stored
211        // in this object rather than in the PropertyMap of this object.
212        assert objectSpill == null;
213        if (isStrict() || isBoundFunction()) {
214            final ScriptFunction typeErrorThrower = global.getTypeErrorThrower();
215            initUserAccessors("arguments", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower);
216            initUserAccessors("caller", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower);
217        }
218    }
219
220    /**
221     * Constructor
222     *
223     * @param name function name
224     * @param methodHandle method handle to function (if specializations are
225     * present, assumed to be most generic)
226     * @param map property map
227     * @param scope scope
228     * @param specs specialized version of this function - other method handles
229     * @param flags {@link ScriptFunctionData} flags
230     */
231    private ScriptFunction(
232            final String name,
233            final MethodHandle methodHandle,
234            final PropertyMap map,
235            final ScriptObject scope,
236            final Specialization[] specs,
237            final int flags,
238            final Global global) {
239        this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope, global);
240    }
241
242    /**
243     * Constructor
244     *
245     * @param name name of function
246     * @param methodHandle handle for invocation
247     * @param scope scope object
248     * @param specs specialized versions of this method, if available, null
249     * otherwise
250     * @param flags {@link ScriptFunctionData} flags
251     */
252    private ScriptFunction(
253            final String name,
254            final MethodHandle methodHandle,
255            final ScriptObject scope,
256            final Specialization[] specs,
257            final int flags) {
258        this(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags, Global.instance());
259    }
260
261    /**
262     * Constructor called by Nasgen generated code, zero added members, use the
263     * default map. Creates builtin functions only.
264     *
265     * @param name name of function
266     * @param invokeHandle handle for invocation
267     * @param specs specialized versions of this method, if available, null
268     * otherwise
269     */
270    protected ScriptFunction(final String name, final MethodHandle invokeHandle, final Specialization[] specs) {
271        this(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR, Global.instance());
272    }
273
274    /**
275     * Constructor called by Nasgen generated code, non zero member count, use
276     * the map passed as argument. Creates builtin functions only.
277     *
278     * @param name name of function
279     * @param invokeHandle handle for invocation
280     * @param map initial property map
281     * @param specs specialized versions of this method, if available, null
282     * otherwise
283     */
284    protected ScriptFunction(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) {
285        this(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR, Global.instance());
286    }
287
288    // Factory methods to create various functions
289    /**
290     * Factory method called by compiler generated code for functions that need
291     * parent scope.
292     *
293     * @param constants the generated class' constant array
294     * @param index the index of the {@code RecompilableScriptFunctionData}
295     * object in the constants array.
296     * @param scope the parent scope object
297     * @return a newly created function object
298     */
299    public static ScriptFunction create(final Object[] constants, final int index, final ScriptObject scope) {
300        final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constants[index];
301        return new ScriptFunction(data, getMap(data.isStrict()), scope, Global.instance());
302    }
303
304    /**
305     * Factory method called by compiler generated code for functions that don't
306     * need parent scope.
307     *
308     * @param constants the generated class' constant array
309     * @param index the index of the {@code RecompilableScriptFunctionData}
310     * object in the constants array.
311     * @return a newly created function object
312     */
313    public static ScriptFunction create(final Object[] constants, final int index) {
314        return create(constants, index, null);
315    }
316
317    /**
318     * Create anonymous function that serves as Function.prototype
319     *
320     * @return anonymous function object
321     */
322    public static ScriptFunction createAnonymous() {
323        return new ScriptFunction("", GlobalFunctions.ANONYMOUS, anonmap$, null);
324    }
325
326    // builtin function create helper factory
327    private static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) {
328        final ScriptFunction func = new ScriptFunction(name, methodHandle, null, specs, flags);
329        func.setPrototype(UNDEFINED);
330        // Non-constructor built-in functions do not have "prototype" property
331        func.deleteOwnProperty(func.getMap().findProperty("prototype"));
332
333        return func;
334    }
335
336    /**
337     * Factory method for non-constructor built-in functions
338     *
339     * @param name function name
340     * @param methodHandle handle for invocation
341     * @param specs specialized versions of function if available, null
342     * otherwise
343     * @return new ScriptFunction
344     */
345    public static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs) {
346        return ScriptFunction.createBuiltin(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN);
347    }
348
349    /**
350     * Factory method for non-constructor built-in functions
351     *
352     * @param name function name
353     * @param methodHandle handle for invocation
354     * @return new ScriptFunction
355     */
356    public static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle) {
357        return ScriptFunction.createBuiltin(name, methodHandle, null);
358    }
359
360    /**
361     * Factory method for non-constructor built-in, strict functions
362     *
363     * @param name function name
364     * @param methodHandle handle for invocation
365     * @return new ScriptFunction
366     */
367    public static ScriptFunction createStrictBuiltin(final String name, final MethodHandle methodHandle) {
368        return ScriptFunction.createBuiltin(name, methodHandle, null, ScriptFunctionData.IS_BUILTIN | ScriptFunctionData.IS_STRICT);
369    }
370
371    // Subclass to represent bound functions
372    private static class Bound extends ScriptFunction {
373        private final ScriptFunction target;
374
375        Bound(final ScriptFunctionData boundData, final ScriptFunction target) {
376            super(boundData, boundfunctionmap$, null, Global.instance());
377            setPrototype(ScriptRuntime.UNDEFINED);
378            this.target = target;
379        }
380
381        @Override
382        protected ScriptFunction getTargetFunction() {
383            return target;
384        }
385    }
386
387    /**
388     * Creates a version of this function bound to a specific "self" and other
389     * arguments, as per {@code Function.prototype.bind} functionality in
390     * ECMAScript 5.1 section 15.3.4.5.
391     *
392     * @param self the self to bind to this function. Can be null (in which
393     * case, null is bound as this).
394     * @param args additional arguments to bind to this function. Can be null or
395     * empty to not bind additional arguments.
396     * @return a function with the specified self and parameters bound.
397     */
398    public final ScriptFunction createBound(final Object self, final Object[] args) {
399        return new Bound(data.makeBoundFunctionData(this, self, args), getTargetFunction());
400    }
401
402    /**
403     * Create a function that invokes this function synchronized on {@code sync}
404     * or the self object of the invocation.
405     *
406     * @param sync the Object to synchronize on, or undefined
407     * @return synchronized function
408     */
409    public final ScriptFunction createSynchronized(final Object sync) {
410        final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync);
411        return createBuiltin(getName(), mh);
412    }
413
414    @Override
415    public String getClassName() {
416        return "Function";
417    }
418
419    /**
420     * ECMA 15.3.5.3 [[HasInstance]] (V) Step 3 if "prototype" value is not an
421     * Object, throw TypeError
422     */
423    @Override
424    public boolean isInstance(final ScriptObject instance) {
425        final Object basePrototype = getTargetFunction().getPrototype();
426        if (!(basePrototype instanceof ScriptObject)) {
427            throw typeError("prototype.not.an.object", ScriptRuntime.safeToString(getTargetFunction()), ScriptRuntime.safeToString(basePrototype));
428        }
429
430        for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
431            if (proto == basePrototype) {
432                return true;
433            }
434        }
435
436        return false;
437    }
438
439    /**
440     * Returns the target function for this function. If the function was not
441     * created using {@link #createBound(Object, Object[])}, its target
442     * function is itself. If it is bound, its target function is the target
443     * function of the function it was made from (therefore, the target function
444     * is always the final, unbound recipient of the calls).
445     *
446     * @return the target function for this function.
447     */
448    protected ScriptFunction getTargetFunction() {
449        return this;
450    }
451
452    final boolean isBoundFunction() {
453        return getTargetFunction() != this;
454    }
455
456    /**
457     * Set the arity of this ScriptFunction
458     *
459     * @param arity arity
460     */
461    public final void setArity(final int arity) {
462        data.setArity(arity);
463    }
464
465    /**
466     * Is this a ECMAScript 'use strict' function?
467     *
468     * @return true if function is in strict mode
469     */
470    public final boolean isStrict() {
471        return data.isStrict();
472    }
473
474    /**
475     * Returns true if this is a non-strict, non-built-in function that requires
476     * non-primitive this argument according to ECMA 10.4.3.
477     *
478     * @return true if this argument must be an object
479     */
480    public final boolean needsWrappedThis() {
481        return data.needsWrappedThis();
482    }
483
484    private static boolean needsWrappedThis(final Object fn) {
485        return fn instanceof ScriptFunction ? ((ScriptFunction) fn).needsWrappedThis() : false;
486    }
487
488    /**
489     * Execute this script function.
490     *
491     * @param self Target object.
492     * @param arguments Call arguments.
493     * @return ScriptFunction result.
494     * @throws Throwable if there is an exception/error with the invocation or
495     * thrown from it
496     */
497    final Object invoke(final Object self, final Object... arguments) throws Throwable {
498        if (Context.DEBUG) {
499            invokes.increment();
500        }
501        return data.invoke(this, self, arguments);
502    }
503
504    /**
505     * Execute this script function as a constructor.
506     *
507     * @param arguments Call arguments.
508     * @return Newly constructed result.
509     * @throws Throwable if there is an exception/error with the invocation or
510     * thrown from it
511     */
512    final Object construct(final Object... arguments) throws Throwable {
513        return data.construct(this, arguments);
514    }
515
516    /**
517     * Allocate function. Called from generated {@link ScriptObject} code for
518     * allocation as a factory method
519     *
520     * @return a new instance of the {@link ScriptObject} whose allocator this
521     * is
522     */
523    @SuppressWarnings("unused")
524    private Object allocate() {
525        if (Context.DEBUG) {
526            allocations.increment();
527        }
528
529        assert !isBoundFunction(); // allocate never invoked on bound functions
530
531        final ScriptObject prototype = getAllocatorPrototype();
532        final ScriptObject object = data.allocate(getAllocatorMap(prototype));
533
534        if (object != null) {
535            object.setInitialProto(prototype);
536        }
537
538        return object;
539    }
540
541    /**
542     * Get the property map used by "allocate"
543     * @param prototype actual prototype object
544     * @return property map
545     */
546    private synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
547        if (allocatorMap == null || allocatorMap.isInvalidSharedMapFor(prototype)) {
548            // The prototype map has changed since this function was last used as constructor.
549            // Get a new allocator map.
550            allocatorMap = data.getAllocatorMap(prototype);
551        }
552        return allocatorMap;
553    }
554
555    /**
556     * Return the actual prototype used by "allocate"
557     * @return allocator prototype
558     */
559    private ScriptObject getAllocatorPrototype() {
560        final Object prototype = getPrototype();
561        if (prototype instanceof ScriptObject) {
562            return (ScriptObject) prototype;
563        }
564        return Global.objectPrototype();
565    }
566
567    @Override
568    public final String safeToString() {
569        return toSource();
570    }
571
572    @Override
573    public final String toString() {
574        return data.toString();
575    }
576
577    /**
578     * Get this function as a String containing its source code. If no source
579     * code exists in this ScriptFunction, its contents will be displayed as
580     * {@code [native code]}
581     *
582     * @return string representation of this function's source
583     */
584    public final String toSource() {
585        return data.toSource();
586    }
587
588    /**
589     * Get the prototype object for this function
590     *
591     * @return prototype
592     */
593    public final Object getPrototype() {
594        if (prototype == LAZY_PROTOTYPE) {
595            prototype = new PrototypeObject(this);
596        }
597        return prototype;
598    }
599
600    /**
601     * Set the prototype object for this function
602     *
603     * @param newPrototype new prototype object
604     */
605    public synchronized final void setPrototype(final Object newPrototype) {
606        if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) {
607            // Unset allocator map to be replaced with one matching the new prototype.
608            allocatorMap = null;
609        }
610        this.prototype = newPrototype;
611    }
612
613    /**
614     * Return the invoke handle bound to a given ScriptObject self reference. If
615     * callee parameter is required result is rebound to this.
616     *
617     * @param self self reference
618     * @return bound invoke handle
619     */
620    public final MethodHandle getBoundInvokeHandle(final Object self) {
621        return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), self);
622    }
623
624    /**
625     * Bind the method handle to this {@code ScriptFunction} instance if it
626     * needs a callee parameter. If this function's method handles don't have a
627     * callee parameter, the handle is returned unchanged.
628     *
629     * @param methodHandle the method handle to potentially bind to this
630     * function instance.
631     * @return the potentially bound method handle
632     */
633    private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) {
634        return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle;
635
636    }
637
638    /**
639     * Get the name for this function
640     *
641     * @return the name
642     */
643    public final String getName() {
644        return data.getName();
645    }
646
647    /**
648     * Get the scope for this function
649     *
650     * @return the scope
651     */
652    public final ScriptObject getScope() {
653        return scope;
654    }
655
656    /**
657     * Prototype getter for this ScriptFunction - follows the naming convention
658     * used by Nasgen and the code generator
659     *
660     * @param self self reference
661     * @return self's prototype
662     */
663    public static Object G$prototype(final Object self) {
664        return self instanceof ScriptFunction
665                ? ((ScriptFunction) self).getPrototype()
666                : UNDEFINED;
667    }
668
669    /**
670     * Prototype setter for this ScriptFunction - follows the naming convention
671     * used by Nasgen and the code generator
672     *
673     * @param self self reference
674     * @param prototype prototype to set
675     */
676    public static void S$prototype(final Object self, final Object prototype) {
677        if (self instanceof ScriptFunction) {
678            ((ScriptFunction) self).setPrototype(prototype);
679        }
680    }
681
682    /**
683     * Length getter - ECMA 15.3.3.2: Function.length
684     *
685     * @param self self reference
686     * @return length
687     */
688    public static int G$length(final Object self) {
689        if (self instanceof ScriptFunction) {
690            return ((ScriptFunction) self).data.getArity();
691        }
692
693        return 0;
694    }
695
696    /**
697     * Name getter - ECMA Function.name
698     *
699     * @param self self refence
700     * @return the name, or undefined if none
701     */
702    public static Object G$name(final Object self) {
703        if (self instanceof ScriptFunction) {
704            return ((ScriptFunction) self).getName();
705        }
706
707        return UNDEFINED;
708    }
709
710    /**
711     * Get the prototype for this ScriptFunction
712     *
713     * @param constructor constructor
714     * @return prototype, or null if given constructor is not a ScriptFunction
715     */
716    public static ScriptObject getPrototype(final ScriptFunction constructor) {
717        if (constructor != null) {
718            final Object proto = constructor.getPrototype();
719            if (proto instanceof ScriptObject) {
720                return (ScriptObject) proto;
721            }
722        }
723
724        return null;
725    }
726
727    // These counters are updated only in debug mode.
728    private static LongAdder constructorCount;
729    private static LongAdder invokes;
730    private static LongAdder allocations;
731
732    static {
733        if (Context.DEBUG) {
734            constructorCount = new LongAdder();
735            invokes = new LongAdder();
736            allocations = new LongAdder();
737        }
738    }
739
740    /**
741     * @return the constructorCount
742     */
743    public static long getConstructorCount() {
744        return constructorCount.longValue();
745    }
746
747    /**
748     * @return the invokes
749     */
750    public static long getInvokes() {
751        return invokes.longValue();
752    }
753
754    /**
755     * @return the allocations
756     */
757    public static long getAllocations() {
758        return allocations.longValue();
759    }
760
761    @Override
762    protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
763        final MethodType type = desc.getMethodType();
764        assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
765        final CompiledFunction cf = data.getBestConstructor(type, scope, CompiledFunction.NO_FUNCTIONS);
766        final GuardedInvocation bestCtorInv = cf.createConstructorInvocation();
767        //TODO - ClassCastException
768        return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null);
769    }
770
771    private static Object wrapFilter(final Object obj) {
772        if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
773            return obj;
774        }
775        return Context.getGlobal().wrapAsObject(obj);
776    }
777
778    @SuppressWarnings("unused")
779    private static Object globalFilter(final Object object) {
780        // replace whatever we get with the current global object
781        return Context.getGlobal();
782    }
783
784    /**
785     * Some receivers are primitive, in that case, according to the Spec we
786     * create a new native object per callsite with the wrap filter. We can only
787     * apply optimistic builtins if there is no per instance state saved for
788     * these wrapped objects (e.g. currently NativeStrings), otherwise we can't
789     * create optimistic versions
790     *
791     * @param self receiver
792     * @param linkLogicClass linkLogicClass, or null if no link logic exists
793     * @return link logic instance, or null if one could not be constructed for
794     * this receiver
795     */
796    private static LinkLogic getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass) {
797        if (linkLogicClass == null) {
798            return LinkLogic.EMPTY_INSTANCE; //always OK to link this, specialization but without special linking logic
799        }
800
801        if (!Context.getContextTrusted().getEnv()._optimistic_types) {
802            return null; //if optimistic types are off, optimistic builtins are too
803        }
804
805        final Object wrappedSelf = wrapFilter(self);
806        if (wrappedSelf instanceof OptimisticBuiltins) {
807            if (wrappedSelf != self && ((OptimisticBuiltins) wrappedSelf).hasPerInstanceAssumptions()) {
808                return null; //pessimistic - we created a wrapped object different from the primitive, but the assumptions have instance state
809            }
810            return ((OptimisticBuiltins) wrappedSelf).getLinkLogic(linkLogicClass);
811        }
812        return null;
813    }
814
815    /**
816     * dyn:call call site signature: (callee, thiz, [args...]) generated method
817     * signature: (callee, thiz, [args...])
818     *
819     * cases:
820     * (a) method has callee parameter
821     *     (1) for local/scope calls, we just bind thiz and drop the second argument.
822     *     (2) for normal this-calls, we have to swap thiz and callee to get matching signatures.
823     * (b) method doesn't have callee parameter (builtin functions)
824     *     (3) for local/scope calls, bind thiz and drop both callee and thiz.
825     *     (4) for normal this-calls, drop callee.
826     *
827     * @return guarded invocation for call
828     */
829    @Override
830    protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
831        final MethodType type = desc.getMethodType();
832
833        final String name = getName();
834        final boolean isUnstable = request.isCallSiteUnstable();
835        final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
836        final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name);
837        final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name);
838
839        final boolean isApplyOrCall = isCall | isApply;
840
841        if (isUnstable && !isApplyOrCall) {
842            //megamorphic - replace call with apply
843            final MethodHandle handle;
844            //ensure that the callsite is vararg so apply can consume it
845            if (type.parameterCount() == 3 && type.parameterType(2) == Object[].class) {
846                // Vararg call site
847                handle = ScriptRuntime.APPLY.methodHandle();
848            } else {
849                // (callee, this, args...) => (callee, this, args[])
850                handle = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2);
851            }
852
853            // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a
854            // generic "is this a ScriptFunction?" guard.
855            return new GuardedInvocation(
856                    handle,
857                    null,
858                    (SwitchPoint) null,
859                    ClassCastException.class);
860        }
861
862        MethodHandle boundHandle;
863        MethodHandle guard = null;
864
865        // Special handling of Function.apply and Function.call. Note we must be invoking
866        if (isApplyOrCall && !isUnstable) {
867            final Object[] args = request.getArguments();
868            if (Bootstrap.isCallable(args[1])) {
869                return createApplyOrCallCall(isApply, desc, request, args);
870            }
871        } //else just fall through and link as ordinary function or unstable apply
872
873        int programPoint = INVALID_PROGRAM_POINT;
874        if (NashornCallSiteDescriptor.isOptimistic(desc)) {
875            programPoint = NashornCallSiteDescriptor.getProgramPoint(desc);
876        }
877
878        CompiledFunction cf = data.getBestInvoker(type, scope, CompiledFunction.NO_FUNCTIONS);
879        final Object self = request.getArguments()[1];
880        final Collection<CompiledFunction> forbidden = new HashSet<>();
881
882        //check for special fast versions of the compiled function
883        final List<SwitchPoint> sps = new ArrayList<>();
884        Class<? extends Throwable> exceptionGuard = null;
885
886        while (cf.isSpecialization()) {
887            final Class<? extends LinkLogic> linkLogicClass = cf.getLinkLogicClass();
888            //if linklogic is null, we can always link with the standard mechanism, it's still a specialization
889            final LinkLogic linkLogic = getLinkLogic(self, linkLogicClass);
890
891            if (linkLogic != null && linkLogic.checkLinkable(self, desc, request)) {
892                final DebugLogger log = Context.getContextTrusted().getLogger(Compiler.class);
893
894                if (log.isEnabled()) {
895                    log.info("Linking optimistic builtin function: '", name, "' args=", Arrays.toString(request.getArguments()), " desc=", desc);
896                }
897
898                exceptionGuard = linkLogic.getRelinkException();
899
900                break;
901            }
902
903            //could not link this specialization because link check failed
904            forbidden.add(cf);
905            final CompiledFunction oldCf = cf;
906            cf = data.getBestInvoker(type, scope, forbidden);
907            assert oldCf != cf;
908        }
909
910        final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint);
911        final MethodHandle callHandle = bestInvoker.getInvocation();
912
913        if (data.needsCallee()) {
914            if (scopeCall && needsWrappedThis()) {
915                // (callee, this, args...) => (callee, [this], args...)
916                boundHandle = MH.filterArguments(callHandle, 1, SCRIPTFUNCTION_GLOBALFILTER);
917            } else {
918                // It's already (callee, this, args...), just what we need
919                boundHandle = callHandle;
920            }
921        } else if (data.isBuiltin() && "extend".equals(data.getName())) {
922            // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
923            // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
924            boundHandle = MH.dropArguments(MH.bindTo(callHandle, getLookupPrivileged(desc)), 0, type.parameterType(0), type.parameterType(1));
925        } else if (scopeCall && needsWrappedThis()) {
926            // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
927            // (this, args...) => ([this], args...)
928            boundHandle = MH.filterArguments(callHandle, 0, SCRIPTFUNCTION_GLOBALFILTER);
929            // ([this], args...) => ([callee], [this], args...)
930            boundHandle = MH.dropArguments(boundHandle, 0, type.parameterType(0));
931        } else {
932            // (this, args...) => ([callee], this, args...)
933            boundHandle = MH.dropArguments(callHandle, 0, type.parameterType(0));
934        }
935
936        // For non-strict functions, check whether this-object is primitive type.
937        // If so add a to-object-wrapper argument filter.
938        // Else install a guard that will trigger a relink when the argument becomes primitive.
939        if (!scopeCall && needsWrappedThis()) {
940            if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) {
941                boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
942            } else {
943                guard = getNonStrictFunctionGuard(this);
944            }
945        }
946
947        boundHandle = pairArguments(boundHandle, type);
948
949        if (bestInvoker.getSwitchPoints() != null) {
950            sps.addAll(Arrays.asList(bestInvoker.getSwitchPoints()));
951        }
952        final SwitchPoint[] spsArray = sps.isEmpty() ? null : sps.toArray(new SwitchPoint[sps.size()]);
953
954        return new GuardedInvocation(
955                boundHandle,
956                guard == null ?
957                        getFunctionGuard(
958                                this,
959                                cf.getFlags()) :
960                        guard,
961                spsArray,
962                exceptionGuard);
963    }
964
965    private static Lookup getLookupPrivileged(final CallSiteDescriptor desc) {
966        // NOTE: we'd rather not make NashornCallSiteDescriptor.getLookupPrivileged public.
967        return AccessController.doPrivileged((PrivilegedAction<Lookup>)()->desc.getLookup(),
968                GET_LOOKUP_PERMISSION_CONTEXT);
969    }
970
971    private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
972        final MethodType descType = desc.getMethodType();
973        final int paramCount = descType.parameterCount();
974        if (descType.parameterType(paramCount - 1).isArray()) {
975            // This is vararg invocation of apply or call. This can normally only happen when we do a recursive
976            // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate
977            // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader.
978            return createVarArgApplyOrCallCall(isApply, desc, request, args);
979        }
980
981        final boolean passesThis = paramCount > 2;
982        final boolean passesArgs = paramCount > 3;
983        final int realArgCount = passesArgs ? paramCount - 3 : 0;
984
985        final Object appliedFn = args[1];
986        final boolean appliedFnNeedsWrappedThis = needsWrappedThis(appliedFn);
987
988        //box call back to apply
989        CallSiteDescriptor appliedDesc = desc;
990        final SwitchPoint applyToCallSwitchPoint = Global.getBuiltinFunctionApplySwitchPoint();
991        //enough to change the proto switchPoint here
992
993        final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
994        final boolean isFailedApplyToCall = isApplyToCall && applyToCallSwitchPoint.hasBeenInvalidated();
995
996        // R(apply|call, ...) => R(...)
997        MethodType appliedType = descType.dropParameterTypes(0, 1);
998        if (!passesThis) {
999            // R() => R(this)
1000            appliedType = appliedType.insertParameterTypes(1, Object.class);
1001        } else if (appliedFnNeedsWrappedThis) {
1002            appliedType = appliedType.changeParameterType(1, Object.class);
1003        }
1004
1005        /*
1006         * dropArgs is a synthetic method handle that contains any args that we need to
1007         * get rid of that come after the arguments array in the apply case. We adapt
1008         * the callsite to ask for 3 args only and then dropArguments on the method handle
1009         * to make it fit the extraneous args.
1010         */
1011        MethodType dropArgs = MH.type(void.class);
1012        if (isApply && !isFailedApplyToCall) {
1013            final int pc = appliedType.parameterCount();
1014            for (int i = 3; i < pc; i++) {
1015                dropArgs = dropArgs.appendParameterTypes(appliedType.parameterType(i));
1016            }
1017            if (pc > 3) {
1018                appliedType = appliedType.dropParameterTypes(3, pc);
1019            }
1020        }
1021
1022        if (isApply || isFailedApplyToCall) {
1023            if (passesArgs) {
1024                // R(this, args) => R(this, Object[])
1025                appliedType = appliedType.changeParameterType(2, Object[].class);
1026                // drop any extraneous arguments for the apply fail case
1027                if (isFailedApplyToCall) {
1028                    appliedType = appliedType.dropParameterTypes(3, paramCount - 1);
1029                }
1030            } else {
1031                // R(this) => R(this, Object[])
1032                appliedType = appliedType.insertParameterTypes(2, Object[].class);
1033            }
1034        }
1035
1036        appliedDesc = appliedDesc.changeMethodType(appliedType); //no extra args
1037
1038        // Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation
1039        final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()];
1040        appliedArgs[0] = appliedFn;
1041        appliedArgs[1] = passesThis ? appliedFnNeedsWrappedThis ? ScriptFunctionData.wrapThis(args[2]) : args[2] : ScriptRuntime.UNDEFINED;
1042        if (isApply && !isFailedApplyToCall) {
1043            appliedArgs[2] = passesArgs ? NativeFunction.toApplyArgs(args[3]) : ScriptRuntime.EMPTY_ARRAY;
1044        } else {
1045            if (passesArgs) {
1046                if (isFailedApplyToCall) {
1047                    final Object[] tmp = new Object[args.length - 3];
1048                    System.arraycopy(args, 3, tmp, 0, tmp.length);
1049                    appliedArgs[2] = NativeFunction.toApplyArgs(tmp);
1050                } else {
1051                    assert !isApply;
1052                    System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
1053                }
1054            } else if (isFailedApplyToCall) {
1055                appliedArgs[2] = ScriptRuntime.EMPTY_ARRAY;
1056            }
1057        }
1058
1059        // Ask the linker machinery for an invocation of the target function
1060        final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs);
1061
1062        GuardedInvocation appliedInvocation;
1063        try {
1064            appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest);
1065        } catch (final RuntimeException | Error e) {
1066            throw e;
1067        } catch (final Exception e) {
1068            throw new RuntimeException(e);
1069        }
1070        assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage.
1071
1072        final Class<?> applyFnType = descType.parameterType(0);
1073        MethodHandle inv = appliedInvocation.getInvocation(); //method handle from apply invocation. the applied function invocation
1074
1075        if (isApply && !isFailedApplyToCall) {
1076            if (passesArgs) {
1077                // Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it.
1078                inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS);
1079            } else {
1080                // If the original call site doesn't pass argArray, pass in an empty array
1081                inv = MH.insertArguments(inv, 2, (Object) ScriptRuntime.EMPTY_ARRAY);
1082            }
1083        }
1084
1085        if (isApplyToCall) {
1086            if (isFailedApplyToCall) {
1087                //take the real arguments that were passed to a call and force them into the apply instead
1088                Context.getContextTrusted().getLogger(ApplySpecialization.class).info("Collection arguments to revert call to apply in " + appliedFn);
1089                inv = MH.asCollector(inv, Object[].class, realArgCount);
1090            } else {
1091                appliedInvocation = appliedInvocation.addSwitchPoint(applyToCallSwitchPoint);
1092            }
1093        }
1094
1095        if (!passesThis) {
1096            // If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed
1097            inv = bindImplicitThis(appliedFn, inv);
1098        } else if (appliedFnNeedsWrappedThis) {
1099            // target function needs a wrapped this, so make sure we filter for that
1100            inv = MH.filterArguments(inv, 1, WRAP_THIS);
1101        }
1102        inv = MH.dropArguments(inv, 0, applyFnType);
1103
1104        /*
1105         * Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which
1106         * is when we need to add arguments to the callsite to catch and ignore the synthetic
1107         * extra args that someone has added to the command line.
1108         */
1109        for (int i = 0; i < dropArgs.parameterCount(); i++) {
1110            inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i));
1111        }
1112
1113        MethodHandle guard = appliedInvocation.getGuard();
1114        // If the guard checks the value of "this" but we aren't passing thisArg, insert the default one
1115        if (!passesThis && guard.type().parameterCount() > 1) {
1116            guard = bindImplicitThis(appliedFn, guard);
1117        }
1118        final MethodType guardType = guard.type();
1119
1120        // We need to account for the dropped (apply|call) function argument.
1121        guard = MH.dropArguments(guard, 0, descType.parameterType(0));
1122        // Take the "isApplyFunction" guard, and bind it to this function.
1123        MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); //TODO replace this with switchpoint
1124        // Adapt the guard to receive all the arguments that the original guard does.
1125        applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray());
1126        // Fold the original function guard into our apply guard.
1127        guard = MH.foldArguments(applyFnGuard, guard);
1128
1129        return appliedInvocation.replaceMethods(inv, guard);
1130    }
1131
1132    /*
1133     * This method is used for linking nested apply. Specialized apply and call linking will create a variable arity
1134     * call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with
1135     * Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method.
1136     * This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back
1137     * to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to
1138     * invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity
1139     * invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already
1140     * solved by createApplyOrCallCall) non-vararg call site linking.
1141     */
1142    private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc,
1143            final LinkRequest request, final Object[] args) {
1144        final MethodType descType = desc.getMethodType();
1145        final int paramCount = descType.parameterCount();
1146        final Object[] varArgs = (Object[]) args[paramCount - 1];
1147        // -1 'cause we're not passing the vararg array itself
1148        final int copiedArgCount = args.length - 1;
1149        final int varArgCount = varArgs.length;
1150
1151        // Spread arguments for the delegate createApplyOrCallCall invocation.
1152        final Object[] spreadArgs = new Object[copiedArgCount + varArgCount];
1153        System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount);
1154        System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount);
1155
1156        // Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and
1157        // replace it with a list of Object.class.
1158        final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes(
1159                Collections.<Class<?>>nCopies(varArgCount, Object.class));
1160        final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType);
1161
1162        // Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/
1163        final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs);
1164        final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs);
1165
1166        // Add spreader combinators to returned invocation and guard.
1167        return spreadInvocation.replaceMethods(
1168                // Use standard ScriptObject.pairArguments on the invocation
1169                pairArguments(spreadInvocation.getInvocation(), descType),
1170                // Use our specialized spreadGuardArguments on the guard (see below).
1171                spreadGuardArguments(spreadInvocation.getGuard(), descType));
1172    }
1173
1174    private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) {
1175        final MethodType guardType = guard.type();
1176        final int guardParamCount = guardType.parameterCount();
1177        final int descParamCount = descType.parameterCount();
1178        final int spreadCount = guardParamCount - descParamCount + 1;
1179        if (spreadCount <= 0) {
1180            // Guard doesn't dip into the varargs
1181            return guard;
1182        }
1183
1184        final MethodHandle arrayConvertingGuard;
1185        // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply
1186        // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail
1187        // with ClassCastException of NativeArray to Object[].
1188        if (guardType.parameterType(guardParamCount - 1).isArray()) {
1189            arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS);
1190        } else {
1191            arrayConvertingGuard = guard;
1192        }
1193
1194        return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount);
1195    }
1196
1197    private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
1198        final MethodHandle bound;
1199        if (fn instanceof ScriptFunction && ((ScriptFunction) fn).needsWrappedThis()) {
1200            bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER);
1201        } else {
1202            bound = mh;
1203        }
1204        return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED);
1205    }
1206
1207    /**
1208     * Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
1209     *
1210     * These don't want a callee parameter, so bind that. Name binding is
1211     * optional.
1212     */
1213    MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
1214        return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type);
1215    }
1216
1217    private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
1218        if (bindName == null) {
1219            return methodHandle;
1220        }
1221
1222        // if it is vararg method, we need to extend argument array with
1223        // a new zeroth element that is set to bindName value.
1224        final MethodType methodType = methodHandle.type();
1225        final int parameterCount = methodType.parameterCount();
1226        final boolean isVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
1227
1228        if (isVarArg) {
1229            return MH.filterArguments(methodHandle, 1, MH.insertArguments(ADD_ZEROTH_ELEMENT, 1, bindName));
1230        }
1231        return MH.insertArguments(methodHandle, 1, bindName);
1232    }
1233
1234    /**
1235     * Get the guard that checks if a {@link ScriptFunction} is equal to a known
1236     * ScriptFunction, using reference comparison
1237     *
1238     * @param function The ScriptFunction to check against. This will be bound
1239     * to the guard method handle
1240     *
1241     * @return method handle for guard
1242     */
1243    private static MethodHandle getFunctionGuard(final ScriptFunction function, final int flags) {
1244        assert function.data != null;
1245        // Built-in functions have a 1-1 correspondence to their ScriptFunctionData, so we can use a cheaper identity
1246        // comparison for them.
1247        if (function.data.isBuiltin()) {
1248            return Guards.getIdentityGuard(function);
1249        }
1250        return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
1251    }
1252
1253    /**
1254     * Get a guard that checks if a {@link ScriptFunction} is equal to a known
1255     * ScriptFunction using reference comparison, and whether the type of the
1256     * second argument (this-object) is not a JavaScript primitive type.
1257     *
1258     * @param function The ScriptFunction to check against. This will be bound
1259     * to the guard method handle
1260     *
1261     * @return method handle for guard
1262     */
1263    private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) {
1264        assert function.data != null;
1265        return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data);
1266    }
1267
1268    @SuppressWarnings("unused")
1269    private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) {
1270        return self instanceof ScriptFunction && ((ScriptFunction) self).data == data;
1271    }
1272
1273    @SuppressWarnings("unused")
1274    private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) {
1275        return self instanceof ScriptFunction && ((ScriptFunction) self).data == data && arg instanceof ScriptObject;
1276    }
1277
1278    //TODO this can probably be removed given that we have builtin switchpoints in the context
1279    @SuppressWarnings("unused")
1280    private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) {
1281        // NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call()
1282        return appliedFnCondition && self == expectedSelf;
1283    }
1284
1285    @SuppressWarnings("unused")
1286    private static Object[] addZerothElement(final Object[] args, final Object value) {
1287        // extends input array with by adding new zeroth element
1288        final Object[] src = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
1289        final Object[] result = new Object[src.length + 1];
1290        System.arraycopy(src, 0, result, 1, src.length);
1291        result[0] = value;
1292        return result;
1293    }
1294
1295    @SuppressWarnings("unused")
1296    private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args)
1297            throws Throwable {
1298        final Object syncObj = sync == UNDEFINED ? self : sync;
1299        synchronized (syncObj) {
1300            return func.invoke(self, args);
1301        }
1302    }
1303
1304    private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
1305        return MH.findStatic(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
1306    }
1307
1308    private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
1309        return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
1310    }
1311}
1312