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