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