ScriptFunction.java revision 1000:0b7b3bd3cc04
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.codegen.CompilerConstants.virtualCallNoLookup; 29import static jdk.nashorn.internal.lookup.Lookup.MH; 30import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 31import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 32import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 33 34import java.lang.invoke.MethodHandle; 35import java.lang.invoke.MethodHandles; 36import java.lang.invoke.MethodType; 37import java.lang.invoke.SwitchPoint; 38import java.util.Collections; 39 40import jdk.internal.dynalink.CallSiteDescriptor; 41import jdk.internal.dynalink.linker.GuardedInvocation; 42import jdk.internal.dynalink.linker.LinkRequest; 43import jdk.internal.dynalink.support.Guards; 44import jdk.nashorn.internal.codegen.ApplySpecialization; 45import jdk.nashorn.internal.codegen.CompilerConstants.Call; 46import jdk.nashorn.internal.objects.Global; 47import jdk.nashorn.internal.objects.NativeFunction; 48import jdk.nashorn.internal.runtime.ScriptFunctionData; 49import jdk.nashorn.internal.runtime.ScriptObject; 50import jdk.nashorn.internal.runtime.ScriptRuntime; 51import jdk.nashorn.internal.runtime.linker.Bootstrap; 52import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 53 54/** 55 * Runtime representation of a JavaScript function. 56 */ 57public abstract class ScriptFunction extends ScriptObject { 58 59 /** Method handle for prototype getter for this ScriptFunction */ 60 public static final MethodHandle G$PROTOTYPE = findOwnMH_S("G$prototype", Object.class, Object.class); 61 62 /** Method handle for prototype setter for this ScriptFunction */ 63 public static final MethodHandle S$PROTOTYPE = findOwnMH_S("S$prototype", void.class, Object.class, Object.class); 64 65 /** Method handle for length getter for this ScriptFunction */ 66 public static final MethodHandle G$LENGTH = findOwnMH_S("G$length", int.class, Object.class); 67 68 /** Method handle for name getter for this ScriptFunction */ 69 public static final MethodHandle G$NAME = findOwnMH_S("G$name", Object.class, Object.class); 70 71 /** Method handle used for implementing sync() in mozilla_compat */ 72 public static final MethodHandle INVOKE_SYNC = findOwnMH_S("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class); 73 74 /** Method handle for allocate function for this ScriptFunction */ 75 static final MethodHandle ALLOCATE = findOwnMH_V("allocate", Object.class); 76 77 private static final MethodHandle WRAPFILTER = findOwnMH_S("wrapFilter", Object.class, Object.class); 78 79 private static final MethodHandle SCRIPTFUNCTION_GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); 80 81 /** method handle to scope getter for this ScriptFunction */ 82 public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); 83 84 private static final MethodHandle IS_FUNCTION_MH = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class); 85 86 private static final MethodHandle IS_APPLY_FUNCTION = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class); 87 88 private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH_S("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class); 89 90 private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH_S("addZerothElement", Object[].class, Object[].class, Object.class); 91 92 private static final MethodHandle WRAP_THIS = MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, "wrapThis", MH.type(Object.class, Object.class)); 93 94 /** The parent scope. */ 95 private final ScriptObject scope; 96 97 private final ScriptFunctionData data; 98 99 /** The property map used for newly allocated object when function is used as constructor. */ 100 protected PropertyMap allocatorMap; 101 102 /** 103 * Constructor 104 * 105 * @param name function name 106 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) 107 * @param map property map 108 * @param scope scope 109 * @param specs specialized version of this function - other method handles 110 * @param flags {@link ScriptFunctionData} flags 111 */ 112 protected ScriptFunction( 113 final String name, 114 final MethodHandle methodHandle, 115 final PropertyMap map, 116 final ScriptObject scope, 117 final MethodHandle[] specs, 118 final int flags) { 119 120 this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope); 121 } 122 123 /** 124 * Constructor 125 * 126 * @param data static function data 127 * @param map property map 128 * @param scope scope 129 */ 130 protected ScriptFunction( 131 final ScriptFunctionData data, 132 final PropertyMap map, 133 final ScriptObject scope) { 134 135 super(map); 136 137 if (Context.DEBUG) { 138 constructorCount++; 139 } 140 141 this.data = data; 142 this.scope = scope; 143 this.allocatorMap = data.getAllocatorMap(); 144 } 145 146 @Override 147 public String getClassName() { 148 return "Function"; 149 } 150 151 /** 152 * ECMA 15.3.5.3 [[HasInstance]] (V) 153 * Step 3 if "prototype" value is not an Object, throw TypeError 154 */ 155 @Override 156 public boolean isInstance(final ScriptObject instance) { 157 final Object basePrototype = getTargetFunction().getPrototype(); 158 if (!(basePrototype instanceof ScriptObject)) { 159 throw typeError("prototype.not.an.object", ScriptRuntime.safeToString(getTargetFunction()), ScriptRuntime.safeToString(basePrototype)); 160 } 161 162 for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) { 163 if (proto == basePrototype) { 164 return true; 165 } 166 } 167 168 return false; 169 } 170 171 /** 172 * Returns the target function for this function. If the function was not created using 173 * {@link #makeBoundFunction(Object, Object[])}, its target function is itself. If it is bound, its target function 174 * is the target function of the function it was made from (therefore, the target function is always the final, 175 * unbound recipient of the calls). 176 * @return the target function for this function. 177 */ 178 protected ScriptFunction getTargetFunction() { 179 return this; 180 } 181 182 boolean isBoundFunction() { 183 return getTargetFunction() != this; 184 } 185 186 /** 187 * Set the arity of this ScriptFunction 188 * @param arity arity 189 */ 190 public final void setArity(final int arity) { 191 data.setArity(arity); 192 } 193 194 /** 195 * Is this a ECMAScript 'use strict' function? 196 * @return true if function is in strict mode 197 */ 198 public boolean isStrict() { 199 return data.isStrict(); 200 } 201 202 /** 203 * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument 204 * according to ECMA 10.4.3. 205 * @return true if this argument must be an object 206 */ 207 public boolean needsWrappedThis() { 208 return data.needsWrappedThis(); 209 } 210 211 private static boolean needsWrappedThis(final Object fn) { 212 return fn instanceof ScriptFunction ? ((ScriptFunction)fn).needsWrappedThis() : false; 213 } 214 215 /** 216 * Execute this script function. 217 * @param self Target object. 218 * @param arguments Call arguments. 219 * @return ScriptFunction result. 220 * @throws Throwable if there is an exception/error with the invocation or thrown from it 221 */ 222 Object invoke(final Object self, final Object... arguments) throws Throwable { 223 if (Context.DEBUG) { 224 invokes++; 225 } 226 return data.invoke(this, self, arguments); 227 } 228 229 /** 230 * Execute this script function as a constructor. 231 * @param arguments Call arguments. 232 * @return Newly constructed result. 233 * @throws Throwable if there is an exception/error with the invocation or thrown from it 234 */ 235 Object construct(final Object... arguments) throws Throwable { 236 return data.construct(this, arguments); 237 } 238 239 /** 240 * Allocate function. Called from generated {@link ScriptObject} code 241 * for allocation as a factory method 242 * 243 * @return a new instance of the {@link ScriptObject} whose allocator this is 244 */ 245 @SuppressWarnings("unused") 246 private Object allocate() { 247 if (Context.DEBUG) { 248 allocations++; 249 } 250 251 assert !isBoundFunction(); // allocate never invoked on bound functions 252 253 final ScriptObject object = data.allocate(allocatorMap); 254 255 if (object != null) { 256 final Object prototype = getPrototype(); 257 if (prototype instanceof ScriptObject) { 258 object.setInitialProto((ScriptObject)prototype); 259 } 260 261 if (object.getProto() == null) { 262 object.setInitialProto(getObjectPrototype()); 263 } 264 } 265 266 return object; 267 } 268 269 /** 270 * Return Object.prototype - used by "allocate" 271 * @return Object.prototype 272 */ 273 protected abstract ScriptObject getObjectPrototype(); 274 275 /** 276 * Creates a version of this function bound to a specific "self" and other arguments, as per 277 * {@code Function.prototype.bind} functionality in ECMAScript 5.1 section 15.3.4.5. 278 * @param self the self to bind to this function. Can be null (in which case, null is bound as this). 279 * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments. 280 * @return a function with the specified self and parameters bound. 281 */ 282 protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) { 283 return makeBoundFunction(data.makeBoundFunctionData(this, self, args)); 284 } 285 286 /** 287 * Create a version of this function as in {@link ScriptFunction#makeBoundFunction(Object, Object[])}, 288 * but using a {@link ScriptFunctionData} for the bound data. 289 * 290 * @param boundData ScriptFuntionData for the bound function 291 * @return a function with the bindings performed according to the given data 292 */ 293 protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData); 294 295 @Override 296 public final String safeToString() { 297 return toSource(); 298 } 299 300 @Override 301 public String toString() { 302 return data.toString(); 303 } 304 305 /** 306 * Get this function as a String containing its source code. If no source code 307 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} 308 * @return string representation of this function's source 309 */ 310 public final String toSource() { 311 return data.toSource(); 312 } 313 314 /** 315 * Get the prototype object for this function 316 * @return prototype 317 */ 318 public abstract Object getPrototype(); 319 320 /** 321 * Set the prototype object for this function 322 * @param prototype new prototype object 323 */ 324 public abstract void setPrototype(Object prototype); 325 326 /** 327 * Create a function that invokes this function synchronized on {@code sync} or the self object 328 * of the invocation. 329 * @param sync the Object to synchronize on, or undefined 330 * @return synchronized function 331 */ 332 public abstract ScriptFunction makeSynchronizedFunction(Object sync); 333 334 /** 335 * Return the invoke handle bound to a given ScriptObject self reference. 336 * If callee parameter is required result is rebound to this. 337 * 338 * @param self self reference 339 * @return bound invoke handle 340 */ 341 public final MethodHandle getBoundInvokeHandle(final Object self) { 342 return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), self); 343 } 344 345 /** 346 * Bind the method handle to this {@code ScriptFunction} instance if it needs a callee parameter. If this function's 347 * method handles don't have a callee parameter, the handle is returned unchanged. 348 * @param methodHandle the method handle to potentially bind to this function instance. 349 * @return the potentially bound method handle 350 */ 351 private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) { 352 return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle; 353 354 } 355 356 /** 357 * Get the name for this function 358 * @return the name 359 */ 360 public final String getName() { 361 return data.getName(); 362 } 363 364 365 /** 366 * Get the scope for this function 367 * @return the scope 368 */ 369 public final ScriptObject getScope() { 370 return scope; 371 } 372 373 /** 374 * Prototype getter for this ScriptFunction - follows the naming convention 375 * used by Nasgen and the code generator 376 * 377 * @param self self reference 378 * @return self's prototype 379 */ 380 public static Object G$prototype(final Object self) { 381 return self instanceof ScriptFunction ? 382 ((ScriptFunction)self).getPrototype() : 383 UNDEFINED; 384 } 385 386 /** 387 * Prototype setter for this ScriptFunction - follows the naming convention 388 * used by Nasgen and the code generator 389 * 390 * @param self self reference 391 * @param prototype prototype to set 392 */ 393 public static void S$prototype(final Object self, final Object prototype) { 394 if (self instanceof ScriptFunction) { 395 ((ScriptFunction)self).setPrototype(prototype); 396 } 397 } 398 399 /** 400 * Length getter - ECMA 15.3.3.2: Function.length 401 * @param self self reference 402 * @return length 403 */ 404 public static int G$length(final Object self) { 405 if (self instanceof ScriptFunction) { 406 return ((ScriptFunction)self).data.getArity(); 407 } 408 409 return 0; 410 } 411 412 /** 413 * Name getter - ECMA Function.name 414 * @param self self refence 415 * @return the name, or undefined if none 416 */ 417 public static Object G$name(final Object self) { 418 if (self instanceof ScriptFunction) { 419 return ((ScriptFunction)self).getName(); 420 } 421 422 return UNDEFINED; 423 } 424 425 /** 426 * Get the prototype for this ScriptFunction 427 * @param constructor constructor 428 * @return prototype, or null if given constructor is not a ScriptFunction 429 */ 430 public static ScriptObject getPrototype(final ScriptFunction constructor) { 431 if (constructor != null) { 432 final Object proto = constructor.getPrototype(); 433 if (proto instanceof ScriptObject) { 434 return (ScriptObject)proto; 435 } 436 } 437 438 return null; 439 } 440 441 // These counters are updated only in debug mode. 442 private static int constructorCount; 443 private static int invokes; 444 private static int allocations; 445 446 /** 447 * @return the constructorCount 448 */ 449 public static int getConstructorCount() { 450 return constructorCount; 451 } 452 453 /** 454 * @return the invokes 455 */ 456 public static int getInvokes() { 457 return invokes; 458 } 459 460 /** 461 * @return the allocations 462 */ 463 public static int getAllocations() { 464 return allocations; 465 } 466 467 @Override 468 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) { 469 final MethodType type = desc.getMethodType(); 470 assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc); 471 final CompiledFunction cf = data.getBestConstructor(type, scope); 472 final GuardedInvocation bestCtorInv = cf.createConstructorInvocation(); 473 //TODO - ClassCastException 474 return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null); 475 } 476 477 @SuppressWarnings("unused") 478 private static Object wrapFilter(final Object obj) { 479 if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) { 480 return obj; 481 } 482 return Context.getGlobal().wrapAsObject(obj); 483 } 484 485 486 @SuppressWarnings("unused") 487 private static Object globalFilter(final Object object) { 488 // replace whatever we get with the current global object 489 return Context.getGlobal(); 490 } 491 492 /** 493 * dyn:call call site signature: (callee, thiz, [args...]) 494 * generated method signature: (callee, thiz, [args...]) 495 * 496 * cases: 497 * (a) method has callee parameter 498 * (1) for local/scope calls, we just bind thiz and drop the second argument. 499 * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. 500 * (b) method doesn't have callee parameter (builtin functions) 501 * (3) for local/scope calls, bind thiz and drop both callee and thiz. 502 * (4) for normal this-calls, drop callee. 503 * 504 * @return guarded invocation for call 505 */ 506 @Override 507 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 508 final MethodType type = desc.getMethodType(); 509 510 final String name = getName(); 511 final boolean isUnstable = request.isCallSiteUnstable(); 512 final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); 513 final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name); 514 final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name); 515 516 final boolean isApplyOrCall = isCall | isApply; 517 518 if (isUnstable && !isApplyOrCall) { 519 //megamorphic - replace call with apply 520 final MethodHandle handle; 521 //ensure that the callsite is vararg so apply can consume it 522 if (type.parameterCount() == 3 && type.parameterType(2) == Object[].class) { 523 // Vararg call site 524 handle = ScriptRuntime.APPLY.methodHandle(); 525 } else { 526 // (callee, this, args...) => (callee, this, args[]) 527 handle = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2); 528 } 529 530 // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a 531 // generic "is this a ScriptFunction?" guard. 532 return new GuardedInvocation( 533 handle, 534 null, 535 (SwitchPoint)null, 536 ClassCastException.class); 537 } 538 539 MethodHandle boundHandle; 540 MethodHandle guard = null; 541 542 // Special handling of Function.apply and Function.call. Note we must be invoking 543 if (isApplyOrCall && !isUnstable) { 544 final Object[] args = request.getArguments(); 545 if (Bootstrap.isCallable(args[1])) { 546 return createApplyOrCallCall(isApply, desc, request, args); 547 } 548 } //else just fall through and link as ordinary function or unstable apply 549 550 final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT; 551 final CompiledFunction cf = data.getBestInvoker(type, scope); 552 final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint); 553 final MethodHandle callHandle = bestInvoker.getInvocation(); 554 555 if (data.needsCallee()) { 556 if (scopeCall && needsWrappedThis()) { 557 // (callee, this, args...) => (callee, [this], args...) 558 boundHandle = MH.filterArguments(callHandle, 1, SCRIPTFUNCTION_GLOBALFILTER); 559 } else { 560 // It's already (callee, this, args...), just what we need 561 boundHandle = callHandle; 562 } 563 } else if (data.isBuiltin() && "extend".equals(data.getName())) { 564 // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the 565 // current lookup as its "this" so it can do security-sensitive creation of adapter classes. 566 boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, type.parameterType(0), type.parameterType(1)); 567 } else if (scopeCall && needsWrappedThis()) { 568 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined 569 // (this, args...) => ([this], args...) 570 boundHandle = MH.filterArguments(callHandle, 0, SCRIPTFUNCTION_GLOBALFILTER); 571 // ([this], args...) => ([callee], [this], args...) 572 boundHandle = MH.dropArguments(boundHandle, 0, type.parameterType(0)); 573 } else { 574 // (this, args...) => ([callee], this, args...) 575 boundHandle = MH.dropArguments(callHandle, 0, type.parameterType(0)); 576 } 577 578 // For non-strict functions, check whether this-object is primitive type. 579 // If so add a to-object-wrapper argument filter. 580 // Else install a guard that will trigger a relink when the argument becomes primitive. 581 if (!scopeCall && needsWrappedThis()) { 582 if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) { 583 boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER); 584 } else { 585 guard = getNonStrictFunctionGuard(this); 586 } 587 } 588 589 boundHandle = pairArguments(boundHandle, type); 590 591 return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this, cf.getFlags()) : guard, bestInvoker.getSwitchPoints(), null); 592 } 593 594 private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) { 595 final MethodType descType = desc.getMethodType(); 596 final int paramCount = descType.parameterCount(); 597 if(descType.parameterType(paramCount - 1).isArray()) { 598 // This is vararg invocation of apply or call. This can normally only happen when we do a recursive 599 // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate 600 // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader. 601 return createVarArgApplyOrCallCall(isApply, desc, request, args); 602 } 603 604 final boolean passesThis = paramCount > 2; 605 final boolean passesArgs = paramCount > 3; 606 final int realArgCount = passesArgs ? paramCount - 3 : 0; 607 608 final Object appliedFn = args[1]; 609 final boolean appliedFnNeedsWrappedThis = needsWrappedThis(appliedFn); 610 611 //box call back to apply 612 CallSiteDescriptor appliedDesc = desc; 613 final SwitchPoint applyToCallSwitchPoint = Global.instance().getChangeCallback("apply"); 614 //enough to change the proto switchPoint here 615 616 final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc); 617 final boolean isFailedApplyToCall = isApplyToCall && applyToCallSwitchPoint.hasBeenInvalidated(); 618 619 // R(apply|call, ...) => R(...) 620 MethodType appliedType = descType.dropParameterTypes(0, 1); 621 if (!passesThis) { 622 // R() => R(this) 623 appliedType = appliedType.insertParameterTypes(1, Object.class); 624 } else if (appliedFnNeedsWrappedThis) { 625 appliedType = appliedType.changeParameterType(1, Object.class); 626 } 627 628 /* 629 * dropArgs is a synthetic method handle that contains any args that we need to 630 * get rid of that come after the arguments array in the apply case. We adapt 631 * the callsite to ask for 3 args only and then dropArguments on the method handle 632 * to make it fit the extraneous args. 633 */ 634 MethodType dropArgs = MH.type(void.class); 635 if (isApply && !isFailedApplyToCall) { 636 final int pc = appliedType.parameterCount(); 637 for (int i = 3; i < pc; i++) { 638 dropArgs = dropArgs.appendParameterTypes(appliedType.parameterType(i)); 639 } 640 if (pc > 3) { 641 appliedType = appliedType.dropParameterTypes(3, pc); 642 } 643 } 644 645 if (isApply || isFailedApplyToCall) { 646 if (passesArgs) { 647 // R(this, args) => R(this, Object[]) 648 appliedType = appliedType.changeParameterType(2, Object[].class); 649 // drop any extraneous arguments for the apply fail case 650 if (isFailedApplyToCall) { 651 appliedType = appliedType.dropParameterTypes(3, paramCount - 1); 652 } 653 } else { 654 // R(this) => R(this, Object[]) 655 appliedType = appliedType.insertParameterTypes(2, Object[].class); 656 } 657 } 658 659 appliedDesc = appliedDesc.changeMethodType(appliedType); 660 661 // Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation 662 final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()]; 663 appliedArgs[0] = appliedFn; 664 appliedArgs[1] = passesThis ? appliedFnNeedsWrappedThis ? ScriptFunctionData.wrapThis(args[2]) : args[2] : ScriptRuntime.UNDEFINED; 665 if (isApply && !isFailedApplyToCall) { 666 appliedArgs[2] = passesArgs ? NativeFunction.toApplyArgs(args[3]) : ScriptRuntime.EMPTY_ARRAY; 667 } else { 668 if (passesArgs) { 669 if (isFailedApplyToCall) { 670 final Object[] tmp = new Object[args.length - 3]; 671 System.arraycopy(args, 3, tmp, 0, tmp.length); 672 appliedArgs[2] = NativeFunction.toApplyArgs(tmp); 673 } else { 674 assert !isApply; 675 System.arraycopy(args, 3, appliedArgs, 2, args.length - 3); 676 } 677 } else if (isFailedApplyToCall) { 678 appliedArgs[2] = ScriptRuntime.EMPTY_ARRAY; 679 } 680 } 681 682 // Ask the linker machinery for an invocation of the target function 683 final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs); 684 GuardedInvocation appliedInvocation; 685 try { 686 appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest); 687 } catch (final RuntimeException | Error e) { 688 throw e; 689 } catch (final Exception e) { 690 throw new RuntimeException(e); 691 } 692 assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage. 693 694 final Class<?> applyFnType = descType.parameterType(0); 695 MethodHandle inv = appliedInvocation.getInvocation(); //method handle from apply invocation. the applied function invocation 696 697 if (isApply && !isFailedApplyToCall) { 698 if (passesArgs) { 699 // Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it. 700 inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS); 701 } else { 702 // If the original call site doesn't pass argArray, pass in an empty array 703 inv = MH.insertArguments(inv, 2, (Object)ScriptRuntime.EMPTY_ARRAY); 704 } 705 } 706 707 if (isApplyToCall) { 708 if (isFailedApplyToCall) { 709 //take the real arguments that were passed to a call and force them into the apply instead 710 Context.getContextTrusted().getLogger(ApplySpecialization.class).info("Collection arguments to revert call to apply in " + appliedFn); 711 inv = MH.asCollector(inv, Object[].class, realArgCount); 712 } else { 713 appliedInvocation = appliedInvocation.addSwitchPoint(applyToCallSwitchPoint); 714 } 715 } 716 717 if (!passesThis) { 718 // If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed 719 inv = bindImplicitThis(appliedFn, inv); 720 } else if (appliedFnNeedsWrappedThis) { 721 // target function needs a wrapped this, so make sure we filter for that 722 inv = MH.filterArguments(inv, 1, WRAP_THIS); 723 } 724 inv = MH.dropArguments(inv, 0, applyFnType); 725 726 /* 727 * Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which 728 * is when we need to add arguments to the callsite to catch and ignore the synthetic 729 * extra args that someone has added to the command line. 730 */ 731 for (int i = 0; i < dropArgs.parameterCount(); i++) { 732 inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i)); 733 } 734 735 MethodHandle guard = appliedInvocation.getGuard(); 736 // If the guard checks the value of "this" but we aren't passing thisArg, insert the default one 737 if (!passesThis && guard.type().parameterCount() > 1) { 738 guard = bindImplicitThis(appliedFn, guard); 739 } 740 final MethodType guardType = guard.type(); 741 742 // We need to account for the dropped (apply|call) function argument. 743 guard = MH.dropArguments(guard, 0, descType.parameterType(0)); 744 // Take the "isApplyFunction" guard, and bind it to this function. 745 MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); 746 // Adapt the guard to receive all the arguments that the original guard does. 747 applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray()); 748 // Fold the original function guard into our apply guard. 749 guard = MH.foldArguments(applyFnGuard, guard); 750 751 return appliedInvocation.replaceMethods(inv, guard); 752 } 753 754 /* 755 * This method is used for linking nested apply. Specialized apply and call linking will create a variable arity 756 * call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with 757 * Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method. 758 * This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back 759 * to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to 760 * invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity 761 * invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already 762 * solved by createApplyOrCallCall) non-vararg call site linking. 763 */ 764 private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, 765 final LinkRequest request, final Object[] args) { 766 final MethodType descType = desc.getMethodType(); 767 final int paramCount = descType.parameterCount(); 768 final Object[] varArgs = (Object[])args[paramCount - 1]; 769 // -1 'cause we're not passing the vararg array itself 770 final int copiedArgCount = args.length - 1; 771 final int varArgCount = varArgs.length; 772 773 // Spread arguments for the delegate createApplyOrCallCall invocation. 774 final Object[] spreadArgs = new Object[copiedArgCount + varArgCount]; 775 System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount); 776 System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount); 777 778 // Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and 779 // replace it with a list of Object.class. 780 final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes( 781 Collections.<Class<?>>nCopies(varArgCount, Object.class)); 782 final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType); 783 784 // Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/ 785 final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs); 786 final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs); 787 788 // Add spreader combinators to returned invocation and guard. 789 return spreadInvocation.replaceMethods( 790 // Use standard ScriptObject.pairArguments on the invocation 791 pairArguments(spreadInvocation.getInvocation(), descType), 792 // Use our specialized spreadGuardArguments on the guard (see below). 793 spreadGuardArguments(spreadInvocation.getGuard(), descType)); 794 } 795 796 private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) { 797 final MethodType guardType = guard.type(); 798 final int guardParamCount = guardType.parameterCount(); 799 final int descParamCount = descType.parameterCount(); 800 final int spreadCount = guardParamCount - descParamCount + 1; 801 if (spreadCount <= 0) { 802 // Guard doesn't dip into the varargs 803 return guard; 804 } 805 806 final MethodHandle arrayConvertingGuard; 807 // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply 808 // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail 809 // with ClassCastException of NativeArray to Object[]. 810 if(guardType.parameterType(guardParamCount - 1).isArray()) { 811 arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS); 812 } else { 813 arrayConvertingGuard = guard; 814 } 815 816 return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount); 817 } 818 819 private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) { 820 final MethodHandle bound; 821 if(fn instanceof ScriptFunction && ((ScriptFunction)fn).needsWrappedThis()) { 822 bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER); 823 } else { 824 bound = mh; 825 } 826 return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED); 827 } 828 829 /** 830 * Used for noSuchMethod/noSuchProperty and JSAdapter hooks. 831 * 832 * These don't want a callee parameter, so bind that. Name binding is optional. 833 */ 834 MethodHandle getCallMethodHandle(final MethodType type, final String bindName) { 835 return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type); 836 } 837 838 private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) { 839 if (bindName == null) { 840 return methodHandle; 841 } 842 843 // if it is vararg method, we need to extend argument array with 844 // a new zeroth element that is set to bindName value. 845 final MethodType methodType = methodHandle.type(); 846 final int parameterCount = methodType.parameterCount(); 847 final boolean isVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray(); 848 849 if (isVarArg) { 850 return MH.filterArguments(methodHandle, 1, MH.insertArguments(ADD_ZEROTH_ELEMENT, 1, bindName)); 851 } 852 return MH.insertArguments(methodHandle, 1, bindName); 853 } 854 855 /** 856 * Get the guard that checks if a {@link ScriptFunction} is equal to 857 * a known ScriptFunction, using reference comparison 858 * 859 * @param function The ScriptFunction to check against. This will be bound to the guard method handle 860 * 861 * @return method handle for guard 862 */ 863 private static MethodHandle getFunctionGuard(final ScriptFunction function, final int flags) { 864 assert function.data != null; 865 // Built-in functions have a 1-1 correspondence to their ScriptFunctionData, so we can use a cheaper identity 866 // comparison for them. 867 if (function.data.isBuiltin()) { 868 return Guards.getIdentityGuard(function); 869 } 870 return MH.insertArguments(IS_FUNCTION_MH, 1, function.data); 871 } 872 873 /** 874 * Get a guard that checks if a {@link ScriptFunction} is equal to 875 * a known ScriptFunction using reference comparison, and whether the type of 876 * the second argument (this-object) is not a JavaScript primitive type. 877 * 878 * @param function The ScriptFunction to check against. This will be bound to the guard method handle 879 * 880 * @return method handle for guard 881 */ 882 private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) { 883 assert function.data != null; 884 return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data); 885 } 886 887 @SuppressWarnings("unused") 888 private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) { 889 return self instanceof ScriptFunction && ((ScriptFunction)self).data == data; 890 } 891 892 @SuppressWarnings("unused") 893 private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) { 894 return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject; 895 } 896 897 @SuppressWarnings("unused") 898 private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) { 899 // NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call() 900 return appliedFnCondition && self == expectedSelf; 901 } 902 903 @SuppressWarnings("unused") 904 private static Object[] addZerothElement(final Object[] args, final Object value) { 905 // extends input array with by adding new zeroth element 906 final Object[] src = args == null? ScriptRuntime.EMPTY_ARRAY : args; 907 final Object[] result = new Object[src.length + 1]; 908 System.arraycopy(src, 0, result, 1, src.length); 909 result[0] = value; 910 return result; 911 } 912 913 @SuppressWarnings("unused") 914 private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args) 915 throws Throwable { 916 final Object syncObj = sync == UNDEFINED ? self : sync; 917 synchronized (syncObj) { 918 return func.invoke(self, args); 919 } 920 } 921 922 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 923 return MH.findStatic(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types)); 924 } 925 926 private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) { 927 return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types)); 928 } 929} 930 931