ScriptFunctionData.java revision 1010:fc80190e129f
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 26package jdk.nashorn.internal.runtime; 27 28import static jdk.nashorn.internal.lookup.Lookup.MH; 29import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 31 32import java.io.IOException; 33import java.io.ObjectInputStream; 34import java.io.Serializable; 35import java.lang.invoke.MethodHandle; 36import java.lang.invoke.MethodHandles; 37import java.lang.invoke.MethodType; 38import java.util.LinkedList; 39import java.util.List; 40import jdk.nashorn.internal.runtime.linker.LinkerCallSite; 41 42 43/** 44 * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime. 45 * Instances of this class are created during codegen and stored in script classes' 46 * constants array to reduce function instantiation overhead during runtime. 47 */ 48public abstract class ScriptFunctionData implements Serializable { 49 static final int MAX_ARITY = LinkerCallSite.ARGLIMIT; 50 static { 51 // Assert it fits in a byte, as that's what we store it in. It's just a size optimization though, so if needed 52 // "byte arity" field can be widened. 53 assert MAX_ARITY < 256; 54 } 55 56 /** Name of the function or "" for anonymous functions */ 57 protected final String name; 58 59 /** 60 * A list of code versions of a function sorted in ascending order of generic descriptors. 61 */ 62 protected transient LinkedList<CompiledFunction> code = new LinkedList<>(); 63 64 /** Function flags */ 65 protected int flags; 66 67 // Parameter arity of the function, corresponding to "f.length". E.g. "function f(a, b, c) { ... }" arity is 3, and 68 // some built-in ECMAScript functions have their arity declared by the specification. Note that regardless of this 69 // value, the function might still be capable of receiving variable number of arguments, see isVariableArity. 70 private int arity; 71 72 /** 73 * A pair of method handles used for generic invoker and constructor. Field is volatile as it can be initialized by 74 * multiple threads concurrently, but we still tolerate a race condition in it as all values stored into it are 75 * idempotent. 76 */ 77 private volatile transient GenericInvokers genericInvokers; 78 79 private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class); 80 81 /** Is this a strict mode function? */ 82 public static final int IS_STRICT = 1 << 0; 83 /** Is this a built-in function? */ 84 public static final int IS_BUILTIN = 1 << 1; 85 /** Is this a constructor function? */ 86 public static final int IS_CONSTRUCTOR = 1 << 2; 87 /** Does this function expect a callee argument? */ 88 public static final int NEEDS_CALLEE = 1 << 3; 89 /** Does this function make use of the this-object argument? */ 90 public static final int USES_THIS = 1 << 4; 91 /** Is this a variable arity function? */ 92 public static final int IS_VARIABLE_ARITY = 1 << 5; 93 94 /** Flag for strict or built-in functions */ 95 public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN; 96 /** Flag for built-in constructors */ 97 public static final int IS_BUILTIN_CONSTRUCTOR = IS_BUILTIN | IS_CONSTRUCTOR; 98 /** Flag for strict constructors */ 99 public static final int IS_STRICT_CONSTRUCTOR = IS_STRICT | IS_CONSTRUCTOR; 100 101 private static final long serialVersionUID = 4252901245508769114L; 102 103 /** 104 * Constructor 105 * 106 * @param name script function name 107 * @param arity arity 108 * @param flags the function flags 109 */ 110 ScriptFunctionData(final String name, final int arity, final int flags) { 111 this.name = name; 112 this.flags = flags; 113 setArity(arity); 114 } 115 116 final int getArity() { 117 return arity; 118 } 119 120 final boolean isVariableArity() { 121 return (flags & IS_VARIABLE_ARITY) != 0; 122 } 123 124 /** 125 * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final 126 * @param arity new arity 127 */ 128 void setArity(final int arity) { 129 if(arity < 0 || arity > MAX_ARITY) { 130 throw new IllegalArgumentException(String.valueOf(arity)); 131 } 132 this.arity = arity; 133 } 134 135 CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) { 136 final MethodHandle boundInvoker = bindInvokeHandle(originalInv.createComposableInvoker(), fn, self, args); 137 138 if (isConstructor()) { 139 return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.createComposableConstructor(), fn, args)); 140 } 141 142 return new CompiledFunction(boundInvoker); 143 } 144 145 /** 146 * Is this a ScriptFunction generated with strict semantics? 147 * @return true if strict, false otherwise 148 */ 149 public boolean isStrict() { 150 return (flags & IS_STRICT) != 0; 151 } 152 153 /** 154 * Return the complete internal function name for this 155 * data, not anonymous or similar. May be identical 156 * @return internal function name 157 */ 158 protected String getFunctionName() { 159 return getName(); 160 } 161 162 boolean isBuiltin() { 163 return (flags & IS_BUILTIN) != 0; 164 } 165 166 boolean isConstructor() { 167 return (flags & IS_CONSTRUCTOR) != 0; 168 } 169 170 abstract boolean needsCallee(); 171 172 /** 173 * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument 174 * according to ECMA 10.4.3. 175 * @return true if this argument must be an object 176 */ 177 boolean needsWrappedThis() { 178 return (flags & USES_THIS) != 0 && (flags & IS_STRICT_OR_BUILTIN) == 0; 179 } 180 181 String toSource() { 182 return "function " + (name == null ? "" : name) + "() { [native code] }"; 183 } 184 185 String getName() { 186 return name; 187 } 188 189 /** 190 * Get this function as a String containing its source code. If no source code 191 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} 192 * 193 * @return string representation of this function 194 */ 195 @Override 196 public String toString() { 197 return name.isEmpty() ? "<anonymous>" : name; 198 } 199 200 /** 201 * Verbose description of data 202 * @return verbose description 203 */ 204 public String toStringVerbose() { 205 final StringBuilder sb = new StringBuilder(); 206 207 sb.append("name='"). 208 append(name.isEmpty() ? "<anonymous>" : name). 209 append("' "). 210 append(code.size()). 211 append(" invokers="). 212 append(code); 213 214 return sb.toString(); 215 } 216 217 /** 218 * Pick the best invoker, i.e. the one version of this method with as narrow and specific 219 * types as possible. If the call site arguments are objects, but boxed primitives we can 220 * also try to get a primitive version of the method and do an unboxing filter, but then 221 * we need to insert a guard that checks the argument is really always a boxed primitive 222 * and not suddenly a "real" object 223 * 224 * @param callSiteType callsite type 225 * @return compiled function object representing the best invoker. 226 */ 227 final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope) { 228 final CompiledFunction cf = getBest(callSiteType, runtimeScope); 229 assert cf != null; 230 return cf; 231 } 232 233 final CompiledFunction getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope) { 234 if (!isConstructor()) { 235 throw typeError("not.a.constructor", toSource()); 236 } 237 // Constructor call sites don't have a "this", but getBest is meant to operate on "callee, this, ..." style 238 final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope); 239 return cf; 240 } 241 242 /** 243 * If we can have lazy code generation, this is a hook to ensure that the code has been compiled. 244 * This does not guarantee the code been installed in this {@code ScriptFunctionData} instance 245 */ 246 protected void ensureCompiled() { 247 //empty 248 } 249 250 /** 251 * Return a generic Object/Object invoker for this method. It will ensure code 252 * is generated, get the most generic of all versions of this function and adapt it 253 * to Objects. 254 * 255 * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the 256 * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime 257 * scope is not known, but that might cause compilation of code that will need more deoptimization passes. 258 * @return generic invoker of this script function 259 */ 260 final MethodHandle getGenericInvoker(final ScriptObject runtimeScope) { 261 // This method has race conditions both on genericsInvoker and genericsInvoker.invoker, but even if invoked 262 // concurrently, they'll create idempotent results, so it doesn't matter. We could alternatively implement this 263 // using java.util.concurrent.AtomicReferenceFieldUpdater, but it's hardly worth it. 264 final GenericInvokers lgenericInvokers = ensureGenericInvokers(); 265 MethodHandle invoker = lgenericInvokers.invoker; 266 if(invoker == null) { 267 lgenericInvokers.invoker = invoker = createGenericInvoker(runtimeScope); 268 } 269 return invoker; 270 } 271 272 private MethodHandle createGenericInvoker(final ScriptObject runtimeScope) { 273 return makeGenericMethod(getGeneric(runtimeScope).createComposableInvoker()); 274 } 275 276 final MethodHandle getGenericConstructor(final ScriptObject runtimeScope) { 277 // This method has race conditions both on genericsInvoker and genericsInvoker.constructor, but even if invoked 278 // concurrently, they'll create idempotent results, so it doesn't matter. We could alternatively implement this 279 // using java.util.concurrent.AtomicReferenceFieldUpdater, but it's hardly worth it. 280 final GenericInvokers lgenericInvokers = ensureGenericInvokers(); 281 MethodHandle constructor = lgenericInvokers.constructor; 282 if(constructor == null) { 283 lgenericInvokers.constructor = constructor = createGenericConstructor(runtimeScope); 284 } 285 return constructor; 286 } 287 288 private MethodHandle createGenericConstructor(final ScriptObject runtimeScope) { 289 return makeGenericMethod(getGeneric(runtimeScope).createComposableConstructor()); 290 } 291 292 private GenericInvokers ensureGenericInvokers() { 293 GenericInvokers lgenericInvokers = genericInvokers; 294 if(lgenericInvokers == null) { 295 genericInvokers = lgenericInvokers = new GenericInvokers(); 296 } 297 return lgenericInvokers; 298 } 299 300 private static MethodType widen(final MethodType cftype) { 301 final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()]; 302 for (int i = 0; i < cftype.parameterCount(); i++) { 303 paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class; 304 } 305 return MH.type(cftype.returnType(), paramTypes); 306 } 307 308 /** 309 * Used to find an apply to call version that fits this callsite. 310 * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int) 311 * for (Object, Object, int, int, int) or we will destroy the semantics and get 312 * a function that, when padded with undefineds, behaves differently 313 * @param type actual call site type 314 * @return apply to call that perfectly fits this callsite or null if none found 315 */ 316 CompiledFunction lookupExactApplyToCall(final MethodType type) { 317 for (final CompiledFunction cf : code) { 318 if (!cf.isApplyToCall()) { 319 continue; 320 } 321 322 final MethodType cftype = cf.type(); 323 if (cftype.parameterCount() != type.parameterCount()) { 324 continue; 325 } 326 327 if (widen(cftype).equals(widen(type))) { 328 return cf; 329 } 330 } 331 332 return null; 333 } 334 335 CompiledFunction pickFunction(final MethodType callSiteType, final boolean canPickVarArg) { 336 for (final CompiledFunction candidate : code) { 337 if (candidate.matchesCallSite(callSiteType, canPickVarArg)) { 338 return candidate; 339 } 340 } 341 return null; 342 } 343 344 /** 345 * Returns the best function for the specified call site type. 346 * @param callSiteType The call site type. Call site types are expected to have the form 347 * {@code (callee, this[, args...])}. 348 * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the 349 * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime 350 * scope is not known, but that might cause compilation of code that will need more deoptimization passes. 351 * @return the best function for the specified call site type. 352 */ 353 CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) { 354 assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this) 355 assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function 356 357 if (isRecompilable()) { 358 final CompiledFunction candidate = pickFunction(callSiteType, false); 359 if (candidate != null) { 360 return candidate; 361 } 362 return pickFunction(callSiteType, true); //try vararg last 363 } 364 365 CompiledFunction best = null; 366 for(final CompiledFunction candidate: code) { 367 if(candidate.betterThanFinal(best, callSiteType)) { 368 best = candidate; 369 } 370 } 371 372 return best; 373 } 374 375 376 abstract boolean isRecompilable(); 377 378 CompiledFunction getGeneric(final ScriptObject runtimeScope) { 379 return getBest(getGenericType(), runtimeScope); 380 } 381 382 /** 383 * Get a method type for a generic invoker. 384 * @return the method type for the generic invoker 385 */ 386 abstract MethodType getGenericType(); 387 388 /** 389 * Allocates an object using this function's allocator. 390 * 391 * @param map the property map for the allocated object. 392 * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator. 393 */ 394 ScriptObject allocate(final PropertyMap map) { 395 return null; 396 } 397 398 /** 399 * Get the property map to use for objects allocated by this function. 400 * 401 * @return the property map for allocated objects. 402 */ 403 PropertyMap getAllocatorMap() { 404 return null; 405 } 406 407 /** 408 * This method is used to create the immutable portion of a bound function. 409 * See {@link ScriptFunction#makeBoundFunction(Object, Object[])} 410 * 411 * @param fn the original function being bound 412 * @param self this reference to bind. Can be null. 413 * @param args additional arguments to bind. Can be null. 414 */ 415 ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) { 416 final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args; 417 final int length = args == null ? 0 : args.length; 418 // Clear the callee and this flags 419 final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS; 420 421 final List<CompiledFunction> boundList = new LinkedList<>(); 422 final ScriptObject runtimeScope = fn.getScope(); 423 final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope)); 424 boundList.add(bind(bindTarget, fn, self, allArgs)); 425 426 return new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, boundFlags); 427 } 428 429 /** 430 * Convert this argument for non-strict functions according to ES 10.4.3 431 * 432 * @param thiz the this argument 433 * 434 * @return the converted this object 435 */ 436 private Object convertThisObject(final Object thiz) { 437 return needsWrappedThis() ? wrapThis(thiz) : thiz; 438 } 439 440 static Object wrapThis(final Object thiz) { 441 if (!(thiz instanceof ScriptObject)) { 442 if (JSType.nullOrUndefined(thiz)) { 443 return Context.getGlobal(); 444 } 445 446 if (isPrimitiveThis(thiz)) { 447 return Context.getGlobal().wrapAsObject(thiz); 448 } 449 } 450 451 return thiz; 452 } 453 454 static boolean isPrimitiveThis(final Object obj) { 455 return obj instanceof String || obj instanceof ConsString || 456 obj instanceof Number || obj instanceof Boolean; 457 } 458 459 /** 460 * Creates an invoker method handle for a bound function. 461 * 462 * @param targetFn the function being bound 463 * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or 464 * any of its specializations. 465 * @param self the "this" value being bound 466 * @param args additional arguments being bound 467 * 468 * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting 469 * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting 470 * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed 471 * to the original invoker on invocation. 472 */ 473 private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) { 474 // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound 475 // in the target and will be ignored anyway. 476 final boolean isTargetBound = targetFn.isBoundFunction(); 477 478 final boolean needsCallee = needsCallee(originalInvoker); 479 assert needsCallee == needsCallee() : "callee contract violation 2"; 480 assert !(isTargetBound && needsCallee); // already bound functions don't need a callee 481 482 final Object boundSelf = isTargetBound ? null : convertThisObject(self); 483 final MethodHandle boundInvoker; 484 485 if (isVarArg(originalInvoker)) { 486 // First, bind callee and this without arguments 487 final MethodHandle noArgBoundInvoker; 488 489 if (isTargetBound) { 490 // Don't bind either callee or this 491 noArgBoundInvoker = originalInvoker; 492 } else if (needsCallee) { 493 // Bind callee and this 494 noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf); 495 } else { 496 // Only bind this 497 noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf); 498 } 499 // Now bind arguments 500 if (args.length > 0) { 501 boundInvoker = varArgBinder(noArgBoundInvoker, args); 502 } else { 503 boundInvoker = noArgBoundInvoker; 504 } 505 } else { 506 // If target is already bound, insert additional bound arguments after "this" argument, at position 1. 507 final int argInsertPos = isTargetBound ? 1 : 0; 508 final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : needsCallee ? 2 : 1))]; 509 int next = 0; 510 if (!isTargetBound) { 511 if (needsCallee) { 512 boundArgs[next++] = targetFn; 513 } 514 boundArgs[next++] = boundSelf; 515 } 516 // If more bound args were specified than the function can take, we'll just drop those. 517 System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next); 518 // If target is already bound, insert additional bound arguments after "this" argument, at position 1; 519 // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions 520 // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args 521 // start at position 1. If the function is not bound, we start inserting arguments at position 0. 522 boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs); 523 } 524 525 if (isTargetBound) { 526 return boundInvoker; 527 } 528 529 // If the target is not already bound, add a dropArguments that'll throw away the passed this 530 return MH.dropArguments(boundInvoker, 0, Object.class); 531 } 532 533 /** 534 * Creates a constructor method handle for a bound function using the passed constructor handle. 535 * 536 * @param originalConstructor the constructor handle to bind. It must be a composed constructor. 537 * @param fn the function being bound 538 * @param args arguments being bound 539 * 540 * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never 541 * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor 542 * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if 543 * this script function data object has no constructor handle, null is returned. 544 */ 545 private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) { 546 assert originalConstructor != null; 547 548 // If target function is already bound, don't bother binding the callee. 549 final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor : 550 MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class); 551 552 if (args.length == 0) { 553 return calleeBoundConstructor; 554 } 555 556 if (isVarArg(calleeBoundConstructor)) { 557 return varArgBinder(calleeBoundConstructor, args); 558 } 559 560 final Object[] boundArgs; 561 562 final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1; 563 if (args.length <= maxArgCount) { 564 boundArgs = args; 565 } else { 566 boundArgs = new Object[maxArgCount]; 567 System.arraycopy(args, 0, boundArgs, 0, maxArgCount); 568 } 569 570 return MH.insertArguments(calleeBoundConstructor, 1, boundArgs); 571 } 572 573 /** 574 * Takes a method handle, and returns a potentially different method handle that can be used in 575 * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}. 576 * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into 577 * {@code Object} as well, except for the following ones: 578 * <ul> 579 * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> 580 * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself 581 * (callee) as an argument.</li> 582 * </ul> 583 * 584 * @param mh the original method handle 585 * 586 * @return the new handle, conforming to the rules above. 587 */ 588 private static MethodHandle makeGenericMethod(final MethodHandle mh) { 589 final MethodType type = mh.type(); 590 final MethodType newType = makeGenericType(type); 591 return type.equals(newType) ? mh : mh.asType(newType); 592 } 593 594 private static MethodType makeGenericType(final MethodType type) { 595 MethodType newType = type.generic(); 596 if (isVarArg(type)) { 597 newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); 598 } 599 if (needsCallee(type)) { 600 newType = newType.changeParameterType(0, ScriptFunction.class); 601 } 602 return newType; 603 } 604 605 /** 606 * Execute this script function. 607 * 608 * @param self Target object. 609 * @param arguments Call arguments. 610 * @return ScriptFunction result. 611 * 612 * @throws Throwable if there is an exception/error with the invocation or thrown from it 613 */ 614 Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable { 615 final MethodHandle mh = getGenericInvoker(fn.getScope()); 616 final Object selfObj = convertThisObject(self); 617 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 618 619 DebuggerSupport.notifyInvoke(mh); 620 621 if (isVarArg(mh)) { 622 if (needsCallee(mh)) { 623 return mh.invokeExact(fn, selfObj, args); 624 } 625 return mh.invokeExact(selfObj, args); 626 } 627 628 final int paramCount = mh.type().parameterCount(); 629 if (needsCallee(mh)) { 630 switch (paramCount) { 631 case 2: 632 return mh.invokeExact(fn, selfObj); 633 case 3: 634 return mh.invokeExact(fn, selfObj, getArg(args, 0)); 635 case 4: 636 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1)); 637 case 5: 638 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 639 case 6: 640 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 641 case 7: 642 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 643 case 8: 644 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 645 default: 646 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args)); 647 } 648 } 649 650 switch (paramCount) { 651 case 1: 652 return mh.invokeExact(selfObj); 653 case 2: 654 return mh.invokeExact(selfObj, getArg(args, 0)); 655 case 3: 656 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); 657 case 4: 658 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 659 case 5: 660 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 661 case 6: 662 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 663 case 7: 664 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 665 default: 666 return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args)); 667 } 668 } 669 670 Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable { 671 final MethodHandle mh = getGenericConstructor(fn.getScope()); 672 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 673 674 DebuggerSupport.notifyInvoke(mh); 675 676 if (isVarArg(mh)) { 677 if (needsCallee(mh)) { 678 return mh.invokeExact(fn, args); 679 } 680 return mh.invokeExact(args); 681 } 682 683 final int paramCount = mh.type().parameterCount(); 684 if (needsCallee(mh)) { 685 switch (paramCount) { 686 case 1: 687 return mh.invokeExact(fn); 688 case 2: 689 return mh.invokeExact(fn, getArg(args, 0)); 690 case 3: 691 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1)); 692 case 4: 693 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 694 case 5: 695 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 696 case 6: 697 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 698 case 7: 699 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 700 default: 701 return mh.invokeWithArguments(withArguments(fn, paramCount, args)); 702 } 703 } 704 705 switch (paramCount) { 706 case 0: 707 return mh.invokeExact(); 708 case 1: 709 return mh.invokeExact(getArg(args, 0)); 710 case 2: 711 return mh.invokeExact(getArg(args, 0), getArg(args, 1)); 712 case 3: 713 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2)); 714 case 4: 715 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 716 case 5: 717 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 718 case 6: 719 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 720 default: 721 return mh.invokeWithArguments(withArguments(null, paramCount, args)); 722 } 723 } 724 725 private static Object getArg(final Object[] args, final int i) { 726 return i < args.length ? args[i] : UNDEFINED; 727 } 728 729 private static Object[] withArguments(final ScriptFunction fn, final int argCount, final Object[] args) { 730 final Object[] finalArgs = new Object[argCount]; 731 732 int nextArg = 0; 733 if (fn != null) { 734 //needs callee 735 finalArgs[nextArg++] = fn; 736 } 737 738 // Don't add more args that there is argCount in the handle (including self and callee). 739 for (int i = 0; i < args.length && nextArg < argCount;) { 740 finalArgs[nextArg++] = args[i++]; 741 } 742 743 // If we have fewer args than argCount, pad with undefined. 744 while (nextArg < argCount) { 745 finalArgs[nextArg++] = UNDEFINED; 746 } 747 748 return finalArgs; 749 } 750 751 private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) { 752 final Object[] finalArgs = new Object[argCount]; 753 754 int nextArg = 0; 755 if (fn != null) { 756 //needs callee 757 finalArgs[nextArg++] = fn; 758 } 759 finalArgs[nextArg++] = self; 760 761 // Don't add more args that there is argCount in the handle (including self and callee). 762 for (int i = 0; i < args.length && nextArg < argCount;) { 763 finalArgs[nextArg++] = args[i++]; 764 } 765 766 // If we have fewer args than argCount, pad with undefined. 767 while (nextArg < argCount) { 768 finalArgs[nextArg++] = UNDEFINED; 769 } 770 771 return finalArgs; 772 } 773 /** 774 * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the 775 * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on 776 * invocation 777 * 778 * @param mh the handle 779 * @param args the bound arguments 780 * 781 * @return the bound method handle 782 */ 783 private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) { 784 assert args != null; 785 assert args.length > 0; 786 return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args)); 787 } 788 789 /** 790 * Heuristic to figure out if the method handle has a callee argument. If it's type is 791 * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as 792 * the constructor above is not passed this information, and can't just blindly assume it's false 793 * (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore 794 * they also always receive a callee). 795 * 796 * @param mh the examined method handle 797 * 798 * @return true if the method handle expects a callee, false otherwise 799 */ 800 protected static boolean needsCallee(final MethodHandle mh) { 801 return needsCallee(mh.type()); 802 } 803 804 static boolean needsCallee(final MethodType type) { 805 final int length = type.parameterCount(); 806 807 if (length == 0) { 808 return false; 809 } 810 811 final Class<?> param0 = type.parameterType(0); 812 return param0 == ScriptFunction.class || param0 == boolean.class && length > 1 && type.parameterType(1) == ScriptFunction.class; 813 } 814 815 /** 816 * Check if a javascript function methodhandle is a vararg handle 817 * 818 * @param mh method handle to check 819 * 820 * @return true if vararg 821 */ 822 protected static boolean isVarArg(final MethodHandle mh) { 823 return isVarArg(mh.type()); 824 } 825 826 static boolean isVarArg(final MethodType type) { 827 return type.parameterType(type.parameterCount() - 1).isArray(); 828 } 829 830 /** 831 * Is this ScriptFunction declared in a dynamic context 832 * @return true if in dynamic context, false if not or irrelevant 833 */ 834 public boolean inDynamicContext() { 835 return false; 836 } 837 838 @SuppressWarnings("unused") 839 private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) { 840 if (array2 == null) { 841 // Must clone it, as we can't allow the receiving method to alter the array 842 return array1.clone(); 843 } 844 845 final int l2 = array2.length; 846 if (l2 == 0) { 847 return array1.clone(); 848 } 849 850 final int l1 = array1.length; 851 final Object[] concat = new Object[l1 + l2]; 852 System.arraycopy(array1, 0, concat, 0, l1); 853 System.arraycopy(array2, 0, concat, l1, l2); 854 855 return concat; 856 } 857 858 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 859 return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types)); 860 } 861 862 /** 863 * This class is used to hold the generic invoker and generic constructor pair. It is structured in this way since 864 * most functions will never use them, so this way ScriptFunctionData only pays storage cost for one null reference 865 * to the GenericInvokers object, instead of two null references for the two method handles. 866 */ 867 private static final class GenericInvokers { 868 volatile MethodHandle invoker; 869 volatile MethodHandle constructor; 870 } 871 872 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 873 in.defaultReadObject(); 874 code = new LinkedList<>(); 875 } 876} 877