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