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