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