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

Mon, 24 Jun 2013 19:06:01 +0530

author
sundar
date
Mon, 24 Jun 2013 19:06:01 +0530
changeset 376
51a5ee93d6bc
parent 277
92164a5742db
child 476
fbd21b00197b
permissions
-rw-r--r--

8015959: Can't call foreign constructor
Reviewed-by: jlaskey, hannesw

     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.lookup.Lookup.MH;
    29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    32 import java.lang.invoke.MethodHandle;
    33 import java.lang.invoke.MethodHandles;
    34 import java.lang.invoke.MethodType;
    35 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
    37 /**
    38  * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
    39  * Instances of this class are created during codegen and stored in script classes'
    40  * constants array to reduce function instantiation overhead during runtime.
    41  */
    42 public abstract class ScriptFunctionData {
    44     /** Name of the function or "" for anonynous functions */
    45     protected final String name;
    47     /** All versions of this function that have been generated to code */
    48     protected final CompiledFunctions code;
    50     private int arity;
    52     private final boolean isStrict;
    54     private final boolean isBuiltin;
    56     private final boolean isConstructor;
    58     private static final MethodHandle NEWFILTER     = findOwnMH("newFilter", Object.class, Object.class, Object.class);
    59     private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
    61     /**
    62      * Constructor
    63      *
    64      * @param name          script function name
    65      * @param arity         arity
    66      * @param isStrict      is the function strict
    67      * @param isBuiltin     is the function built in
    68      * @param isConstructor is the function a constructor
    69      */
    70     protected ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
    71         this.name          = name;
    72         this.arity         = arity;
    73         this.code          = new CompiledFunctions();
    74         this.isStrict      = isStrict;
    75         this.isBuiltin     = isBuiltin;
    76         this.isConstructor = isConstructor;
    77     }
    79     final int getArity() {
    80         return arity;
    81     }
    83     /**
    84      * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final
    85      * @param arity new arity
    86      */
    87     void setArity(final int arity) {
    88         this.arity = arity;
    89     }
    91     CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
    92         final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args);
    94         //TODO the boundinvoker.type() could actually be more specific here
    95         if (isConstructor()) {
    96             ensureConstructor(originalInv);
    97             return new CompiledFunction(boundInvoker.type(), boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args));
    98         }
   100         return new CompiledFunction(boundInvoker.type(), 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     final MethodHandle getGenericConstructor() {
   220         ensureCodeGenerated();
   221         ensureConstructor(code.mostGeneric());
   222         return composeGenericMethod(code.mostGeneric().getConstructor());
   223     }
   225     private CompiledFunction getBest(final MethodType callSiteType) {
   226         ensureCodeGenerated();
   227         return code.best(callSiteType);
   228     }
   230     /**
   231      * Allocates an object using this function's allocator.
   232      * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
   233      */
   234     ScriptObject allocate() {
   235         return null;
   236     }
   238     /**
   239      * This method is used to create the immutable portion of a bound function.
   240      * See {@link ScriptFunction#makeBoundFunction(Object, Object[])}
   241      *
   242      * @param fn the original function being bound
   243      * @param self this reference to bind. Can be null.
   244      * @param args additional arguments to bind. Can be null.
   245      */
   246     ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
   247         ensureCodeGenerated();
   249         final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
   250         final int length = args == null ? 0 : args.length;
   252         CompiledFunctions boundList = new CompiledFunctions();
   253         for (final CompiledFunction inv : code) {
   254             boundList.add(bind(inv, fn, self, allArgs));
   255         }
   256         ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor());
   257         return boundData;
   258     }
   260     /**
   261      * Compose a constructor given a primordial constructor handle
   262      *
   263      * @param ctor         primordial constructor handle
   264      * @param needsCallee  do we need to pass a callee
   265      *
   266      * @return the composed constructor
   267      */
   268     protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) {
   269         // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
   270         // "this" in the first argument position is what allows the elegant folded composition of
   271         // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
   272         // always returns Object.
   273         MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
   275         composedCtor = changeReturnTypeToObject(composedCtor);
   277         final MethodType ctorType = composedCtor.type();
   279         // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
   280         // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
   281         // (this, [callee, ]args...) => ([callee, ]args...)
   282         final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
   284         // Fold constructor into newFilter that replaces the return value from the constructor with the originally
   285         // allocated value when the originally allocated value is a primitive.
   286         // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
   287         composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor);
   289         // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
   290         if (needsCallee) {
   291             // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
   292             // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
   293             // or...
   294             return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE);
   295         }
   297         // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
   298         // (this, args...) filter (callee) => (callee, args...)
   299         return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE);
   300     }
   302     /**
   303      * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
   304      * method handle. If this function's method handles don't need a callee parameter, returns the original method
   305      * handle unchanged.
   306      *
   307      * @param mh a method handle with order of arguments {@code (callee, this, args...)}
   308      *
   309      * @return a method handle with order of arguments {@code (this, callee, args...)}
   310      */
   311     private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
   312         final MethodType type = mh.type();
   313         assert type.parameterType(0) == ScriptFunction.class : type;
   314         assert type.parameterType(1) == Object.class : type;
   315         final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
   316         final int[] reorder = new int[type.parameterCount()];
   317         reorder[0] = 1;
   318         assert reorder[1] == 0;
   319         for (int i = 2; i < reorder.length; ++i) {
   320             reorder[i] = i;
   321         }
   322         return MethodHandles.permuteArguments(mh, newType, reorder);
   323     }
   325     /**
   326      * Convert this argument for non-strict functions according to ES 10.4.3
   327      *
   328      * @param thiz the this argument
   329      *
   330      * @return the converted this object
   331      */
   332     private Object convertThisObject(final Object thiz) {
   333         if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
   334             if (JSType.nullOrUndefined(thiz)) {
   335                 return Context.getGlobalTrusted();
   336             }
   338             if (isPrimitiveThis(thiz)) {
   339                 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
   340             }
   341         }
   343         return thiz;
   344     }
   346     static boolean isPrimitiveThis(final Object obj) {
   347         return obj instanceof String || obj instanceof ConsString ||
   348                obj instanceof Number || obj instanceof Boolean;
   349     }
   351     /**
   352      * Creates an invoker method handle for a bound function.
   353      *
   354      * @param targetFn the function being bound
   355      * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
   356      * any of its specializations.
   357      * @param self the "this" value being bound
   358      * @param args additional arguments being bound
   359      *
   360      * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
   361      * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
   362      * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
   363      * to the original invoker on invocation.
   364      */
   365     private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
   366         // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
   367         // in the target and will be ignored anyway.
   368         final boolean isTargetBound = targetFn.isBoundFunction();
   370         final boolean needsCallee = needsCallee(originalInvoker);
   371         assert needsCallee == needsCallee() : "callee contract violation 2";
   372         assert !(isTargetBound && needsCallee); // already bound functions don't need a callee
   374         final Object boundSelf = isTargetBound ? null : convertThisObject(self);
   375         final MethodHandle boundInvoker;
   377         if (isVarArg(originalInvoker)) {
   378             // First, bind callee and this without arguments
   379             final MethodHandle noArgBoundInvoker;
   381             if (isTargetBound) {
   382                 // Don't bind either callee or this
   383                 noArgBoundInvoker = originalInvoker;
   384             } else if (needsCallee) {
   385                 // Bind callee and this
   386                 noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
   387             } else {
   388                 // Only bind this
   389                 noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
   390             }
   391             // Now bind arguments
   392             if (args.length > 0) {
   393                 boundInvoker = varArgBinder(noArgBoundInvoker, args);
   394             } else {
   395                 boundInvoker = noArgBoundInvoker;
   396             }
   397         } else {
   398             // If target is already bound, insert additional bound arguments after "this" argument, at position 1.
   399             final int argInsertPos = isTargetBound ? 1 : 0;
   400             final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : (needsCallee  ? 2 : 1)))];
   401             int next = 0;
   402             if (!isTargetBound) {
   403                 if (needsCallee) {
   404                     boundArgs[next++] = targetFn;
   405                 }
   406                 boundArgs[next++] = boundSelf;
   407             }
   408             // If more bound args were specified than the function can take, we'll just drop those.
   409             System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
   410             // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
   411             // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
   412             // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
   413             // start at position 1. If the function is not bound, we start inserting arguments at position 0.
   414             boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs);
   415         }
   417         if (isTargetBound) {
   418             return boundInvoker;
   419         }
   421         // If the target is not already bound, add a dropArguments that'll throw away the passed this
   422         return MH.dropArguments(boundInvoker, 0, Object.class);
   423     }
   425     /**
   426      * Creates a constructor method handle for a bound function using the passed constructor handle.
   427      *
   428      * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
   429      * @param fn the function being bound
   430      * @param args arguments being bound
   431      *
   432      * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
   433      * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
   434      * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
   435      * this script function data object has no constructor handle, null is returned.
   436      */
   437     private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
   438         assert originalConstructor != null;
   440         // If target function is already bound, don't bother binding the callee.
   441         final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
   442             MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
   444         if (args.length == 0) {
   445             return calleeBoundConstructor;
   446         }
   448         if (isVarArg(calleeBoundConstructor)) {
   449             return varArgBinder(calleeBoundConstructor, args);
   450         }
   452         final Object[] boundArgs;
   454         final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
   455         if (args.length <= maxArgCount) {
   456             boundArgs = args;
   457         } else {
   458             boundArgs = new Object[maxArgCount];
   459             System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
   460         }
   462         return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
   463     }
   465     /**
   466      * Takes a method handle, and returns a potentially different method handle that can be used in
   467      * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
   468      * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
   469      * {@code Object} as well, except for the following ones:
   470      * <ul>
   471      *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
   472      *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
   473      *   (callee) as an argument.</li>
   474      * </ul>
   475      *
   476      * @param mh the original method handle
   477      *
   478      * @return the new handle, conforming to the rules above.
   479      */
   480     protected MethodHandle composeGenericMethod(final MethodHandle mh) {
   481         final MethodType type = mh.type();
   482         MethodType newType = type.generic();
   483         if (isVarArg(mh)) {
   484             newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
   485         }
   486         if (needsCallee(mh)) {
   487             newType = newType.changeParameterType(0, ScriptFunction.class);
   488         }
   489         return type.equals(newType) ? mh : mh.asType(newType);
   490     }
   492     /**
   493      * Execute this script function.
   494      *
   495      * @param self  Target object.
   496      * @param arguments  Call arguments.
   497      * @return ScriptFunction result.
   498      *
   499      * @throws Throwable if there is an exception/error with the invocation or thrown from it
   500      */
   501     Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
   502         final MethodHandle mh = getGenericInvoker();
   504         final Object       selfObj    = convertThisObject(self);
   505         final Object[]     args       = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
   507         if (isVarArg(mh)) {
   508             if (needsCallee(mh)) {
   509                 return mh.invokeExact(fn, selfObj, args);
   510             }
   511             return mh.invokeExact(selfObj, args);
   512         }
   514         final int paramCount = mh.type().parameterCount();
   515         if (needsCallee(mh)) {
   516             switch (paramCount) {
   517             case 2:
   518                 return mh.invokeExact(fn, selfObj);
   519             case 3:
   520                 return mh.invokeExact(fn, selfObj, getArg(args, 0));
   521             case 4:
   522                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
   523             case 5:
   524                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   525             default:
   526                 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
   527             }
   528         }
   530         switch (paramCount) {
   531         case 1:
   532             return mh.invokeExact(selfObj);
   533         case 2:
   534             return mh.invokeExact(selfObj, getArg(args, 0));
   535         case 3:
   536             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
   537         case 4:
   538             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   539         default:
   540             return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
   541         }
   542     }
   544     Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
   545         final MethodHandle mh = getGenericConstructor();
   547         final Object[]     args       = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
   549         if (isVarArg(mh)) {
   550             if (needsCallee(mh)) {
   551                 return mh.invokeExact(fn, args);
   552             }
   553             return mh.invokeExact(args);
   554         }
   556         final int paramCount = mh.type().parameterCount();
   557         if (needsCallee(mh)) {
   558             switch (paramCount) {
   559             case 1:
   560                 return mh.invokeExact(fn);
   561             case 2:
   562                 return mh.invokeExact(fn, getArg(args, 0));
   563             case 3:
   564                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1));
   565             case 4:
   566                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   567             default:
   568                 return mh.invokeWithArguments(withArguments(fn, paramCount, args));
   569             }
   570         }
   572         switch (paramCount) {
   573         case 0:
   574             return mh.invokeExact();
   575         case 1:
   576             return mh.invokeExact(getArg(args, 0));
   577         case 2:
   578             return mh.invokeExact(getArg(args, 0), getArg(args, 1));
   579         case 3:
   580             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2));
   581         default:
   582             return mh.invokeWithArguments(withArguments(null, paramCount, args));
   583         }
   584     }
   586     private static Object getArg(final Object[] args, final int i) {
   587         return i < args.length ? args[i] : UNDEFINED;
   588     }
   590     private static Object[] withArguments(final ScriptFunction fn, final int argCount, final Object[] args) {
   591         final Object[] finalArgs = new Object[argCount];
   593         int nextArg = 0;
   594         if (fn != null) {
   595             //needs callee
   596             finalArgs[nextArg++] = fn;
   597         }
   599         // Don't add more args that there is argCount in the handle (including self and callee).
   600         for (int i = 0; i < args.length && nextArg < argCount;) {
   601             finalArgs[nextArg++] = args[i++];
   602         }
   604         // If we have fewer args than argCount, pad with undefined.
   605         while (nextArg < argCount) {
   606             finalArgs[nextArg++] = UNDEFINED;
   607         }
   609         return finalArgs;
   610     }
   612     private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
   613         final Object[] finalArgs = new Object[argCount];
   615         int nextArg = 0;
   616         if (fn != null) {
   617             //needs callee
   618             finalArgs[nextArg++] = fn;
   619         }
   620         finalArgs[nextArg++] = self;
   622         // Don't add more args that there is argCount in the handle (including self and callee).
   623         for (int i = 0; i < args.length && nextArg < argCount;) {
   624             finalArgs[nextArg++] = args[i++];
   625         }
   627         // If we have fewer args than argCount, pad with undefined.
   628         while (nextArg < argCount) {
   629             finalArgs[nextArg++] = UNDEFINED;
   630         }
   632         return finalArgs;
   633     }
   634     /**
   635      * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the
   636      * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on
   637      * invocation
   638      *
   639      * @param mh the handle
   640      * @param args the bound arguments
   641      *
   642      * @return the bound method handle
   643      */
   644     private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) {
   645         assert args != null;
   646         assert args.length > 0;
   647         return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args));
   648     }
   650     /**
   651      * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
   652      * {@code Object}, the handle is returned unchanged.
   653      *
   654      * @param mh the handle to adapt
   655      * @return the adapted handle
   656      */
   657     private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
   658         return MH.asType(mh, mh.type().changeReturnType(Object.class));
   659     }
   661     private void ensureConstructor(final CompiledFunction inv) {
   662         if (!inv.hasConstructor()) {
   663             inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker())));
   664         }
   665     }
   667     /**
   668      * Heuristic to figure out if the method handle has a callee argument. If it's type is either
   669      * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
   670      * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
   671      * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
   672      * they also always receive a callee).
   673      *
   674      * @param mh the examined method handle
   675      *
   676      * @return true if the method handle expects a callee, false otherwise
   677      */
   678     protected static boolean needsCallee(final MethodHandle mh) {
   679         final MethodType type   = mh.type();
   680         final int        length = type.parameterCount();
   682         if (length == 0) {
   683             return false;
   684         }
   686         if (type.parameterType(0) == boolean.class) {
   687             return length > 1 && type.parameterType(1) == ScriptFunction.class;
   688         }
   690         return type.parameterType(0) == ScriptFunction.class;
   691     }
   693     /**
   694      * Check if a javascript function methodhandle is a vararg handle
   695      *
   696      * @param mh method handle to check
   697      *
   698      * @return true if vararg
   699      */
   700     protected static boolean isVarArg(final MethodHandle mh) {
   701         final MethodType type = mh.type();
   702         return type.parameterType(type.parameterCount() - 1).isArray();
   703     }
   705     @SuppressWarnings("unused")
   706     private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
   707         if (array2 == null) {
   708             // Must clone it, as we can't allow the receiving method to alter the array
   709             return array1.clone();
   710         }
   712         final int l2 = array2.length;
   713         if (l2 == 0) {
   714             return array1.clone();
   715         }
   717         final int l1 = array1.length;
   718         final Object[] concat = new Object[l1 + l2];
   719         System.arraycopy(array1, 0, concat, 0, l1);
   720         System.arraycopy(array2, 0, concat, l1, l2);
   722         return concat;
   723     }
   725     @SuppressWarnings("unused")
   726     private static Object newFilter(final Object result, final Object allocation) {
   727         return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
   728     }
   730     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
   731         return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
   732     }
   733 }

mercurial