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