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