src/jdk/nashorn/internal/runtime/ScriptFunctionData.java

Tue, 12 Mar 2013 15:30:53 +0100

author
lagergren
date
Tue, 12 Mar 2013 15:30:53 +0100
changeset 137
e15806b9d716
parent 133
5759f600fcf7
child 264
fc20983ef38e
permissions
-rw-r--r--

8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change.
Reviewed-by: attila, jlaskey

     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  */
    26 package jdk.nashorn.internal.runtime;
    28 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    29 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    30 import static jdk.nashorn.internal.lookup.Lookup.MH;
    32 import java.lang.invoke.MethodHandle;
    33 import java.lang.invoke.MethodHandles;
    34 import java.lang.invoke.MethodType;
    36 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
    38 /**
    39  * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
    40  * Instances of this class are created during codegen and stored in script classes'
    41  * constants array to reduce function instantiation overhead during runtime.
    42  */
    43 public abstract class ScriptFunctionData {
    45     /** Name of the function or "" for anonynous functions */
    46     protected final String name;
    48     /** All versions of this function that have been generated to code */
    49     protected final CompiledFunctions code;
    51     private int arity;
    53     private final boolean isStrict;
    55     private final boolean isBuiltin;
    57     private final boolean isConstructor;
    59     private static final MethodHandle NEWFILTER     = findOwnMH("newFilter", Object.class, Object.class, Object.class);
    60     private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
    62     /**
    63      * Constructor
    64      *
    65      * @param name          script function name
    66      * @param arity         arity
    67      * @param isStrict      is the function strict
    68      * @param isBuiltin     is the function built in
    69      * @param isConstructor is the function a constructor
    70      */
    71     protected ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
    72         this.name          = name;
    73         this.arity         = arity;
    74         this.code          = new CompiledFunctions();
    75         this.isStrict      = isStrict;
    76         this.isBuiltin     = isBuiltin;
    77         this.isConstructor = isConstructor;
    78     }
    80     final int getArity() {
    81         return arity;
    82     }
    84     /**
    85      * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final
    86      * @param arity new arity
    87      */
    88     void setArity(final int arity) {
    89         this.arity = arity;
    90     }
    92     CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
    93         final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args);
    95         if (isConstructor()) {
    96             ensureConstructor(originalInv);
    97             return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args));
    98         }
   100         return new CompiledFunction(boundInvoker);
   101     }
   103     /**
   104      * Is this a ScriptFunction generated with strict semantics?
   105      * @return true if strict, false otherwise
   106      */
   107     public boolean isStrict() {
   108         return isStrict;
   109     }
   111     boolean isBuiltin() {
   112         return isBuiltin;
   113     }
   115     boolean isConstructor() {
   116         return isConstructor;
   117     }
   119     boolean needsCallee() {
   120         // we don't know if we need a callee or not unless we are generated
   121         ensureCodeGenerated();
   122         return code.needsCallee();
   123     }
   125     /**
   126      * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
   127      * according to ECMA 10.4.3.
   128      * @return true if this argument must be an object
   129      */
   130     boolean needsWrappedThis() {
   131         return !isStrict && !isBuiltin;
   132     }
   134     String toSource() {
   135         return "function " + (name == null ? "" : name) + "() { [native code] }";
   136     }
   138     String getName() {
   139         return name;
   140     }
   142     /**
   143      * Get this function as a String containing its source code. If no source code
   144      * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
   145      *
   146      * @return string representation of this function
   147      */
   148     @Override
   149     public String toString() {
   150         final StringBuilder sb = new StringBuilder();
   152         sb.append("name='").
   153                 append(name.isEmpty() ? "<anonymous>" : name).
   154                 append("' ").
   155                 append(code.size()).
   156                 append(" invokers=").
   157                 append(code);
   159         return sb.toString();
   160     }
   162     /**
   163      * Pick the best invoker, i.e. the one version of this method with as narrow and specific
   164      * types as possible. If the call site arguments are objects, but boxed primitives we can
   165      * also try to get a primitive version of the method and do an unboxing filter, but then
   166      * we need to insert a guard that checks the argument is really always a boxed primitive
   167      * and not suddenly a "real" object
   168      *
   169      * @param callSiteType callsite type
   170      * @param args         arguments at callsite on first trampoline invocation
   171      * @return method handle to best invoker
   172      */
   173     MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
   174         return getBest(callSiteType).getInvoker();
   175     }
   177     MethodHandle getBestInvoker(final MethodType callSiteType) {
   178         return getBestInvoker(callSiteType, null);
   179     }
   181     MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) {
   182         if (!isConstructor()) {
   183             throw typeError("not.a.constructor", toSource());
   184         }
   185         ensureCodeGenerated();
   187         final CompiledFunction best = getBest(callSiteType);
   188         ensureConstructor(best);
   189         return best.getConstructor();
   190     }
   192     MethodHandle getBestConstructor(final MethodType callSiteType) {
   193         return getBestConstructor(callSiteType, null);
   194     }
   196     /**
   197      * Subclass responsibility. If we can have lazy code generation, this is a hook to ensure that
   198      * code exists before performing an operation.
   199      */
   200     protected void ensureCodeGenerated() {
   201         //empty
   202     }
   204     /**
   205      * Return a generic Object/Object invoker for this method. It will ensure code
   206      * is generated, get the most generic of all versions of this function and adapt it
   207      * to Objects.
   208      *
   209      * TODO this is only public because {@link JavaAdapterFactory} can't supply us with
   210      * a MethodType that we can use for lookup due to boostrapping problems. Can be fixed
   211      *
   212      * @return generic invoker of this script function
   213      */
   214     public final MethodHandle getGenericInvoker() {
   215         ensureCodeGenerated();
   216         return composeGenericMethod(code.mostGeneric().getInvoker());
   217     }
   219     private CompiledFunction getBest(final MethodType callSiteType) {
   220         ensureCodeGenerated();
   221         return code.best(callSiteType);
   222     }
   224     /**
   225      * Allocates an object using this function's allocator.
   226      * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
   227      */
   228     ScriptObject allocate() {
   229         return null;
   230     }
   232     /**
   233      * This method is used to create the immutable portion of a bound function.
   234      * See {@link ScriptFunction#makeBoundFunction(Object, Object[])}
   235      *
   236      * @param fn the original function being bound
   237      * @param self this reference to bind. Can be null.
   238      * @param args additional arguments to bind. Can be null.
   239      */
   240     ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
   241         ensureCodeGenerated();
   243         final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
   244         final int length = args == null ? 0 : args.length;
   246         CompiledFunctions boundList = new CompiledFunctions();
   247         for (final CompiledFunction inv : code) {
   248             boundList.add(bind(inv, fn, self, allArgs));
   249         }
   250         ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor());
   251         return boundData;
   252     }
   254     /**
   255      * Compose a constructor given a primordial constructor handle
   256      *
   257      * @param ctor         primordial constructor handle
   258      * @param needsCallee  do we need to pass a callee
   259      *
   260      * @return the composed constructor
   261      */
   262     protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) {
   263         // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
   264         // "this" in the first argument position is what allows the elegant folded composition of
   265         // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
   266         // always returns Object.
   267         MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
   269         composedCtor = changeReturnTypeToObject(composedCtor);
   271         final MethodType ctorType = composedCtor.type();
   273         // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
   274         // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
   275         // (this, [callee, ]args...) => ([callee, ]args...)
   276         final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
   278         // Fold constructor into newFilter that replaces the return value from the constructor with the originally
   279         // allocated value when the originally allocated value is a primitive.
   280         // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
   281         composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor);
   283         // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
   284         if (needsCallee) {
   285             // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
   286             // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
   287             // or...
   288             return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE);
   289         }
   291         // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
   292         // (this, args...) filter (callee) => (callee, args...)
   293         return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE);
   294     }
   296     /**
   297      * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
   298      * method handle. If this function's method handles don't need a callee parameter, returns the original method
   299      * handle unchanged.
   300      *
   301      * @param mh a method handle with order of arguments {@code (callee, this, args...)}
   302      *
   303      * @return a method handle with order of arguments {@code (this, callee, args...)}
   304      */
   305     private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
   306         final MethodType type = mh.type();
   307         assert type.parameterType(0) == ScriptFunction.class : type;
   308         assert type.parameterType(1) == Object.class : type;
   309         final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
   310         final int[] reorder = new int[type.parameterCount()];
   311         reorder[0] = 1;
   312         assert reorder[1] == 0;
   313         for (int i = 2; i < reorder.length; ++i) {
   314             reorder[i] = i;
   315         }
   316         return MethodHandles.permuteArguments(mh, newType, reorder);
   317     }
   319     /**
   320      * Convert this argument for non-strict functions according to ES 10.4.3
   321      *
   322      * @param thiz the this argument
   323      *
   324      * @return the converted this object
   325      */
   326     private Object convertThisObject(final Object thiz) {
   327         if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
   328             if (JSType.nullOrUndefined(thiz)) {
   329                 return Context.getGlobalTrusted();
   330             }
   332             if (isPrimitiveThis(thiz)) {
   333                 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
   334             }
   335         }
   337         return thiz;
   338     }
   340     static boolean isPrimitiveThis(final Object obj) {
   341         return obj instanceof String || obj instanceof ConsString ||
   342                obj instanceof Number || obj instanceof Boolean;
   343     }
   345     /**
   346      * Creates an invoker method handle for a bound function.
   347      *
   348      * @param targetFn the function being bound
   349      * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
   350      * any of its specializations.
   351      * @param self the "this" value being bound
   352      * @param args additional arguments being bound
   353      *
   354      * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
   355      * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
   356      * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
   357      * to the original invoker on invocation.
   358      */
   359     private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
   360         // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
   361         // in the target and will be ignored anyway.
   362         final boolean isTargetBound = targetFn.isBoundFunction();
   364         final boolean needsCallee = needsCallee(originalInvoker);
   365         assert needsCallee == needsCallee() : "callee contract violation 2";
   366         assert !(isTargetBound && needsCallee); // already bound functions don't need a callee
   368         final Object boundSelf = isTargetBound ? null : convertThisObject(self);
   369         final MethodHandle boundInvoker;
   371         if (isVarArg(originalInvoker)) {
   372             // First, bind callee and this without arguments
   373             final MethodHandle noArgBoundInvoker;
   375             if (isTargetBound) {
   376                 // Don't bind either callee or this
   377                 noArgBoundInvoker = originalInvoker;
   378             } else if (needsCallee) {
   379                 // Bind callee and this
   380                 noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
   381             } else {
   382                 // Only bind this
   383                 noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
   384             }
   385             // Now bind arguments
   386             if (args.length > 0) {
   387                 boundInvoker = varArgBinder(noArgBoundInvoker, args);
   388             } else {
   389                 boundInvoker = noArgBoundInvoker;
   390             }
   391         } else {
   392             final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(), args.length + (isTargetBound ? 0 : (needsCallee  ? 2 : 1)))];
   393             int next = 0;
   394             if (!isTargetBound) {
   395                 if (needsCallee) {
   396                     boundArgs[next++] = targetFn;
   397                 }
   398                 boundArgs[next++] = boundSelf;
   399             }
   400             // If more bound args were specified than the function can take, we'll just drop those.
   401             System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
   402             // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
   403             // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
   404             // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
   405             // start at position 1. If the function is not bound, we start inserting arguments at position 0.
   406             boundInvoker = MH.insertArguments(originalInvoker, isTargetBound ? 1 : 0, boundArgs);
   407         }
   409         if (isTargetBound) {
   410             return boundInvoker;
   411         }
   413         // If the target is not already bound, add a dropArguments that'll throw away the passed this
   414         return MH.dropArguments(boundInvoker, 0, Object.class);
   415     }
   417     /**
   418      * Creates a constructor method handle for a bound function using the passed constructor handle.
   419      *
   420      * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
   421      * @param fn the function being bound
   422      * @param args arguments being bound
   423      *
   424      * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
   425      * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
   426      * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
   427      * this script function data object has no constructor handle, null is returned.
   428      */
   429     private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
   430         assert originalConstructor != null;
   432         // If target function is already bound, don't bother binding the callee.
   433         final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
   434             MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
   436         if (args.length == 0) {
   437             return calleeBoundConstructor;
   438         }
   440         if (isVarArg(calleeBoundConstructor)) {
   441             return varArgBinder(calleeBoundConstructor, args);
   442         }
   444         final Object[] boundArgs;
   446         final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
   447         if (args.length <= maxArgCount) {
   448             boundArgs = args;
   449         } else {
   450             boundArgs = new Object[maxArgCount];
   451             System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
   452         }
   454         return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
   455     }
   457     /**
   458      * Takes a method handle, and returns a potentially different method handle that can be used in
   459      * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
   460      * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
   461      * {@code Object} as well, except for the following ones:
   462      * <ul>
   463      *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
   464      *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
   465      *   (callee) as an argument.</li>
   466      * </ul>
   467      *
   468      * @param mh the original method handle
   469      *
   470      * @return the new handle, conforming to the rules above.
   471      */
   472     protected MethodHandle composeGenericMethod(final MethodHandle mh) {
   473         final MethodType type = mh.type();
   474         MethodType newType = type.generic();
   475         if (isVarArg(mh)) {
   476             newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
   477         }
   478         if (needsCallee(mh)) {
   479             newType = newType.changeParameterType(0, ScriptFunction.class);
   480         }
   481         return type.equals(newType) ? mh : mh.asType(newType);
   482     }
   484     /**
   485      * Execute this script function.
   486      *
   487      * @param self  Target object.
   488      * @param arguments  Call arguments.
   489      * @return ScriptFunction result.
   490      *
   491      * @throws Throwable if there is an exception/error with the invocation or thrown from it
   492      */
   493     Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
   494         final MethodHandle mh = getGenericInvoker();
   496         final Object       selfObj    = convertThisObject(self);
   497         final Object[]     args       = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
   499         if (isVarArg(mh)) {
   500             if (needsCallee(mh)) {
   501                 return mh.invokeExact(fn, selfObj, args);
   502             }
   503             return mh.invokeExact(selfObj, args);
   504         }
   506         final int paramCount = mh.type().parameterCount();
   507         if (needsCallee(mh)) {
   508             switch (paramCount) {
   509             case 2:
   510                 return mh.invokeExact(fn, selfObj);
   511             case 3:
   512                 return mh.invokeExact(fn, selfObj, getArg(args, 0));
   513             case 4:
   514                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
   515             case 5:
   516                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   517             default:
   518                 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
   519             }
   520         }
   522         switch (paramCount) {
   523         case 1:
   524             return mh.invokeExact(selfObj);
   525         case 2:
   526             return mh.invokeExact(selfObj, getArg(args, 0));
   527         case 3:
   528             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
   529         case 4:
   530             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   531         default:
   532             return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
   533         }
   534     }
   536     private static Object getArg(final Object[] args, final int i) {
   537         return i < args.length ? args[i] : UNDEFINED;
   538     }
   540     private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
   541         final Object[] finalArgs = new Object[argCount];
   543         int nextArg = 0;
   544         if (fn != null) {
   545             //needs callee
   546             finalArgs[nextArg++] = fn;
   547         }
   548         finalArgs[nextArg++] = self;
   550         // Don't add more args that there is argCount in the handle (including self and callee).
   551         for (int i = 0; i < args.length && nextArg < argCount;) {
   552             finalArgs[nextArg++] = args[i++];
   553         }
   555         // If we have fewer args than argCount, pad with undefined.
   556         while (nextArg < argCount) {
   557             finalArgs[nextArg++] = UNDEFINED;
   558         }
   560         return finalArgs;
   561     }
   562     /**
   563      * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the
   564      * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on
   565      * invocation
   566      *
   567      * @param mh the handle
   568      * @param args the bound arguments
   569      *
   570      * @return the bound method handle
   571      */
   572     private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) {
   573         assert args != null;
   574         assert args.length > 0;
   575         return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args));
   576     }
   578     /**
   579      * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
   580      * {@code Object}, the handle is returned unchanged.
   581      *
   582      * @param mh the handle to adapt
   583      * @return the adapted handle
   584      */
   585     private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
   586         return MH.asType(mh, mh.type().changeReturnType(Object.class));
   587     }
   589     private void ensureConstructor(final CompiledFunction inv) {
   590         if (!inv.hasConstructor()) {
   591             inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker())));
   592         }
   593     }
   595     /**
   596      * Heuristic to figure out if the method handle has a callee argument. If it's type is either
   597      * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
   598      * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
   599      * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
   600      * they also always receive a callee).
   601      *
   602      * @param mh the examined method handle
   603      *
   604      * @return true if the method handle expects a callee, false otherwise
   605      */
   606     protected static boolean needsCallee(final MethodHandle mh) {
   607         final MethodType type   = mh.type();
   608         final int        length = type.parameterCount();
   610         if (length == 0) {
   611             return false;
   612         }
   614         if (type.parameterType(0) == boolean.class) {
   615             return length > 1 && type.parameterType(1) == ScriptFunction.class;
   616         }
   618         return type.parameterType(0) == ScriptFunction.class;
   619     }
   621     /**
   622      * Check if a javascript function methodhandle is a vararg handle
   623      *
   624      * @param mh method handle to check
   625      *
   626      * @return true if vararg
   627      */
   628     protected static boolean isVarArg(final MethodHandle mh) {
   629         final MethodType type = mh.type();
   630         return type.parameterType(type.parameterCount() - 1).isArray();
   631     }
   633     @SuppressWarnings("unused")
   634     private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
   635         if (array2 == null) {
   636             // Must clone it, as we can't allow the receiving method to alter the array
   637             return array1.clone();
   638         }
   640         final int l2 = array2.length;
   641         if (l2 == 0) {
   642             return array1.clone();
   643         }
   645         final int l1 = array1.length;
   646         final Object[] concat = new Object[l1 + l2];
   647         System.arraycopy(array1, 0, concat, 0, l1);
   648         System.arraycopy(array2, 0, concat, l1, l2);
   650         return concat;
   651     }
   653     @SuppressWarnings("unused")
   654     private static Object newFilter(final Object result, final Object allocation) {
   655         return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
   656     }
   658     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
   659         return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
   660     }
   661 }

mercurial