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 
26 package jdk.nashorn.internal.runtime;
27 
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 
32 import java.io.IOException;
33 import java.io.ObjectInputStream;
34 import java.io.Serializable;
35 import java.lang.invoke.MethodHandle;
36 import java.lang.invoke.MethodHandles;
37 import java.lang.invoke.MethodType;
38 import java.util.Collection;
39 import java.util.LinkedList;
40 import java.util.List;
41 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
42 
43 
44 /**
45  * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
46  * Instances of this class are created during codegen and stored in script classes'
47  * constants array to reduce function instantiation overhead during runtime.
48  */
49 public abstract class ScriptFunctionData implements Serializable {
50     static final int MAX_ARITY = LinkerCallSite.ARGLIMIT;
51     static {
52         // Assert it fits in a byte, as that's what we store it in. It's just a size optimization though, so if needed
53         // "byte arity" field can be widened.
54         assert MAX_ARITY < 256;
55     }
56 
57     /** Name of the function or "" for anonymous functions */
58     protected final String name;
59 
60     /**
61      * A list of code versions of a function sorted in ascending order of generic descriptors.
62      */
63     protected transient LinkedList<CompiledFunction> code = new LinkedList<>();
64 
65     /** Function flags */
66     protected int flags;
67 
68     // Parameter arity of the function, corresponding to "f.length". E.g. "function f(a, b, c) { ... }" arity is 3, and
69     // some built-in ECMAScript functions have their arity declared by the specification. Note that regardless of this
70     // value, the function might still be capable of receiving variable number of arguments, see isVariableArity.
71     private int arity;
72 
73     /**
74      * A pair of method handles used for generic invoker and constructor. Field is volatile as it can be initialized by
75      * multiple threads concurrently, but we still tolerate a race condition in it as all values stored into it are
76      * idempotent.
77      */
78     private volatile transient GenericInvokers genericInvokers;
79 
80     private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
81 
82     /** Is this a strict mode function? */
83     public static final int IS_STRICT            = 1 << 0;
84     /** Is this a built-in function? */
85     public static final int IS_BUILTIN           = 1 << 1;
86     /** Is this a constructor function? */
87     public static final int IS_CONSTRUCTOR       = 1 << 2;
88     /** Does this function expect a callee argument? */
89     public static final int NEEDS_CALLEE         = 1 << 3;
90     /** Does this function make use of the this-object argument? */
91     public static final int USES_THIS            = 1 << 4;
92     /** Is this a variable arity function? */
93     public static final int IS_VARIABLE_ARITY    = 1 << 5;
94     /** Is this a object literal property getter or setter? */
95     public static final int IS_PROPERTY_ACCESSOR = 1 << 6;
96     /** Is this an ES6 method? */
97     public static final int IS_ES6_METHOD        = 1 << 7;
98 
99     /** Flag for strict or built-in functions */
100     public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN;
101     /** Flag for built-in constructors */
102     public static final int IS_BUILTIN_CONSTRUCTOR = IS_BUILTIN | IS_CONSTRUCTOR;
103 
104     private static final long serialVersionUID = 4252901245508769114L;
105 
106     /**
107      * Constructor
108      *
109      * @param name  script function name
110      * @param arity arity
111      * @param flags the function flags
112      */
113     ScriptFunctionData(final String name, final int arity, final int flags) {
114         this.name  = name;
115         this.flags = flags;
116         setArity(arity);
117     }
118 
119     final int getArity() {
120         return arity;
121     }
122 
123     String getDocumentation() {
124         return toSource();
125     }
126 
127     String getDocumentationKey() {
128         return null;
129     }
130 
131     final boolean isVariableArity() {
132         return (flags & IS_VARIABLE_ARITY) != 0;
133     }
134 
135     /**
136      * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final
137      * @param arity new arity
138      */
139     void setArity(final int arity) {
140         if(arity < 0 || arity > MAX_ARITY) {
141             throw new IllegalArgumentException(String.valueOf(arity));
142         }
143         this.arity = arity;
144     }
145 
146     /**
147      * Used from nasgen generated code.
148      *
149      * @param docKey documentation key for this function
150      */
151     void setDocumentationKey(final String docKey) {
152     }
153 
154 
155     CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
156         final MethodHandle boundInvoker = bindInvokeHandle(originalInv.createComposableInvoker(), fn, self, args);
157 
158         if (isConstructor()) {
159             return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.createComposableConstructor(), fn, args), null);
160         }
161 
162         return new CompiledFunction(boundInvoker);
163     }
164 
165     /**
166      * Is this a ScriptFunction generated with strict semantics?
167      * @return true if strict, false otherwise
168      */
169     public final boolean isStrict() {
170         return (flags & IS_STRICT) != 0;
171     }
172 
173     /**
174      * Return the complete internal function name for this
175      * data, not anonymous or similar. May be identical
176      * @return internal function name
177      */
178     protected String getFunctionName() {
179         return getName();
180     }
181 
182     final boolean isBuiltin() {
183         return (flags & IS_BUILTIN) != 0;
184     }
185 
186     final boolean isConstructor() {
187         return (flags & IS_CONSTRUCTOR) != 0;
188     }
189 
190     abstract boolean needsCallee();
191 
192     /**
193      * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
194      * according to ECMA 10.4.3.
195      * @return true if this argument must be an object
196      */
197     final boolean needsWrappedThis() {
198         return (flags & USES_THIS) != 0 && (flags & IS_STRICT_OR_BUILTIN) == 0;
199     }
200 
201     String toSource() {
202         return "function " + (name == null ? "" : name) + "() { [native code] }";
203     }
204 
205     String getName() {
206         return name;
207     }
208 
209     /**
210      * Get this function as a String containing its source code. If no source code
211      * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
212      *
213      * @return string representation of this function
214      */
215     @Override
216     public String toString() {
217         return name.isEmpty() ? "<anonymous>" : name;
218     }
219 
220     /**
221      * Verbose description of data
222      * @return verbose description
223      */
224     public String toStringVerbose() {
225         final StringBuilder sb = new StringBuilder();
226 
227         sb.append("name='").
228                 append(name.isEmpty() ? "<anonymous>" : name).
229                 append("' ").
230                 append(code.size()).
231                 append(" invokers=").
232                 append(code);
233 
234         return sb.toString();
235     }
236 
237     /**
238      * Pick the best invoker, i.e. the one version of this method with as narrow and specific
239      * types as possible. If the call site arguments are objects, but boxed primitives we can
240      * also try to get a primitive version of the method and do an unboxing filter, but then
241      * we need to insert a guard that checks the argument is really always a boxed primitive
242      * and not suddenly a "real" object
243      *
244      * @param callSiteType callsite type
245      * @return compiled function object representing the best invoker.
246      */
247     final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope) {
248         return getBestInvoker(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS);
249     }
250 
251     final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
252         final CompiledFunction cf = getBest(callSiteType, runtimeScope, forbidden);
253         assert cf != null;
254         return cf;
255     }
256 
257     final CompiledFunction getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
258         if (!isConstructor()) {
259             throw typeError("not.a.constructor", toSource());
260         }
261         // Constructor call sites don't have a "this", but getBest is meant to operate on "callee, this, ..." style
262         final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope, forbidden);
263         return cf;
264     }
265 
266     /**
267      * If we can have lazy code generation, this is a hook to ensure that the code has been compiled.
268      * This does not guarantee the code been installed in this {@code ScriptFunctionData} instance
269      */
270     protected void ensureCompiled() {
271         //empty
272     }
273 
274     /**
275      * Return a generic Object/Object invoker for this method. It will ensure code
276      * is generated, get the most generic of all versions of this function and adapt it
277      * to Objects.
278      *
279      * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the
280      * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime
281      * scope is not known, but that might cause compilation of code that will need more deoptimization passes.
282      * @return generic invoker of this script function
283      */
284     final MethodHandle getGenericInvoker(final ScriptObject runtimeScope) {
285         // This method has race conditions both on genericsInvoker and genericsInvoker.invoker, but even if invoked
286         // concurrently, they'll create idempotent results, so it doesn't matter. We could alternatively implement this
287         // using java.util.concurrent.AtomicReferenceFieldUpdater, but it's hardly worth it.
288         final GenericInvokers lgenericInvokers = ensureGenericInvokers();
289         MethodHandle invoker = lgenericInvokers.invoker;
290         if(invoker == null) {
291             lgenericInvokers.invoker = invoker = createGenericInvoker(runtimeScope);
292         }
293         return invoker;
294     }
295 
296     private MethodHandle createGenericInvoker(final ScriptObject runtimeScope) {
297         return makeGenericMethod(getGeneric(runtimeScope).createComposableInvoker());
298     }
299 
300     final MethodHandle getGenericConstructor(final ScriptObject runtimeScope) {
301         // This method has race conditions both on genericsInvoker and genericsInvoker.constructor, but even if invoked
302         // concurrently, they'll create idempotent results, so it doesn't matter. We could alternatively implement this
303         // using java.util.concurrent.AtomicReferenceFieldUpdater, but it's hardly worth it.
304         final GenericInvokers lgenericInvokers = ensureGenericInvokers();
305         MethodHandle constructor = lgenericInvokers.constructor;
306         if(constructor == null) {
307             lgenericInvokers.constructor = constructor = createGenericConstructor(runtimeScope);
308         }
309         return constructor;
310     }
311 
312     private MethodHandle createGenericConstructor(final ScriptObject runtimeScope) {
313         return makeGenericMethod(getGeneric(runtimeScope).createComposableConstructor());
314     }
315 
316     private GenericInvokers ensureGenericInvokers() {
317         GenericInvokers lgenericInvokers = genericInvokers;
318         if(lgenericInvokers == null) {
319             genericInvokers = lgenericInvokers = new GenericInvokers();
320         }
321         return lgenericInvokers;
322     }
323 
324     private static MethodType widen(final MethodType cftype) {
325         final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
326         for (int i = 0; i < cftype.parameterCount(); i++) {
327             paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
328         }
329         return MH.type(cftype.returnType(), paramTypes);
330     }
331 
332     /**
333      * Used to find an apply to call version that fits this callsite.
334      * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
335      * for (Object, Object, int, int, int) or we will destroy the semantics and get
336      * a function that, when padded with undefined values, behaves differently
337      * @param type actual call site type
338      * @return apply to call that perfectly fits this callsite or null if none found
339      */
340     CompiledFunction lookupExactApplyToCall(final MethodType type) {
341         // Callsite type always has callee, drop it if this function doesn't need it.
342         final MethodType adaptedType = needsCallee() ? type : type.dropParameterTypes(0, 1);
343 
344         for (final CompiledFunction cf : code) {
345             if (!cf.isApplyToCall()) {
346                 continue;
347             }
348 
349             final MethodType cftype = cf.type();
350             if (cftype.parameterCount() != adaptedType.parameterCount()) {
351                 continue;
352             }
353 
354             if (widen(cftype).equals(widen(adaptedType))) {
355                 return cf;
356             }
357         }
358 
359         return null;
360     }
361 
362     CompiledFunction pickFunction(final MethodType callSiteType, final boolean canPickVarArg) {
363         for (final CompiledFunction candidate : code) {
364             if (candidate.matchesCallSite(callSiteType, canPickVarArg)) {
365                 return candidate;
366             }
367         }
368         return null;
369     }
370 
371     /**
372      * Returns the best function for the specified call site type.
373      * @param callSiteType The call site type. Call site types are expected to have the form
374      * {@code (callee, this[, args...])}.
375      * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the
376      * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime
377      * scope is not known, but that might cause compilation of code that will need more deoptimization passes.
378      * @param linkLogicOkay is a CompiledFunction with a LinkLogic acceptable?
379      * @return the best function for the specified call site type.
380      */
381     abstract CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden, final boolean linkLogicOkay);
382 
383     /**
384      * Returns the best function for the specified call site type.
385      * @param callSiteType The call site type. Call site types are expected to have the form
386      * {@code (callee, this[, args...])}.
387      * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the
388      * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime
389      * scope is not known, but that might cause compilation of code that will need more deoptimization passes.
390      * @return the best function for the specified call site type.
391      */
392     final CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
393         return getBest(callSiteType, runtimeScope, forbidden, true);
394     }
395 
396     boolean isValidCallSite(final MethodType callSiteType) {
397         return callSiteType.parameterCount() >= 2  && // Must have at least (callee, this)
398                callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class); // Callee must be assignable from script function
399     }
400 
401     CompiledFunction getGeneric(final ScriptObject runtimeScope) {
402         return getBest(getGenericType(), runtimeScope, CompiledFunction.NO_FUNCTIONS, false);
403     }
404 
405     /**
406      * Get a method type for a generic invoker.
407      * @return the method type for the generic invoker
408      */
409     abstract MethodType getGenericType();
410 
411     /**
412      * Allocates an object using this function's allocator.
413      *
414      * @param map the property map for the allocated object.
415      * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
416      */
417     ScriptObject allocate(final PropertyMap map) {
418         return null;
419     }
420 
421     /**
422      * Get the property map to use for objects allocated by this function.
423      *
424      * @param prototype the prototype of the allocated object
425      * @return the property map for allocated objects.
426      */
427     PropertyMap getAllocatorMap(final ScriptObject prototype) {
428         return null;
429     }
430 
431     /**
432      * This method is used to create the immutable portion of a bound function.
433      * See {@link ScriptFunction#createBound(Object, Object[])}
434      *
435      * @param fn the original function being bound
436      * @param self this reference to bind. Can be null.
437      * @param args additional arguments to bind. Can be null.
438      */
439     ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
440         final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
441         final int length = args == null ? 0 : args.length;
442         // Clear the callee and this flags
443         final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS;
444 
445         final List<CompiledFunction> boundList = new LinkedList<>();
446         final ScriptObject runtimeScope = fn.getScope();
447         final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope), null);
448         boundList.add(bind(bindTarget, fn, self, allArgs));
449 
450         return new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, boundFlags);
451     }
452 
453     /**
454      * Convert this argument for non-strict functions according to ES 10.4.3
455      *
456      * @param thiz the this argument
457      *
458      * @return the converted this object
459      */
460     private Object convertThisObject(final Object thiz) {
461         return needsWrappedThis() ? wrapThis(thiz) : thiz;
462     }
463 
464     static Object wrapThis(final Object thiz) {
465         if (!(thiz instanceof ScriptObject)) {
466             if (JSType.nullOrUndefined(thiz)) {
467                 return Context.getGlobal();
468             }
469 
470             if (isPrimitiveThis(thiz)) {
471                 return Context.getGlobal().wrapAsObject(thiz);
472             }
473         }
474 
475         return thiz;
476     }
477 
478     static boolean isPrimitiveThis(final Object obj) {
479         return JSType.isString(obj) || obj instanceof Number || obj instanceof Boolean;
480     }
481 
482     /**
483      * Creates an invoker method handle for a bound function.
484      *
485      * @param targetFn the function being bound
486      * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
487      * any of its specializations.
488      * @param self the "this" value being bound
489      * @param args additional arguments being bound
490      *
491      * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
492      * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
493      * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
494      * to the original invoker on invocation.
495      */
496     private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
497         // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
498         // in the target and will be ignored anyway.
499         final boolean isTargetBound = targetFn.isBoundFunction();
500 
501         final boolean needsCallee = needsCallee(originalInvoker);
502         assert needsCallee == needsCallee() : "callee contract violation 2";
503         assert !(isTargetBound && needsCallee); // already bound functions don't need a callee
504 
505         final Object boundSelf = isTargetBound ? null : convertThisObject(self);
506         final MethodHandle boundInvoker;
507 
508         if (isVarArg(originalInvoker)) {
509             // First, bind callee and this without arguments
510             final MethodHandle noArgBoundInvoker;
511 
512             if (isTargetBound) {
513                 // Don't bind either callee or this
514                 noArgBoundInvoker = originalInvoker;
515             } else if (needsCallee) {
516                 // Bind callee and this
517                 noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
518             } else {
519                 // Only bind this
520                 noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
521             }
522             // Now bind arguments
523             if (args.length > 0) {
524                 boundInvoker = varArgBinder(noArgBoundInvoker, args);
525             } else {
526                 boundInvoker = noArgBoundInvoker;
527             }
528         } else {
529             // If target is already bound, insert additional bound arguments after "this" argument, at position 1.
530             final int argInsertPos = isTargetBound ? 1 : 0;
531             final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : needsCallee  ? 2 : 1))];
532             int next = 0;
533             if (!isTargetBound) {
534                 if (needsCallee) {
535                     boundArgs[next++] = targetFn;
536                 }
537                 boundArgs[next++] = boundSelf;
538             }
539             // If more bound args were specified than the function can take, we'll just drop those.
540             System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
541             // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
542             // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
543             // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
544             // start at position 1. If the function is not bound, we start inserting arguments at position 0.
545             boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs);
546         }
547 
548         if (isTargetBound) {
549             return boundInvoker;
550         }
551 
552         // If the target is not already bound, add a dropArguments that'll throw away the passed this
553         return MH.dropArguments(boundInvoker, 0, Object.class);
554     }
555 
556     /**
557      * Creates a constructor method handle for a bound function using the passed constructor handle.
558      *
559      * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
560      * @param fn the function being bound
561      * @param args arguments being bound
562      *
563      * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
564      * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
565      * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
566      * this script function data object has no constructor handle, null is returned.
567      */
568     private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
569         assert originalConstructor != null;
570 
571         // If target function is already bound, don't bother binding the callee.
572         final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
573             MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
574 
575         if (args.length == 0) {
576             return calleeBoundConstructor;
577         }
578 
579         if (isVarArg(calleeBoundConstructor)) {
580             return varArgBinder(calleeBoundConstructor, args);
581         }
582 
583         final Object[] boundArgs;
584 
585         final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
586         if (args.length <= maxArgCount) {
587             boundArgs = args;
588         } else {
589             boundArgs = new Object[maxArgCount];
590             System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
591         }
592 
593         return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
594     }
595 
596     /**
597      * Takes a method handle, and returns a potentially different method handle that can be used in
598      * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
599      * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
600      * {@code Object} as well, except for the following ones:
601      * <ul>
602      *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
603      *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
604      *   (callee) as an argument.</li>
605      * </ul>
606      *
607      * @param mh the original method handle
608      *
609      * @return the new handle, conforming to the rules above.
610      */
611     private static MethodHandle makeGenericMethod(final MethodHandle mh) {
612         final MethodType type = mh.type();
613         final MethodType newType = makeGenericType(type);
614         return type.equals(newType) ? mh : mh.asType(newType);
615     }
616 
617     private static MethodType makeGenericType(final MethodType type) {
618         MethodType newType = type.generic();
619         if (isVarArg(type)) {
620             newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
621         }
622         if (needsCallee(type)) {
623             newType = newType.changeParameterType(0, ScriptFunction.class);
624         }
625         return newType;
626     }
627 
628     /**
629      * Execute this script function.
630      *
631      * @param self  Target object.
632      * @param arguments  Call arguments.
633      * @return ScriptFunction result.
634      *
635      * @throws Throwable if there is an exception/error with the invocation or thrown from it
636      */
637     Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
638         final MethodHandle mh      = getGenericInvoker(fn.getScope());
639         final Object       selfObj = convertThisObject(self);
640         final Object[]     args    = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
641 
642         DebuggerSupport.notifyInvoke(mh);
643 
644         if (isVarArg(mh)) {
645             if (needsCallee(mh)) {
646                 return mh.invokeExact(fn, selfObj, args);
647             }
648             return mh.invokeExact(selfObj, args);
649         }
650 
651         final int paramCount = mh.type().parameterCount();
652         if (needsCallee(mh)) {
653             switch (paramCount) {
654             case 2:
655                 return mh.invokeExact(fn, selfObj);
656             case 3:
657                 return mh.invokeExact(fn, selfObj, getArg(args, 0));
658             case 4:
659                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
660             case 5:
661                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
662             case 6:
663                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
664             case 7:
665                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
666             case 8:
667                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
668             default:
669                 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
670             }
671         }
672 
673         switch (paramCount) {
674         case 1:
675             return mh.invokeExact(selfObj);
676         case 2:
677             return mh.invokeExact(selfObj, getArg(args, 0));
678         case 3:
679             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
680         case 4:
681             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
682         case 5:
683             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
684         case 6:
685             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
686         case 7:
687             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
688         default:
689             return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
690         }
691     }
692 
693     Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
694         final MethodHandle mh   = getGenericConstructor(fn.getScope());
695         final Object[]     args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
696 
697         DebuggerSupport.notifyInvoke(mh);
698 
699         if (isVarArg(mh)) {
700             if (needsCallee(mh)) {
701                 return mh.invokeExact(fn, args);
702             }
703             return mh.invokeExact(args);
704         }
705 
706         final int paramCount = mh.type().parameterCount();
707         if (needsCallee(mh)) {
708             switch (paramCount) {
709             case 1:
710                 return mh.invokeExact(fn);
711             case 2:
712                 return mh.invokeExact(fn, getArg(args, 0));
713             case 3:
714                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1));
715             case 4:
716                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2));
717             case 5:
718                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
719             case 6:
720                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
721             case 7:
722                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
723             default:
724                 return mh.invokeWithArguments(withArguments(fn, paramCount, args));
725             }
726         }
727 
728         switch (paramCount) {
729         case 0:
730             return mh.invokeExact();
731         case 1:
732             return mh.invokeExact(getArg(args, 0));
733         case 2:
734             return mh.invokeExact(getArg(args, 0), getArg(args, 1));
735         case 3:
736             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2));
737         case 4:
738             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
739         case 5:
740             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
741         case 6:
742             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
743         default:
744             return mh.invokeWithArguments(withArguments(null, paramCount, args));
745         }
746     }
747 
748     private static Object getArg(final Object[] args, final int i) {
749         return i < args.length ? args[i] : UNDEFINED;
750     }
751 
752     private static Object[] withArguments(final ScriptFunction fn, final int argCount, final Object[] args) {
753         final Object[] finalArgs = new Object[argCount];
754 
755         int nextArg = 0;
756         if (fn != null) {
757             //needs callee
758             finalArgs[nextArg++] = fn;
759         }
760 
761         // Don't add more args that there is argCount in the handle (including self and callee).
762         for (int i = 0; i < args.length && nextArg < argCount;) {
763             finalArgs[nextArg++] = args[i++];
764         }
765 
766         // If we have fewer args than argCount, pad with undefined.
767         while (nextArg < argCount) {
768             finalArgs[nextArg++] = UNDEFINED;
769         }
770 
771         return finalArgs;
772     }
773 
774     private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
775         final Object[] finalArgs = new Object[argCount];
776 
777         int nextArg = 0;
778         if (fn != null) {
779             //needs callee
780             finalArgs[nextArg++] = fn;
781         }
782         finalArgs[nextArg++] = self;
783 
784         // Don't add more args that there is argCount in the handle (including self and callee).
785         for (int i = 0; i < args.length && nextArg < argCount;) {
786             finalArgs[nextArg++] = args[i++];
787         }
788 
789         // If we have fewer args than argCount, pad with undefined.
790         while (nextArg < argCount) {
791             finalArgs[nextArg++] = UNDEFINED;
792         }
793 
794         return finalArgs;
795     }
796     /**
797      * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the
798      * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on
799      * invocation
800      *
801      * @param mh the handle
802      * @param args the bound arguments
803      *
804      * @return the bound method handle
805      */
806     private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) {
807         assert args != null;
808         assert args.length > 0;
809         return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args));
810     }
811 
812     /**
813      * Heuristic to figure out if the method handle has a callee argument. If it's type is
814      * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as
815      * the constructor above is not passed this information, and can't just blindly assume it's false
816      * (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
817      * they also always receive a callee).
818      *
819      * @param mh the examined method handle
820      *
821      * @return true if the method handle expects a callee, false otherwise
822      */
823     protected static boolean needsCallee(final MethodHandle mh) {
824         return needsCallee(mh.type());
825     }
826 
827     static boolean needsCallee(final MethodType type) {
828         final int length = type.parameterCount();
829 
830         if (length == 0) {
831             return false;
832         }
833 
834         final Class<?> param0 = type.parameterType(0);
835         return param0 == ScriptFunction.class || param0 == boolean.class && length > 1 && type.parameterType(1) == ScriptFunction.class;
836     }
837 
838     /**
839      * Check if a javascript function methodhandle is a vararg handle
840      *
841      * @param mh method handle to check
842      *
843      * @return true if vararg
844      */
845     protected static boolean isVarArg(final MethodHandle mh) {
846         return isVarArg(mh.type());
847     }
848 
849     static boolean isVarArg(final MethodType type) {
850         return type.parameterType(type.parameterCount() - 1).isArray();
851     }
852 
853     /**
854      * Is this ScriptFunction declared in a dynamic context
855      * @return true if in dynamic context, false if not or irrelevant
856      */
857     public boolean inDynamicContext() {
858         return false;
859     }
860 
861     @SuppressWarnings("unused")
862     private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
863         if (array2 == null) {
864             // Must clone it, as we can't allow the receiving method to alter the array
865             return array1.clone();
866         }
867 
868         final int l2 = array2.length;
869         if (l2 == 0) {
870             return array1.clone();
871         }
872 
873         final int l1 = array1.length;
874         final Object[] concat = new Object[l1 + l2];
875         System.arraycopy(array1, 0, concat, 0, l1);
876         System.arraycopy(array2, 0, concat, l1, l2);
877 
878         return concat;
879     }
880 
881     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
882         return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
883     }
884 
885     /**
886      * This class is used to hold the generic invoker and generic constructor pair. It is structured in this way since
887      * most functions will never use them, so this way ScriptFunctionData only pays storage cost for one null reference
888      * to the GenericInvokers object, instead of two null references for the two method handles.
889      */
890     private static final class GenericInvokers {
891         volatile MethodHandle invoker;
892         volatile MethodHandle constructor;
893     }
894 
895     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
896         in.defaultReadObject();
897         code = new LinkedList<>();
898     }
899 }
900