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