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