1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import static jdk.nashorn.internal.lookup.Lookup.MH;
  29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  31 
  32 import java.lang.invoke.MethodHandle;
  33 import java.lang.invoke.MethodHandles;
  34 import java.lang.invoke.MethodType;
  35 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
  36 
  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 {
  43 
  44     /** Name of the function or "" for anonynous functions */
  45     protected final String name;
  46 
  47     /** All versions of this function that have been generated to code */
  48     protected final CompiledFunctions code;
  49 
  50     private int arity;
  51 
  52     private final boolean isStrict;
  53 
  54     private final boolean isBuiltin;
  55 
  56     private final boolean isConstructor;
  57 
  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);
  60 



















  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     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     }
  78 
  79     final int getArity() {
  80         return arity;
  81     }
  82 
  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     }
  90 
  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);
  93 
  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         }
  99 
 100         return new CompiledFunction(boundInvoker.type(), boundInvoker);
 101     }
 102 
 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     }
 110 
 111     boolean isBuiltin() {
 112         return isBuiltin;
 113     }
 114 
 115     boolean isConstructor() {
 116         return isConstructor;
 117     }
 118 
 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     }
 124 
 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     }
 133 
 134     String toSource() {
 135         return "function " + (name == null ? "" : name) + "() { [native code] }";
 136     }
 137 
 138     String getName() {
 139         return name;
 140     }
 141 
 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();
 151 
 152         sb.append("name='").
 153                 append(name.isEmpty() ? "<anonymous>" : name).
 154                 append("' ").
 155                 append(code.size()).
 156                 append(" invokers=").
 157                 append(code);
 158 
 159         return sb.toString();
 160     }
 161 
 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     }
 176 
 177     MethodHandle getBestInvoker(final MethodType callSiteType) {
 178         return getBestInvoker(callSiteType, null);
 179     }
 180 
 181     MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) {
 182         if (!isConstructor()) {
 183             throw typeError("not.a.constructor", toSource());
 184         }
 185         ensureCodeGenerated();
 186 
 187         final CompiledFunction best = getBest(callSiteType);
 188         ensureConstructor(best);
 189         return best.getConstructor();
 190     }
 191 
 192     MethodHandle getBestConstructor(final MethodType callSiteType) {
 193         return getBestConstructor(callSiteType, null);
 194     }
 195 
 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     }
 203 
 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 code.generic().getInvoker();
 217     }
 218 
 219     final MethodHandle getGenericConstructor() {
 220         ensureCodeGenerated();
 221         ensureConstructor(code.generic());
 222         return code.generic().getConstructor();
 223     }
 224 
 225     private CompiledFunction getBest(final MethodType callSiteType) {
 226         ensureCodeGenerated();
 227         return code.best(callSiteType);
 228     }
 229 
 230     /**
 231      * Allocates an object using this function's allocator.
 232      *
 233      * @param map the property map for the allocated object.
 234      * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
 235      */
 236     ScriptObject allocate(final PropertyMap map) {
 237         return null;
 238     }
 239 
 240     /**
 241      * Get the property map to use for objects allocated by this function.
 242      *
 243      * @return the property map for allocated objects.
 244      */
 245     PropertyMap getAllocatorMap() {
 246         return null;
 247     }
 248 
 249     /**
 250      * This method is used to create the immutable portion of a bound function.
 251      * See {@link ScriptFunction#makeBoundFunction(Object, Object[])}
 252      *
 253      * @param fn the original function being bound
 254      * @param self this reference to bind. Can be null.
 255      * @param args additional arguments to bind. Can be null.
 256      */
 257     ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
 258         ensureCodeGenerated();
 259 
 260         final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
 261         final int length = args == null ? 0 : args.length;


 262 
 263         CompiledFunctions boundList = new CompiledFunctions();
 264         if (code.size() == 1) {
 265             // only one variant - bind that
 266             boundList.add(bind(code.first(), fn, self, allArgs));
 267         } else {
 268             // There are specialized versions. Get the most generic one.
 269             // This is to avoid ambiguous overloaded versions of bound and
 270             // specialized variants and choosing wrong overload.
 271             final MethodHandle genInvoker = getGenericInvoker();
 272             final CompiledFunction inv = new CompiledFunction(genInvoker.type(), genInvoker, getGenericConstructor());
 273             boundList.add(bind(inv, fn, self, allArgs));
 274         }
 275 
 276         ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor());
 277         return boundData;
 278     }
 279 
 280     /**
 281      * Compose a constructor given a primordial constructor handle.
 282      *
 283      * @param ctor primordial constructor handle
 284      * @return the composed constructor
 285      */
 286     protected MethodHandle composeConstructor(final MethodHandle ctor) {
 287         // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
 288         // "this" in the first argument position is what allows the elegant folded composition of
 289         // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
 290         // always returns Object.
 291         final boolean needsCallee = needsCallee(ctor);
 292         MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
 293 
 294         composedCtor = changeReturnTypeToObject(composedCtor);
 295 
 296         final MethodType ctorType = composedCtor.type();
 297 
 298         // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
 299         // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
 300         // (this, [callee, ]args...) => ([callee, ]args...)
 301         final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
 302 
 303         // Fold constructor into newFilter that replaces the return value from the constructor with the originally
 304         // allocated value when the originally allocated value is a primitive.
 305         // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
 306         composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor);
 307 
 308         // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
 309         if (needsCallee) {
 310             // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
 311             // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
 312             // or...
 313             return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE);
 314         }
 315 
 316         // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
 317         // (this, args...) filter (callee) => (callee, args...)
 318         return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE);
 319     }
 320 
 321     /**
 322      * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
 323      * method handle. If this function's method handles don't need a callee parameter, returns the original method
 324      * handle unchanged.
 325      *
 326      * @param mh a method handle with order of arguments {@code (callee, this, args...)}
 327      *
 328      * @return a method handle with order of arguments {@code (this, callee, args...)}
 329      */
 330     private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
 331         final MethodType type = mh.type();
 332         assert type.parameterType(0) == ScriptFunction.class : type;
 333         assert type.parameterType(1) == Object.class : type;
 334         final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
 335         final int[] reorder = new int[type.parameterCount()];
 336         reorder[0] = 1;
 337         assert reorder[1] == 0;
 338         for (int i = 2; i < reorder.length; ++i) {
 339             reorder[i] = i;
 340         }
 341         return MethodHandles.permuteArguments(mh, newType, reorder);
 342     }
 343 
 344     /**
 345      * Convert this argument for non-strict functions according to ES 10.4.3
 346      *
 347      * @param thiz the this argument
 348      *
 349      * @return the converted this object
 350      */
 351     private Object convertThisObject(final Object thiz) {
 352         if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
 353             if (JSType.nullOrUndefined(thiz)) {
 354                 return Context.getGlobalTrusted();
 355             }
 356 
 357             if (isPrimitiveThis(thiz)) {
 358                 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
 359             }
 360         }
 361 
 362         return thiz;
 363     }
 364 
 365     static boolean isPrimitiveThis(final Object obj) {
 366         return obj instanceof String || obj instanceof ConsString ||
 367                obj instanceof Number || obj instanceof Boolean;
 368     }
 369 
 370     /**
 371      * Creates an invoker method handle for a bound function.
 372      *
 373      * @param targetFn the function being bound
 374      * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
 375      * any of its specializations.
 376      * @param self the "this" value being bound
 377      * @param args additional arguments being bound
 378      *
 379      * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
 380      * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
 381      * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
 382      * to the original invoker on invocation.
 383      */
 384     private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
 385         // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
 386         // in the target and will be ignored anyway.
 387         final boolean isTargetBound = targetFn.isBoundFunction();
 388 
 389         final boolean needsCallee = needsCallee(originalInvoker);
 390         assert needsCallee == needsCallee() : "callee contract violation 2";
 391         assert !(isTargetBound && needsCallee); // already bound functions don't need a callee
 392 
 393         final Object boundSelf = isTargetBound ? null : convertThisObject(self);
 394         final MethodHandle boundInvoker;
 395 
 396         if (isVarArg(originalInvoker)) {
 397             // First, bind callee and this without arguments
 398             final MethodHandle noArgBoundInvoker;
 399 
 400             if (isTargetBound) {
 401                 // Don't bind either callee or this
 402                 noArgBoundInvoker = originalInvoker;
 403             } else if (needsCallee) {
 404                 // Bind callee and this
 405                 noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
 406             } else {
 407                 // Only bind this
 408                 noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
 409             }
 410             // Now bind arguments
 411             if (args.length > 0) {
 412                 boundInvoker = varArgBinder(noArgBoundInvoker, args);
 413             } else {
 414                 boundInvoker = noArgBoundInvoker;
 415             }
 416         } else {
 417             // If target is already bound, insert additional bound arguments after "this" argument, at position 1.
 418             final int argInsertPos = isTargetBound ? 1 : 0;
 419             final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : (needsCallee  ? 2 : 1)))];
 420             int next = 0;
 421             if (!isTargetBound) {
 422                 if (needsCallee) {
 423                     boundArgs[next++] = targetFn;
 424                 }
 425                 boundArgs[next++] = boundSelf;
 426             }
 427             // If more bound args were specified than the function can take, we'll just drop those.
 428             System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
 429             // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
 430             // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
 431             // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
 432             // start at position 1. If the function is not bound, we start inserting arguments at position 0.
 433             boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs);
 434         }
 435 
 436         if (isTargetBound) {
 437             return boundInvoker;
 438         }
 439 
 440         // If the target is not already bound, add a dropArguments that'll throw away the passed this
 441         return MH.dropArguments(boundInvoker, 0, Object.class);
 442     }
 443 
 444     /**
 445      * Creates a constructor method handle for a bound function using the passed constructor handle.
 446      *
 447      * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
 448      * @param fn the function being bound
 449      * @param args arguments being bound
 450      *
 451      * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
 452      * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
 453      * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
 454      * this script function data object has no constructor handle, null is returned.
 455      */
 456     private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
 457         assert originalConstructor != null;
 458 
 459         // If target function is already bound, don't bother binding the callee.
 460         final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
 461             MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
 462 
 463         if (args.length == 0) {
 464             return calleeBoundConstructor;
 465         }
 466 
 467         if (isVarArg(calleeBoundConstructor)) {
 468             return varArgBinder(calleeBoundConstructor, args);
 469         }
 470 
 471         final Object[] boundArgs;
 472 
 473         final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
 474         if (args.length <= maxArgCount) {
 475             boundArgs = args;
 476         } else {
 477             boundArgs = new Object[maxArgCount];
 478             System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
 479         }
 480 
 481         return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
 482     }
 483 
 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();
 495         final Object   selfObj = convertThisObject(self);
 496         final Object[] args    = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 497 
 498         if (isVarArg(mh)) {
 499             if (needsCallee(mh)) {
 500                 return mh.invokeExact(fn, selfObj, args);
 501             }
 502             return mh.invokeExact(selfObj, args);
 503         }
 504 
 505         final int paramCount = mh.type().parameterCount();
 506         if (needsCallee(mh)) {
 507             switch (paramCount) {
 508             case 2:
 509                 return mh.invokeExact(fn, selfObj);
 510             case 3:
 511                 return mh.invokeExact(fn, selfObj, getArg(args, 0));
 512             case 4:
 513                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
 514             case 5:
 515                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 516             case 6:
 517                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
 518             case 7:
 519                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
 520             case 8:
 521                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
 522             default:
 523                 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
 524             }
 525         }
 526 
 527         switch (paramCount) {
 528         case 1:
 529             return mh.invokeExact(selfObj);
 530         case 2:
 531             return mh.invokeExact(selfObj, getArg(args, 0));
 532         case 3:
 533             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
 534         case 4:
 535             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 536         case 5:
 537             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
 538         case 6:
 539             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
 540         case 7:
 541             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
 542         default:
 543             return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
 544         }
 545     }
 546 
 547     Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
 548         final MethodHandle mh   = getGenericConstructor();
 549         final Object[]     args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 550 
 551         if (isVarArg(mh)) {
 552             if (needsCallee(mh)) {
 553                 return mh.invokeExact(fn, args);
 554             }
 555             return mh.invokeExact(args);
 556         }
 557 
 558         final int paramCount = mh.type().parameterCount();
 559         if (needsCallee(mh)) {
 560             switch (paramCount) {
 561             case 1:
 562                 return mh.invokeExact(fn);
 563             case 2:
 564                 return mh.invokeExact(fn, getArg(args, 0));
 565             case 3:
 566                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1));
 567             case 4:
 568                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 569             case 5:
 570                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
 571             case 6:
 572                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
 573             case 7:
 574                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
 575             default:
 576                 return mh.invokeWithArguments(withArguments(fn, paramCount, args));
 577             }
 578         }
 579 
 580         switch (paramCount) {
 581         case 0:
 582             return mh.invokeExact();
 583         case 1:
 584             return mh.invokeExact(getArg(args, 0));
 585         case 2:
 586             return mh.invokeExact(getArg(args, 0), getArg(args, 1));
 587         case 3:
 588             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2));
 589         case 4:
 590             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
 591         case 5:
 592             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
 593         case 6:
 594             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
 595         default:
 596             return mh.invokeWithArguments(withArguments(null, paramCount, args));
 597         }
 598     }
 599 
 600     private static Object getArg(final Object[] args, final int i) {
 601         return i < args.length ? args[i] : UNDEFINED;
 602     }
 603 
 604     private static Object[] withArguments(final ScriptFunction fn, final int argCount, final Object[] args) {
 605         final Object[] finalArgs = new Object[argCount];
 606 
 607         int nextArg = 0;
 608         if (fn != null) {
 609             //needs callee
 610             finalArgs[nextArg++] = fn;
 611         }
 612 
 613         // Don't add more args that there is argCount in the handle (including self and callee).
 614         for (int i = 0; i < args.length && nextArg < argCount;) {
 615             finalArgs[nextArg++] = args[i++];
 616         }
 617 
 618         // If we have fewer args than argCount, pad with undefined.
 619         while (nextArg < argCount) {
 620             finalArgs[nextArg++] = UNDEFINED;
 621         }
 622 
 623         return finalArgs;
 624     }
 625 
 626     private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
 627         final Object[] finalArgs = new Object[argCount];
 628 
 629         int nextArg = 0;
 630         if (fn != null) {
 631             //needs callee
 632             finalArgs[nextArg++] = fn;
 633         }
 634         finalArgs[nextArg++] = self;
 635 
 636         // Don't add more args that there is argCount in the handle (including self and callee).
 637         for (int i = 0; i < args.length && nextArg < argCount;) {
 638             finalArgs[nextArg++] = args[i++];
 639         }
 640 
 641         // If we have fewer args than argCount, pad with undefined.
 642         while (nextArg < argCount) {
 643             finalArgs[nextArg++] = UNDEFINED;
 644         }
 645 
 646         return finalArgs;
 647     }
 648     /**
 649      * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the
 650      * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on
 651      * invocation
 652      *
 653      * @param mh the handle
 654      * @param args the bound arguments
 655      *
 656      * @return the bound method handle
 657      */
 658     private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) {
 659         assert args != null;
 660         assert args.length > 0;
 661         return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args));
 662     }
 663 
 664     /**
 665      * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
 666      * {@code Object}, the handle is returned unchanged.
 667      *
 668      * @param mh the handle to adapt
 669      * @return the adapted handle
 670      */
 671     private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
 672         final MethodType type = mh.type();
 673         return (type.returnType() == Object.class) ? mh : MH.asType(mh, type.changeReturnType(Object.class));
 674     }
 675 
 676     private void ensureConstructor(final CompiledFunction inv) {
 677         if (!inv.hasConstructor()) {
 678             inv.setConstructor(composeConstructor(inv.getInvoker()));
 679         }
 680     }
 681 
 682     /**
 683      * Heuristic to figure out if the method handle has a callee argument. If it's type is
 684      * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as
 685      * the constructor above is not passed this information, and can't just blindly assume it's false
 686      * (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
 687      * they also always receive a callee).
 688      *
 689      * @param mh the examined method handle
 690      *
 691      * @return true if the method handle expects a callee, false otherwise
 692      */
 693     protected static boolean needsCallee(final MethodHandle mh) {
 694         final MethodType type = mh.type();
 695         return (type.parameterCount() > 0 && type.parameterType(0) == ScriptFunction.class);
 696     }
 697 
 698     /**
 699      * Check if a javascript function methodhandle is a vararg handle
 700      *
 701      * @param mh method handle to check
 702      *
 703      * @return true if vararg
 704      */
 705     protected static boolean isVarArg(final MethodHandle mh) {
 706         final MethodType type = mh.type();
 707         return type.parameterType(type.parameterCount() - 1).isArray();
 708     }
 709 
 710     @SuppressWarnings("unused")
 711     private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
 712         if (array2 == null) {
 713             // Must clone it, as we can't allow the receiving method to alter the array
 714             return array1.clone();
 715         }
 716 
 717         final int l2 = array2.length;
 718         if (l2 == 0) {
 719             return array1.clone();
 720         }
 721 
 722         final int l1 = array1.length;
 723         final Object[] concat = new Object[l1 + l2];
 724         System.arraycopy(array1, 0, concat, 0, l1);
 725         System.arraycopy(array2, 0, concat, l1, l2);
 726 
 727         return concat;
 728     }
 729 
 730     @SuppressWarnings("unused")
 731     private static Object newFilter(final Object result, final Object allocation) {
 732         return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
 733     }
 734 
 735     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
 736         return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
 737     }
 738 }
--- EOF ---