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.objects.Global; 36 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; 37 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 { 44 45 /** Name of the function or "" for anonynous functions */ 46 protected final String name; 47 48 /** All versions of this function that have been generated to code */ 49 protected final CompiledFunctions code; 50 51 /** Function flags */ 52 protected int flags; 53 54 private int arity; 55 56 private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); 57 private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class); 58 59 /** Is this a strict mode function? */ 60 public static final int IS_STRICT = 1 << 0; 61 /** Is this a built-in function? */ 62 public static final int IS_BUILTIN = 1 << 1; 63 /** Is this a constructor function? */ 64 public static final int IS_CONSTRUCTOR = 1 << 2; 65 /** Does this function expect a callee argument? */ 66 public static final int NEEDS_CALLEE = 1 << 3; 67 /** Does this function make use of the this-object argument? */ 68 public static final int USES_THIS = 1 << 4; 69 70 /** Flag for strict or built-in functions */ 71 public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN; 72 /** Flag for built-in constructors */ 73 public static final int IS_BUILTIN_CONSTRUCTOR = IS_BUILTIN | IS_CONSTRUCTOR; 74 /** Flag for strict constructors */ 75 public static final int IS_STRICT_CONSTRUCTOR = IS_STRICT | IS_CONSTRUCTOR; 76 77 /** 78 * Constructor 79 * 80 * @param name script function name 81 * @param arity arity 82 * @param flags the function flags 83 */ 84 ScriptFunctionData(final String name, final int arity, final int flags) { 85 this.name = name; 86 this.arity = arity; 87 this.code = new CompiledFunctions(); 88 this.flags = flags; 89 } 90 91 final int getArity() { 92 return arity; 93 } 94 95 /** 96 * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final 97 * @param arity new arity 98 */ 99 void setArity(final int arity) { 100 this.arity = arity; 101 } 102 103 CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) { 104 final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args); 105 106 //TODO the boundinvoker.type() could actually be more specific here 107 if (isConstructor()) { 108 ensureConstructor(originalInv); 109 return new CompiledFunction(boundInvoker.type(), boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args)); 110 } 111 112 return new CompiledFunction(boundInvoker.type(), boundInvoker); 113 } 114 115 /** 116 * Is this a ScriptFunction generated with strict semantics? 117 * @return true if strict, false otherwise 118 */ 119 public boolean isStrict() { 120 return (flags & IS_STRICT) != 0; 121 } 122 123 boolean isBuiltin() { 124 return (flags & IS_BUILTIN) != 0; 125 } 126 127 boolean isConstructor() { 128 return (flags & IS_CONSTRUCTOR) != 0; 129 } 130 131 boolean needsCallee() { 132 // we don't know if we need a callee or not unless code has been compiled 133 ensureCompiled(); 134 return (flags & NEEDS_CALLEE) != 0; 135 } 136 137 /** 138 * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument 139 * according to ECMA 10.4.3. 140 * @return true if this argument must be an object 141 */ 142 boolean needsWrappedThis() { 143 return (flags & USES_THIS) != 0 && (flags & IS_STRICT_OR_BUILTIN) == 0; 144 } 145 146 String toSource() { 147 return "function " + (name == null ? "" : name) + "() { [native code] }"; 148 } 149 150 String getName() { 151 return name; 152 } 153 154 /** 155 * Get this function as a String containing its source code. If no source code 156 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} 157 * 158 * @return string representation of this function 159 */ 160 @Override 161 public String toString() { 162 final StringBuilder sb = new StringBuilder(); 163 164 sb.append("name='"). 165 append(name.isEmpty() ? "<anonymous>" : name). 166 append("' "). 167 append(code.size()). 168 append(" invokers="). 169 append(code); 170 171 return sb.toString(); 172 } 173 174 /** 175 * Pick the best invoker, i.e. the one version of this method with as narrow and specific 176 * types as possible. If the call site arguments are objects, but boxed primitives we can 177 * also try to get a primitive version of the method and do an unboxing filter, but then 178 * we need to insert a guard that checks the argument is really always a boxed primitive 179 * and not suddenly a "real" object 180 * 181 * @param callSiteType callsite type 182 * @param args arguments at callsite on first trampoline invocation 183 * @return method handle to best invoker 184 */ 185 MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { 186 return getBest(callSiteType).getInvoker(); 187 } 188 189 MethodHandle getBestInvoker(final MethodType callSiteType) { 190 return getBestInvoker(callSiteType, null); 191 } 192 193 MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) { 194 if (!isConstructor()) { 195 throw typeError("not.a.constructor", toSource()); 196 } 197 ensureCodeGenerated(); 198 199 final CompiledFunction best = getBest(callSiteType); 200 ensureConstructor(best); 201 return best.getConstructor(); 202 } 203 204 MethodHandle getBestConstructor(final MethodType callSiteType) { 205 return getBestConstructor(callSiteType, null); 206 } 207 208 /** 209 * Subclass responsibility. If we can have lazy code generation, this is a hook to ensure that 210 * code exists before performing an operation. 211 */ 212 protected void ensureCodeGenerated() { 213 //empty 214 } 215 216 /** 217 * If we can have lazy code generation, this is a hook to ensure that the code has been compiled. 218 * This does not guarantee the code been installed in this {@code ScriptFunctionData} instance; 219 * use {@link #ensureCodeGenerated()} to install the actual method handles. 220 */ 221 protected void ensureCompiled() { 222 //empty 223 } 224 225 /** 226 * Return a generic Object/Object invoker for this method. It will ensure code 227 * is generated, get the most generic of all versions of this function and adapt it 228 * to Objects. 229 * 230 * TODO this is only public because {@link JavaAdapterFactory} can't supply us with 231 * a MethodType that we can use for lookup due to boostrapping problems. Can be fixed 232 * 233 * @return generic invoker of this script function 234 */ 235 public final MethodHandle getGenericInvoker() { 236 ensureCodeGenerated(); 237 return code.generic().getInvoker(); 238 } 239 240 final MethodHandle getGenericConstructor() { 241 ensureCodeGenerated(); 242 ensureConstructor(code.generic()); 243 return code.generic().getConstructor(); 244 } 245 246 private CompiledFunction getBest(final MethodType callSiteType) { 247 ensureCodeGenerated(); 248 return code.best(callSiteType); 249 } 250 251 /** 252 * Allocates an object using this function's allocator. 253 * 254 * @param map the property map for the allocated object. 255 * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator. 256 */ 257 ScriptObject allocate(final PropertyMap map) { 258 return null; 259 } 260 261 /** 262 * Get the property map to use for objects allocated by this function. 263 * 264 * @return the property map for allocated objects. 265 */ 266 PropertyMap getAllocatorMap() { 267 return null; 268 } 269 270 /** 271 * This method is used to create the immutable portion of a bound function. 272 * See {@link ScriptFunction#makeBoundFunction(Object, Object[])} 273 * 274 * @param fn the original function being bound 275 * @param self this reference to bind. Can be null. 276 * @param args additional arguments to bind. Can be null. 277 */ 278 ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) { 279 ensureCodeGenerated(); 280 281 final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args; 282 final int length = args == null ? 0 : args.length; 283 // Clear the callee and this flags 284 final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS; 285 286 CompiledFunctions boundList = new CompiledFunctions(); 287 if (code.size() == 1) { 288 // only one variant - bind that 289 boundList.add(bind(code.first(), fn, self, allArgs)); 290 } else { 291 // There are specialized versions. Get the most generic one. 292 // This is to avoid ambiguous overloaded versions of bound and 293 // specialized variants and choosing wrong overload. 294 final MethodHandle genInvoker = getGenericInvoker(); 295 final CompiledFunction inv = new CompiledFunction(genInvoker.type(), genInvoker, getGenericConstructor()); 296 boundList.add(bind(inv, fn, self, allArgs)); 297 } 298 299 return new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, boundFlags); 300 } 301 302 /** 303 * Compose a constructor given a primordial constructor handle. 304 * 305 * @param ctor primordial constructor handle 306 * @return the composed constructor 307 */ 308 protected MethodHandle composeConstructor(final MethodHandle ctor) { 309 // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having 310 // "this" in the first argument position is what allows the elegant folded composition of 311 // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor 312 // always returns Object. 313 final boolean needsCallee = needsCallee(ctor); 314 MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor; 315 316 composedCtor = changeReturnTypeToObject(composedCtor); 317 318 final MethodType ctorType = composedCtor.type(); 319 320 // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually 321 // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it. 322 // (this, [callee, ]args...) => ([callee, ]args...) 323 final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); 324 325 // Fold constructor into newFilter that replaces the return value from the constructor with the originally 326 // allocated value when the originally allocated value is a primitive. 327 // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...) 328 composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor); 329 330 // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject... 331 if (needsCallee) { 332 // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and 333 // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...), 334 // or... 335 return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE); 336 } 337 338 // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee 339 // (this, args...) filter (callee) => (callee, args...) 340 return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE); 341 } 342 343 /** 344 * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed 345 * method handle. If this function's method handles don't need a callee parameter, returns the original method 346 * handle unchanged. 347 * 348 * @param mh a method handle with order of arguments {@code (callee, this, args...)} 349 * 350 * @return a method handle with order of arguments {@code (this, callee, args...)} 351 */ 352 private static MethodHandle swapCalleeAndThis(final MethodHandle mh) { 353 final MethodType type = mh.type(); 354 assert type.parameterType(0) == ScriptFunction.class : type; 355 assert type.parameterType(1) == Object.class : type; 356 final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class); 357 final int[] reorder = new int[type.parameterCount()]; 358 reorder[0] = 1; 359 assert reorder[1] == 0; 360 for (int i = 2; i < reorder.length; ++i) { 361 reorder[i] = i; 362 } 363 return MethodHandles.permuteArguments(mh, newType, reorder); 364 } 365 366 /** 367 * Convert this argument for non-strict functions according to ES 10.4.3 368 * 369 * @param thiz the this argument 370 * 371 * @return the converted this object 372 */ 373 private Object convertThisObject(final Object thiz) { 374 if (!(thiz instanceof ScriptObject) && needsWrappedThis()) { 375 if (JSType.nullOrUndefined(thiz)) { 376 return Context.getGlobal(); 377 } 378 379 if (isPrimitiveThis(thiz)) { 380 return Context.getGlobal().wrapAsObject(thiz); 381 } 382 } 383 384 return thiz; 385 } 386 387 static boolean isPrimitiveThis(final Object obj) { 388 return obj instanceof String || obj instanceof ConsString || 389 obj instanceof Number || obj instanceof Boolean; 390 } 391 392 /** 393 * Creates an invoker method handle for a bound function. 394 * 395 * @param targetFn the function being bound 396 * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or 397 * any of its specializations. 398 * @param self the "this" value being bound 399 * @param args additional arguments being bound 400 * 401 * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting 402 * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting 403 * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed 404 * to the original invoker on invocation. 405 */ 406 private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) { 407 // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound 408 // in the target and will be ignored anyway. 409 final boolean isTargetBound = targetFn.isBoundFunction(); 410 411 final boolean needsCallee = needsCallee(originalInvoker); 412 assert needsCallee == needsCallee() : "callee contract violation 2"; 413 assert !(isTargetBound && needsCallee); // already bound functions don't need a callee 414 415 final Object boundSelf = isTargetBound ? null : convertThisObject(self); 416 final MethodHandle boundInvoker; 417 418 if (isVarArg(originalInvoker)) { 419 // First, bind callee and this without arguments 420 final MethodHandle noArgBoundInvoker; 421 422 if (isTargetBound) { 423 // Don't bind either callee or this 424 noArgBoundInvoker = originalInvoker; 425 } else if (needsCallee) { 426 // Bind callee and this 427 noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf); 428 } else { 429 // Only bind this 430 noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf); 431 } 432 // Now bind arguments 433 if (args.length > 0) { 434 boundInvoker = varArgBinder(noArgBoundInvoker, args); 435 } else { 436 boundInvoker = noArgBoundInvoker; 437 } 438 } else { 439 // If target is already bound, insert additional bound arguments after "this" argument, at position 1. 440 final int argInsertPos = isTargetBound ? 1 : 0; 441 final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))]; 442 int next = 0; 443 if (!isTargetBound) { 444 if (needsCallee) { 445 boundArgs[next++] = targetFn; 446 } 447 boundArgs[next++] = boundSelf; 448 } 449 // If more bound args were specified than the function can take, we'll just drop those. 450 System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next); 451 // If target is already bound, insert additional bound arguments after "this" argument, at position 1; 452 // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions 453 // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args 454 // start at position 1. If the function is not bound, we start inserting arguments at position 0. 455 boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs); 456 } 457 458 if (isTargetBound) { 459 return boundInvoker; 460 } 461 462 // If the target is not already bound, add a dropArguments that'll throw away the passed this 463 return MH.dropArguments(boundInvoker, 0, Object.class); 464 } 465 466 /** 467 * Creates a constructor method handle for a bound function using the passed constructor handle. 468 * 469 * @param originalConstructor the constructor handle to bind. It must be a composed constructor. 470 * @param fn the function being bound 471 * @param args arguments being bound 472 * 473 * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never 474 * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor 475 * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if 476 * this script function data object has no constructor handle, null is returned. 477 */ 478 private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) { 479 assert originalConstructor != null; 480 481 // If target function is already bound, don't bother binding the callee. 482 final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor : 483 MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class); 484 485 if (args.length == 0) { 486 return calleeBoundConstructor; 487 } 488 489 if (isVarArg(calleeBoundConstructor)) { 490 return varArgBinder(calleeBoundConstructor, args); 491 } 492 493 final Object[] boundArgs; 494 495 final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1; 496 if (args.length <= maxArgCount) { 497 boundArgs = args; 498 } else { 499 boundArgs = new Object[maxArgCount]; 500 System.arraycopy(args, 0, boundArgs, 0, maxArgCount); 501 } 502 503 return MH.insertArguments(calleeBoundConstructor, 1, boundArgs); 504 } 505 506 /** 507 * Execute this script function. 508 * 509 * @param self Target object. 510 * @param arguments Call arguments. 511 * @return ScriptFunction result. 512 * 513 * @throws Throwable if there is an exception/error with the invocation or thrown from it 514 */ 515 Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable { 516 final MethodHandle mh = getGenericInvoker(); 517 final Object selfObj = convertThisObject(self); 518 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 519 520 if (isVarArg(mh)) { 521 if (needsCallee(mh)) { 522 return mh.invokeExact(fn, selfObj, args); 523 } 524 return mh.invokeExact(selfObj, args); 525 } 526 527 final int paramCount = mh.type().parameterCount(); 528 if (needsCallee(mh)) { 529 switch (paramCount) { 530 case 2: 531 return mh.invokeExact(fn, selfObj); 532 case 3: 533 return mh.invokeExact(fn, selfObj, getArg(args, 0)); 534 case 4: 535 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1)); 536 case 5: 537 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 538 case 6: 539 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 540 case 7: 541 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 542 case 8: 543 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 544 default: 545 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args)); 546 } 547 } 548 549 switch (paramCount) { 550 case 1: 551 return mh.invokeExact(selfObj); 552 case 2: 553 return mh.invokeExact(selfObj, getArg(args, 0)); 554 case 3: 555 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); 556 case 4: 557 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 558 case 5: 559 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 560 case 6: 561 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 562 case 7: 563 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 564 default: 565 return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args)); 566 } 567 } 568 569 Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable { 570 final MethodHandle mh = getGenericConstructor(); 571 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 572 573 if (isVarArg(mh)) { 574 if (needsCallee(mh)) { 575 return mh.invokeExact(fn, args); 576 } 577 return mh.invokeExact(args); 578 } 579 580 final int paramCount = mh.type().parameterCount(); 581 if (needsCallee(mh)) { 582 switch (paramCount) { 583 case 1: 584 return mh.invokeExact(fn); 585 case 2: 586 return mh.invokeExact(fn, getArg(args, 0)); 587 case 3: 588 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1)); 589 case 4: 590 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 591 case 5: 592 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 593 case 6: 594 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 595 case 7: 596 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 597 default: 598 return mh.invokeWithArguments(withArguments(fn, paramCount, args)); 599 } 600 } 601 602 switch (paramCount) { 603 case 0: 604 return mh.invokeExact(); 605 case 1: 606 return mh.invokeExact(getArg(args, 0)); 607 case 2: 608 return mh.invokeExact(getArg(args, 0), getArg(args, 1)); 609 case 3: 610 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2)); 611 case 4: 612 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 613 case 5: 614 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 615 case 6: 616 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 617 default: 618 return mh.invokeWithArguments(withArguments(null, paramCount, args)); 619 } 620 } 621 622 private static Object getArg(final Object[] args, final int i) { 623 return i < args.length ? args[i] : UNDEFINED; 624 } 625 626 private static Object[] withArguments(final ScriptFunction fn, 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 635 // Don't add more args that there is argCount in the handle (including self and callee). 636 for (int i = 0; i < args.length && nextArg < argCount;) { 637 finalArgs[nextArg++] = args[i++]; 638 } 639 640 // If we have fewer args than argCount, pad with undefined. 641 while (nextArg < argCount) { 642 finalArgs[nextArg++] = UNDEFINED; 643 } 644 645 return finalArgs; 646 } 647 648 private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) { 649 final Object[] finalArgs = new Object[argCount]; 650 651 int nextArg = 0; 652 if (fn != null) { 653 //needs callee 654 finalArgs[nextArg++] = fn; 655 } 656 finalArgs[nextArg++] = self; 657 658 // Don't add more args that there is argCount in the handle (including self and callee). 659 for (int i = 0; i < args.length && nextArg < argCount;) { 660 finalArgs[nextArg++] = args[i++]; 661 } 662 663 // If we have fewer args than argCount, pad with undefined. 664 while (nextArg < argCount) { 665 finalArgs[nextArg++] = UNDEFINED; 666 } 667 668 return finalArgs; 669 } 670 /** 671 * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the 672 * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on 673 * invocation 674 * 675 * @param mh the handle 676 * @param args the bound arguments 677 * 678 * @return the bound method handle 679 */ 680 private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) { 681 assert args != null; 682 assert args.length > 0; 683 return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args)); 684 } 685 686 /** 687 * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already 688 * {@code Object}, the handle is returned unchanged. 689 * 690 * @param mh the handle to adapt 691 * @return the adapted handle 692 */ 693 private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) { 694 final MethodType type = mh.type(); 695 return (type.returnType() == Object.class) ? mh : MH.asType(mh, type.changeReturnType(Object.class)); 696 } 697 698 private void ensureConstructor(final CompiledFunction inv) { 699 if (!inv.hasConstructor()) { 700 inv.setConstructor(composeConstructor(inv.getInvoker())); 701 } 702 } 703 704 /** 705 * Heuristic to figure out if the method handle has a callee argument. If it's type is 706 * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as 707 * the constructor above is not passed this information, and can't just blindly assume it's false 708 * (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore 709 * they also always receive a callee). 710 * 711 * @param mh the examined method handle 712 * 713 * @return true if the method handle expects a callee, false otherwise 714 */ 715 protected static boolean needsCallee(final MethodHandle mh) { 716 final MethodType type = mh.type(); 717 return (type.parameterCount() > 0 && type.parameterType(0) == ScriptFunction.class); 718 } 719 720 /** 721 * Check if a javascript function methodhandle is a vararg handle 722 * 723 * @param mh method handle to check 724 * 725 * @return true if vararg 726 */ 727 protected static boolean isVarArg(final MethodHandle mh) { 728 final MethodType type = mh.type(); 729 return type.parameterType(type.parameterCount() - 1).isArray(); 730 } 731 732 @SuppressWarnings("unused") 733 private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) { 734 if (array2 == null) { 735 // Must clone it, as we can't allow the receiving method to alter the array 736 return array1.clone(); 737 } 738 739 final int l2 = array2.length; 740 if (l2 == 0) { 741 return array1.clone(); 742 } 743 744 final int l1 = array1.length; 745 final Object[] concat = new Object[l1 + l2]; 746 System.arraycopy(array1, 0, concat, 0, l1); 747 System.arraycopy(array2, 0, concat, l1, l2); 748 749 return concat; 750 } 751 752 @SuppressWarnings("unused") 753 private static Object newFilter(final Object result, final Object allocation) { 754 return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation; 755 } 756 757 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 758 return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types)); 759 } 760 }