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