1 /* 2 * Copyright (c) 2010, 2016, 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 package jdk.nashorn.internal.runtime; 26 27 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 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 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 32 33 import java.lang.invoke.MethodHandle; 34 import java.lang.invoke.MethodHandles; 35 import java.lang.invoke.MethodHandles.Lookup; 36 import java.lang.invoke.MethodType; 37 import java.lang.invoke.SwitchPoint; 38 import java.security.AccessControlContext; 39 import java.security.AccessController; 40 import java.security.PrivilegedAction; 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.Collection; 44 import java.util.Collections; 45 import java.util.HashSet; 46 import java.util.List; 47 import java.util.concurrent.atomic.LongAdder; 48 import jdk.dynalink.CallSiteDescriptor; 49 import jdk.dynalink.SecureLookupSupplier; 50 import jdk.dynalink.linker.GuardedInvocation; 51 import jdk.dynalink.linker.LinkRequest; 52 import jdk.dynalink.linker.support.Guards; 53 import jdk.nashorn.internal.codegen.ApplySpecialization; 54 import jdk.nashorn.internal.codegen.Compiler; 55 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 56 import jdk.nashorn.internal.ir.FunctionNode; 57 import jdk.nashorn.internal.objects.Global; 58 import jdk.nashorn.internal.objects.NativeFunction; 59 import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic; 60 import jdk.nashorn.internal.runtime.linker.Bootstrap; 61 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 62 import jdk.nashorn.internal.runtime.logging.DebugLogger; 63 64 /** 65 * Runtime representation of a JavaScript function. This class has only private 66 * and protected constructors. There are no *public* constructors - but only 67 * factory methods that follow the naming pattern "createXYZ". 68 */ 69 public class ScriptFunction extends ScriptObject { 70 71 /** 72 * Method handle for prototype getter for this ScriptFunction 73 */ 74 public static final MethodHandle G$PROTOTYPE = findOwnMH_S("G$prototype", Object.class, Object.class); 75 76 /** 77 * Method handle for prototype setter for this ScriptFunction 78 */ 79 public static final MethodHandle S$PROTOTYPE = findOwnMH_S("S$prototype", void.class, Object.class, Object.class); 80 81 /** 82 * Method handle for length getter for this ScriptFunction 83 */ 84 public static final MethodHandle G$LENGTH = findOwnMH_S("G$length", int.class, Object.class); 85 86 /** 87 * Method handle for name getter for this ScriptFunction 88 */ 89 public static final MethodHandle G$NAME = findOwnMH_S("G$name", Object.class, Object.class); 90 91 /** 92 * Method handle used for implementing sync() in mozilla_compat 93 */ 94 public static final MethodHandle INVOKE_SYNC = findOwnMH_S("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class); 95 96 /** 97 * Method handle for allocate function for this ScriptFunction 98 */ 99 static final MethodHandle ALLOCATE = findOwnMH_V("allocate", Object.class); 100 101 private static final MethodHandle WRAPFILTER = findOwnMH_S("wrapFilter", Object.class, Object.class); 102 103 private static final MethodHandle SCRIPTFUNCTION_GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); 104 105 /** 106 * method handle to scope getter for this ScriptFunction 107 */ 108 public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); 109 110 private static final MethodHandle IS_FUNCTION_MH = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class); 111 112 private static final MethodHandle IS_APPLY_FUNCTION = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class); 113 114 private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH_S("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class); 115 116 private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH_S("addZerothElement", Object[].class, Object[].class, Object.class); 117 118 private static final MethodHandle WRAP_THIS = MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, "wrapThis", MH.type(Object.class, Object.class)); 119 120 // various property maps used for different kinds of functions 121 // property map for anonymous function that serves as Function.prototype 122 private static final PropertyMap anonmap$; 123 // property map for strict mode functions 124 private static final PropertyMap strictmodemap$; 125 // property map for bound functions 126 private static final PropertyMap boundfunctionmap$; 127 // property map for non-strict, non-bound functions. 128 private static final PropertyMap map$; 129 130 // Marker object for lazily initialized prototype object 131 private static final Object LAZY_PROTOTYPE = new Object(); 132 133 private static final AccessControlContext GET_LOOKUP_PERMISSION_CONTEXT = 134 AccessControlContextFactory.createAccessControlContext(SecureLookupSupplier.GET_LOOKUP_PERMISSION_NAME); 135 createStrictModeMap(final PropertyMap map)136 private static PropertyMap createStrictModeMap(final PropertyMap map) { 137 final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; 138 PropertyMap newMap = map; 139 // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors. 140 newMap = newMap.addPropertyNoHistory(newMap.newUserAccessors("arguments", flags)); 141 newMap = newMap.addPropertyNoHistory(newMap.newUserAccessors("caller", flags)); 142 return newMap; 143 } 144 createBoundFunctionMap(final PropertyMap strictModeMap)145 private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) { 146 // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see 147 // ECMAScript 5.1 section 15.3.4.5 148 return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype")); 149 } 150 151 static { 152 anonmap$ = PropertyMap.newMap(); 153 final ArrayList<Property> properties = new ArrayList<>(3); 154 properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE)); 155 properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null)); 156 properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null)); 157 map$ = PropertyMap.newMap(properties); 158 strictmodemap$ = createStrictModeMap(map$); 159 boundfunctionmap$ = createBoundFunctionMap(strictmodemap$); 160 } 161 isStrict(final int flags)162 private static boolean isStrict(final int flags) { 163 return (flags & ScriptFunctionData.IS_STRICT) != 0; 164 } 165 166 // Choose the map based on strict mode! getMap(final boolean strict)167 private static PropertyMap getMap(final boolean strict) { 168 return strict ? strictmodemap$ : map$; 169 } 170 171 /** 172 * The parent scope. 173 */ 174 private final ScriptObject scope; 175 176 private final ScriptFunctionData data; 177 178 /** 179 * The property map used for newly allocated object when function is used as 180 * constructor. 181 */ 182 protected PropertyMap allocatorMap; 183 184 /** 185 * Reference to constructor prototype. 186 */ 187 protected Object prototype; 188 189 /** 190 * Constructor 191 * 192 * @param data static function data 193 * @param map property map 194 * @param scope scope 195 */ ScriptFunction( final ScriptFunctionData data, final PropertyMap map, final ScriptObject scope, final Global global)196 private ScriptFunction( 197 final ScriptFunctionData data, 198 final PropertyMap map, 199 final ScriptObject scope, 200 final Global global) { 201 202 super(map); 203 204 if (Context.DEBUG) { 205 constructorCount.increment(); 206 } 207 208 this.data = data; 209 this.scope = scope; 210 this.setInitialProto(global.getFunctionPrototype()); 211 this.prototype = LAZY_PROTOTYPE; 212 213 // We have to fill user accessor functions late as these are stored 214 // in this object rather than in the PropertyMap of this object. 215 assert objectSpill == null; 216 if (isStrict() || isBoundFunction()) { 217 final ScriptFunction typeErrorThrower = global.getTypeErrorThrower(); 218 initUserAccessors("arguments", typeErrorThrower, typeErrorThrower); 219 initUserAccessors("caller", typeErrorThrower, typeErrorThrower); 220 } 221 } 222 223 /** 224 * Constructor 225 * 226 * @param name function name 227 * @param methodHandle method handle to function (if specializations are 228 * present, assumed to be most generic) 229 * @param map property map 230 * @param scope scope 231 * @param specs specialized version of this function - other method handles 232 * @param flags {@link ScriptFunctionData} flags 233 */ ScriptFunction( final String name, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, final Specialization[] specs, final int flags, final Global global)234 private ScriptFunction( 235 final String name, 236 final MethodHandle methodHandle, 237 final PropertyMap map, 238 final ScriptObject scope, 239 final Specialization[] specs, 240 final int flags, 241 final Global global) { 242 this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope, global); 243 } 244 245 /** 246 * Constructor 247 * 248 * @param name name of function 249 * @param methodHandle handle for invocation 250 * @param scope scope object 251 * @param specs specialized versions of this method, if available, null 252 * otherwise 253 * @param flags {@link ScriptFunctionData} flags 254 */ ScriptFunction( final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags)255 private ScriptFunction( 256 final String name, 257 final MethodHandle methodHandle, 258 final ScriptObject scope, 259 final Specialization[] specs, 260 final int flags) { 261 this(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags, Global.instance()); 262 } 263 264 /** 265 * Constructor called by Nasgen generated code, zero added members, use the 266 * default map. Creates builtin functions only. 267 * 268 * @param name name of function 269 * @param invokeHandle handle for invocation 270 * @param specs specialized versions of this method, if available, null 271 * otherwise 272 */ ScriptFunction(final String name, final MethodHandle invokeHandle, final Specialization[] specs)273 protected ScriptFunction(final String name, final MethodHandle invokeHandle, final Specialization[] specs) { 274 this(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR, Global.instance()); 275 } 276 277 /** 278 * Constructor called by Nasgen generated code, non zero member count, use 279 * the map passed as argument. Creates builtin functions only. 280 * 281 * @param name name of function 282 * @param invokeHandle handle for invocation 283 * @param map initial property map 284 * @param specs specialized versions of this method, if available, null 285 * otherwise 286 */ ScriptFunction(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs)287 protected ScriptFunction(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) { 288 this(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR, Global.instance()); 289 } 290 291 // Factory methods to create various functions 292 /** 293 * Factory method called by compiler generated code for functions that need 294 * parent scope. 295 * 296 * @param constants the generated class' constant array 297 * @param index the index of the {@code RecompilableScriptFunctionData} 298 * object in the constants array. 299 * @param scope the parent scope object 300 * @return a newly created function object 301 */ create(final Object[] constants, final int index, final ScriptObject scope)302 public static ScriptFunction create(final Object[] constants, final int index, final ScriptObject scope) { 303 final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constants[index]; 304 return new ScriptFunction(data, getMap(data.isStrict()), scope, Global.instance()); 305 } 306 307 /** 308 * Factory method called by compiler generated code for functions that don't 309 * need parent scope. 310 * 311 * @param constants the generated class' constant array 312 * @param index the index of the {@code RecompilableScriptFunctionData} 313 * object in the constants array. 314 * @return a newly created function object 315 */ create(final Object[] constants, final int index)316 public static ScriptFunction create(final Object[] constants, final int index) { 317 return create(constants, index, null); 318 } 319 320 /** 321 * Create anonymous function that serves as Function.prototype 322 * 323 * @return anonymous function object 324 */ createAnonymous()325 public static ScriptFunction createAnonymous() { 326 return new ScriptFunction("", GlobalFunctions.ANONYMOUS, anonmap$, null); 327 } 328 329 // builtin function create helper factory createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags)330 private static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) { 331 final ScriptFunction func = new ScriptFunction(name, methodHandle, null, specs, flags); 332 func.setPrototype(UNDEFINED); 333 // Non-constructor built-in functions do not have "prototype" property 334 func.deleteOwnProperty(func.getMap().findProperty("prototype")); 335 336 return func; 337 } 338 339 /** 340 * Factory method for non-constructor built-in functions 341 * 342 * @param name function name 343 * @param methodHandle handle for invocation 344 * @param specs specialized versions of function if available, null 345 * otherwise 346 * @return new ScriptFunction 347 */ createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs)348 public static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs) { 349 return ScriptFunction.createBuiltin(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN); 350 } 351 352 /** 353 * Factory method for non-constructor built-in functions 354 * 355 * @param name function name 356 * @param methodHandle handle for invocation 357 * @return new ScriptFunction 358 */ createBuiltin(final String name, final MethodHandle methodHandle)359 public static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle) { 360 return ScriptFunction.createBuiltin(name, methodHandle, null); 361 } 362 363 /** 364 * Factory method for non-constructor built-in, strict functions 365 * 366 * @param name function name 367 * @param methodHandle handle for invocation 368 * @return new ScriptFunction 369 */ createStrictBuiltin(final String name, final MethodHandle methodHandle)370 public static ScriptFunction createStrictBuiltin(final String name, final MethodHandle methodHandle) { 371 return ScriptFunction.createBuiltin(name, methodHandle, null, ScriptFunctionData.IS_BUILTIN | ScriptFunctionData.IS_STRICT); 372 } 373 374 // Subclass to represent bound functions 375 private static class Bound extends ScriptFunction { 376 private final ScriptFunction target; 377 Bound(final ScriptFunctionData boundData, final ScriptFunction target)378 Bound(final ScriptFunctionData boundData, final ScriptFunction target) { 379 super(boundData, boundfunctionmap$, null, Global.instance()); 380 setPrototype(ScriptRuntime.UNDEFINED); 381 this.target = target; 382 } 383 384 @Override getTargetFunction()385 protected ScriptFunction getTargetFunction() { 386 return target; 387 } 388 } 389 390 /** 391 * Creates a version of this function bound to a specific "self" and other 392 * arguments, as per {@code Function.prototype.bind} functionality in 393 * ECMAScript 5.1 section 15.3.4.5. 394 * 395 * @param self the self to bind to this function. Can be null (in which 396 * case, null is bound as this). 397 * @param args additional arguments to bind to this function. Can be null or 398 * empty to not bind additional arguments. 399 * @return a function with the specified self and parameters bound. 400 */ createBound(final Object self, final Object[] args)401 public final ScriptFunction createBound(final Object self, final Object[] args) { 402 return new Bound(data.makeBoundFunctionData(this, self, args), getTargetFunction()); 403 } 404 405 /** 406 * Create a function that invokes this function synchronized on {@code sync} 407 * or the self object of the invocation. 408 * 409 * @param sync the Object to synchronize on, or undefined 410 * @return synchronized function 411 */ createSynchronized(final Object sync)412 public final ScriptFunction createSynchronized(final Object sync) { 413 final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync); 414 return createBuiltin(getName(), mh); 415 } 416 417 @Override getClassName()418 public String getClassName() { 419 return "Function"; 420 } 421 422 /** 423 * ECMA 15.3.5.3 [[HasInstance]] (V) Step 3 if "prototype" value is not an 424 * Object, throw TypeError 425 */ 426 @Override isInstance(final ScriptObject instance)427 public boolean isInstance(final ScriptObject instance) { 428 final Object basePrototype = getTargetFunction().getPrototype(); 429 if (!(basePrototype instanceof ScriptObject)) { 430 throw typeError("prototype.not.an.object", ScriptRuntime.safeToString(getTargetFunction()), ScriptRuntime.safeToString(basePrototype)); 431 } 432 433 for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) { 434 if (proto == basePrototype) { 435 return true; 436 } 437 } 438 439 return false; 440 } 441 442 /** 443 * Returns the target function for this function. If the function was not 444 * created using {@link #createBound(Object, Object[])}, its target 445 * function is itself. If it is bound, its target function is the target 446 * function of the function it was made from (therefore, the target function 447 * is always the final, unbound recipient of the calls). 448 * 449 * @return the target function for this function. 450 */ getTargetFunction()451 protected ScriptFunction getTargetFunction() { 452 return this; 453 } 454 isBoundFunction()455 final boolean isBoundFunction() { 456 return getTargetFunction() != this; 457 } 458 459 /** 460 * Set the arity of this ScriptFunction 461 * 462 * @param arity arity 463 */ setArity(final int arity)464 public final void setArity(final int arity) { 465 data.setArity(arity); 466 } 467 468 /** 469 * Is this a ECMAScript 'use strict' function? 470 * 471 * @return true if function is in strict mode 472 */ isStrict()473 public final boolean isStrict() { 474 return data.isStrict(); 475 } 476 477 /** 478 * Is this is a function with all variables in scope? 479 * @return true if function has all 480 */ hasAllVarsInScope()481 public boolean hasAllVarsInScope() { 482 return data instanceof RecompilableScriptFunctionData && 483 (((RecompilableScriptFunctionData) data).getFunctionFlags() & FunctionNode.HAS_ALL_VARS_IN_SCOPE) != 0; 484 } 485 486 /** 487 * Returns true if this is a non-strict, non-built-in function that requires 488 * non-primitive this argument according to ECMA 10.4.3. 489 * 490 * @return true if this argument must be an object 491 */ needsWrappedThis()492 public final boolean needsWrappedThis() { 493 return data.needsWrappedThis(); 494 } 495 needsWrappedThis(final Object fn)496 private static boolean needsWrappedThis(final Object fn) { 497 return fn instanceof ScriptFunction ? ((ScriptFunction) fn).needsWrappedThis() : false; 498 } 499 500 /** 501 * Execute this script function. 502 * 503 * @param self Target object. 504 * @param arguments Call arguments. 505 * @return ScriptFunction result. 506 * @throws Throwable if there is an exception/error with the invocation or 507 * thrown from it 508 */ invoke(final Object self, final Object... arguments)509 final Object invoke(final Object self, final Object... arguments) throws Throwable { 510 if (Context.DEBUG) { 511 invokes.increment(); 512 } 513 return data.invoke(this, self, arguments); 514 } 515 516 /** 517 * Execute this script function as a constructor. 518 * 519 * @param arguments Call arguments. 520 * @return Newly constructed result. 521 * @throws Throwable if there is an exception/error with the invocation or 522 * thrown from it 523 */ construct(final Object... arguments)524 final Object construct(final Object... arguments) throws Throwable { 525 return data.construct(this, arguments); 526 } 527 528 /** 529 * Allocate function. Called from generated {@link ScriptObject} code for 530 * allocation as a factory method 531 * 532 * @return a new instance of the {@link ScriptObject} whose allocator this 533 * is 534 */ 535 @SuppressWarnings("unused") allocate()536 private Object allocate() { 537 if (Context.DEBUG) { 538 allocations.increment(); 539 } 540 541 assert !isBoundFunction(); // allocate never invoked on bound functions 542 543 final ScriptObject prototype = getAllocatorPrototype(); 544 final ScriptObject object = data.allocate(getAllocatorMap(prototype)); 545 546 if (object != null) { 547 object.setInitialProto(prototype); 548 } 549 550 return object; 551 } 552 553 /** 554 * Get the property map used by "allocate" 555 * @param prototype actual prototype object 556 * @return property map 557 */ getAllocatorMap(final ScriptObject prototype)558 private PropertyMap getAllocatorMap(final ScriptObject prototype) { 559 if (allocatorMap == null || allocatorMap.isInvalidSharedMapFor(prototype)) { 560 // The prototype map has changed since this function was last used as constructor. 561 // Get a new allocator map. 562 allocatorMap = data.getAllocatorMap(prototype); 563 } 564 return allocatorMap; 565 } 566 567 /** 568 * Return the actual prototype used by "allocate" 569 * @return allocator prototype 570 */ getAllocatorPrototype()571 private ScriptObject getAllocatorPrototype() { 572 final Object prototype = getPrototype(); 573 if (prototype instanceof ScriptObject) { 574 return (ScriptObject) prototype; 575 } 576 return Global.objectPrototype(); 577 } 578 579 @Override safeToString()580 public final String safeToString() { 581 return toSource(); 582 } 583 584 @Override toString()585 public final String toString() { 586 return data.toString(); 587 } 588 589 /** 590 * Get this function as a String containing its source code. If no source 591 * code exists in this ScriptFunction, its contents will be displayed as 592 * {@code [native code]} 593 * 594 * @return string representation of this function's source 595 */ toSource()596 public final String toSource() { 597 return data.toSource(); 598 } 599 600 /** 601 * Get the prototype object for this function 602 * 603 * @return prototype 604 */ getPrototype()605 public final Object getPrototype() { 606 if (prototype == LAZY_PROTOTYPE) { 607 prototype = new PrototypeObject(this); 608 } 609 return prototype; 610 } 611 612 /** 613 * Set the prototype object for this function 614 * 615 * @param newPrototype new prototype object 616 */ setPrototype(final Object newPrototype)617 public final void setPrototype(final Object newPrototype) { 618 if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) { 619 // Unset allocator map to be replaced with one matching the new prototype. 620 allocatorMap = null; 621 } 622 this.prototype = newPrototype; 623 } 624 625 /** 626 * Return the invoke handle bound to a given ScriptObject self reference. If 627 * callee parameter is required result is rebound to this. 628 * 629 * @param self self reference 630 * @return bound invoke handle 631 */ getBoundInvokeHandle(final Object self)632 public final MethodHandle getBoundInvokeHandle(final Object self) { 633 return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), self); 634 } 635 636 /** 637 * Bind the method handle to this {@code ScriptFunction} instance if it 638 * needs a callee parameter. If this function's method handles don't have a 639 * callee parameter, the handle is returned unchanged. 640 * 641 * @param methodHandle the method handle to potentially bind to this 642 * function instance. 643 * @return the potentially bound method handle 644 */ bindToCalleeIfNeeded(final MethodHandle methodHandle)645 private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) { 646 return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle; 647 648 } 649 650 /** 651 * Get the documentation for this function 652 * 653 * @return the documentation 654 */ getDocumentation()655 public final String getDocumentation() { 656 return data.getDocumentation(); 657 } 658 659 /** 660 * Get the documentation key for this function 661 * 662 * @return the documentation key 663 */ getDocumentationKey()664 public final String getDocumentationKey() { 665 return data.getDocumentationKey(); 666 } 667 668 /** 669 * Set the documentation key for this function 670 * 671 * @param docKey documentation key String for this function 672 */ setDocumentationKey(final String docKey)673 public final void setDocumentationKey(final String docKey) { 674 data.setDocumentationKey(docKey); 675 } 676 677 /** 678 * Get the name for this function 679 * 680 * @return the name 681 */ getName()682 public final String getName() { 683 return data.getName(); 684 } 685 686 /** 687 * Get the scope for this function 688 * 689 * @return the scope 690 */ getScope()691 public final ScriptObject getScope() { 692 return scope; 693 } 694 695 /** 696 * Prototype getter for this ScriptFunction - follows the naming convention 697 * used by Nasgen and the code generator 698 * 699 * @param self self reference 700 * @return self's prototype 701 */ G$prototype(final Object self)702 public static Object G$prototype(final Object self) { 703 return self instanceof ScriptFunction 704 ? ((ScriptFunction) self).getPrototype() 705 : UNDEFINED; 706 } 707 708 /** 709 * Prototype setter for this ScriptFunction - follows the naming convention 710 * used by Nasgen and the code generator 711 * 712 * @param self self reference 713 * @param prototype prototype to set 714 */ S$prototype(final Object self, final Object prototype)715 public static void S$prototype(final Object self, final Object prototype) { 716 if (self instanceof ScriptFunction) { 717 ((ScriptFunction) self).setPrototype(prototype); 718 } 719 } 720 721 /** 722 * Length getter - ECMA 15.3.3.2: Function.length 723 * 724 * @param self self reference 725 * @return length 726 */ G$length(final Object self)727 public static int G$length(final Object self) { 728 if (self instanceof ScriptFunction) { 729 return ((ScriptFunction) self).data.getArity(); 730 } 731 732 return 0; 733 } 734 735 /** 736 * Name getter - ECMA Function.name 737 * 738 * @param self self reference 739 * @return the name, or undefined if none 740 */ G$name(final Object self)741 public static Object G$name(final Object self) { 742 if (self instanceof ScriptFunction) { 743 return ((ScriptFunction) self).getName(); 744 } 745 746 return UNDEFINED; 747 } 748 749 /** 750 * Get the prototype for this ScriptFunction 751 * 752 * @param constructor constructor 753 * @return prototype, or null if given constructor is not a ScriptFunction 754 */ getPrototype(final ScriptFunction constructor)755 public static ScriptObject getPrototype(final ScriptFunction constructor) { 756 if (constructor != null) { 757 final Object proto = constructor.getPrototype(); 758 if (proto instanceof ScriptObject) { 759 return (ScriptObject) proto; 760 } 761 } 762 763 return null; 764 } 765 766 // These counters are updated only in debug mode. 767 private static LongAdder constructorCount; 768 private static LongAdder invokes; 769 private static LongAdder allocations; 770 771 static { 772 if (Context.DEBUG) { 773 constructorCount = new LongAdder(); 774 invokes = new LongAdder(); 775 allocations = new LongAdder(); 776 } 777 } 778 779 /** 780 * @return the constructorCount 781 */ getConstructorCount()782 public static long getConstructorCount() { 783 return constructorCount.longValue(); 784 } 785 786 /** 787 * @return the invokes 788 */ getInvokes()789 public static long getInvokes() { 790 return invokes.longValue(); 791 } 792 793 /** 794 * @return the allocations 795 */ getAllocations()796 public static long getAllocations() { 797 return allocations.longValue(); 798 } 799 800 @Override findNewMethod(final CallSiteDescriptor desc, final LinkRequest request)801 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) { 802 final MethodType type = desc.getMethodType(); 803 assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc); 804 final CompiledFunction cf = data.getBestConstructor(type, scope, CompiledFunction.NO_FUNCTIONS); 805 final GuardedInvocation bestCtorInv = cf.createConstructorInvocation(); 806 //TODO - ClassCastException 807 return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null); 808 } 809 wrapFilter(final Object obj)810 private static Object wrapFilter(final Object obj) { 811 if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) { 812 return obj; 813 } 814 return Context.getGlobal().wrapAsObject(obj); 815 } 816 817 @SuppressWarnings("unused") globalFilter(final Object object)818 private static Object globalFilter(final Object object) { 819 // replace whatever we get with the current global object 820 return Context.getGlobal(); 821 } 822 823 /** 824 * Some receivers are primitive, in that case, according to the Spec we 825 * create a new native object per callsite with the wrap filter. We can only 826 * apply optimistic builtins if there is no per instance state saved for 827 * these wrapped objects (e.g. currently NativeStrings), otherwise we can't 828 * create optimistic versions 829 * 830 * @param self receiver 831 * @param linkLogicClass linkLogicClass, or null if no link logic exists 832 * @return link logic instance, or null if one could not be constructed for 833 * this receiver 834 */ getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass)835 private static LinkLogic getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass) { 836 if (linkLogicClass == null) { 837 return LinkLogic.EMPTY_INSTANCE; //always OK to link this, specialization but without special linking logic 838 } 839 840 if (!Context.getContextTrusted().getEnv()._optimistic_types) { 841 return null; //if optimistic types are off, optimistic builtins are too 842 } 843 844 final Object wrappedSelf = wrapFilter(self); 845 if (wrappedSelf instanceof OptimisticBuiltins) { 846 if (wrappedSelf != self && ((OptimisticBuiltins) wrappedSelf).hasPerInstanceAssumptions()) { 847 return null; //pessimistic - we created a wrapped object different from the primitive, but the assumptions have instance state 848 } 849 return ((OptimisticBuiltins) wrappedSelf).getLinkLogic(linkLogicClass); 850 } 851 return null; 852 } 853 854 /** 855 * StandardOperation.CALL call site signature: (callee, thiz, [args...]) generated method 856 * signature: (callee, thiz, [args...]) 857 * 858 * cases: 859 * (a) method has callee parameter 860 * (1) for local/scope calls, we just bind thiz and drop the second argument. 861 * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. 862 * (b) method doesn't have callee parameter (builtin functions) 863 * (3) for local/scope calls, bind thiz and drop both callee and thiz. 864 * (4) for normal this-calls, drop callee. 865 * 866 * @return guarded invocation for call 867 */ 868 @Override findCallMethod(final CallSiteDescriptor desc, final LinkRequest request)869 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 870 final MethodType type = desc.getMethodType(); 871 872 final String name = getName(); 873 final boolean isUnstable = request.isCallSiteUnstable(); 874 final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); 875 final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name); 876 final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name); 877 878 final boolean isApplyOrCall = isCall | isApply; 879 880 if (isUnstable && !isApplyOrCall) { 881 //megamorphic - replace call with apply 882 final MethodHandle handle; 883 //ensure that the callsite is vararg so apply can consume it 884 if (type.parameterCount() == 3 && type.parameterType(2) == Object[].class) { 885 // Vararg call site 886 handle = ScriptRuntime.APPLY.methodHandle(); 887 } else { 888 // (callee, this, args...) => (callee, this, args[]) 889 handle = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2); 890 } 891 892 // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a 893 // generic "is this a ScriptFunction?" guard. 894 return new GuardedInvocation( 895 handle, 896 null, 897 (SwitchPoint) null, 898 ClassCastException.class); 899 } 900 901 MethodHandle boundHandle; 902 MethodHandle guard = null; 903 904 // Special handling of Function.apply and Function.call. Note we must be invoking 905 if (isApplyOrCall && !isUnstable) { 906 final Object[] args = request.getArguments(); 907 if (Bootstrap.isCallable(args[1])) { 908 return createApplyOrCallCall(isApply, desc, request, args); 909 } 910 } //else just fall through and link as ordinary function or unstable apply 911 912 int programPoint = INVALID_PROGRAM_POINT; 913 if (NashornCallSiteDescriptor.isOptimistic(desc)) { 914 programPoint = NashornCallSiteDescriptor.getProgramPoint(desc); 915 } 916 917 CompiledFunction cf = data.getBestInvoker(type, scope, CompiledFunction.NO_FUNCTIONS); 918 final Object self = request.getArguments()[1]; 919 final Collection<CompiledFunction> forbidden = new HashSet<>(); 920 921 //check for special fast versions of the compiled function 922 final List<SwitchPoint> sps = new ArrayList<>(); 923 Class<? extends Throwable> exceptionGuard = null; 924 925 while (cf.isSpecialization()) { 926 final Class<? extends LinkLogic> linkLogicClass = cf.getLinkLogicClass(); 927 //if linklogic is null, we can always link with the standard mechanism, it's still a specialization 928 final LinkLogic linkLogic = getLinkLogic(self, linkLogicClass); 929 930 if (linkLogic != null && linkLogic.checkLinkable(self, desc, request)) { 931 final DebugLogger log = Context.getContextTrusted().getLogger(Compiler.class); 932 933 if (log.isEnabled()) { 934 log.info("Linking optimistic builtin function: '", name, "' args=", Arrays.toString(request.getArguments()), " desc=", desc); 935 } 936 937 exceptionGuard = linkLogic.getRelinkException(); 938 939 break; 940 } 941 942 //could not link this specialization because link check failed 943 forbidden.add(cf); 944 final CompiledFunction oldCf = cf; 945 cf = data.getBestInvoker(type, scope, forbidden); 946 assert oldCf != cf; 947 } 948 949 final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint); 950 final MethodHandle callHandle = bestInvoker.getInvocation(); 951 952 if (data.needsCallee()) { 953 if (scopeCall && needsWrappedThis()) { 954 // (callee, this, args...) => (callee, [this], args...) 955 boundHandle = MH.filterArguments(callHandle, 1, SCRIPTFUNCTION_GLOBALFILTER); 956 } else { 957 // It's already (callee, this, args...), just what we need 958 boundHandle = callHandle; 959 } 960 } else if (data.isBuiltin() && Global.isBuiltInJavaExtend(this)) { 961 // We're binding the current lookup as "self" so the function can do 962 // security-sensitive creation of adapter classes. 963 boundHandle = MH.dropArguments(MH.bindTo(callHandle, getLookupPrivileged(desc)), 0, type.parameterType(0), type.parameterType(1)); 964 } else if (data.isBuiltin() && Global.isBuiltInJavaTo(this)) { 965 // We're binding the current call site descriptor as "self" so the function can do 966 // security-sensitive creation of adapter classes. 967 boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc), 0, type.parameterType(0), type.parameterType(1)); 968 } else if (scopeCall && needsWrappedThis()) { 969 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined 970 // (this, args...) => ([this], args...) 971 boundHandle = MH.filterArguments(callHandle, 0, SCRIPTFUNCTION_GLOBALFILTER); 972 // ([this], args...) => ([callee], [this], args...) 973 boundHandle = MH.dropArguments(boundHandle, 0, type.parameterType(0)); 974 } else { 975 // (this, args...) => ([callee], this, args...) 976 boundHandle = MH.dropArguments(callHandle, 0, type.parameterType(0)); 977 } 978 979 // For non-strict functions, check whether this-object is primitive type. 980 // If so add a to-object-wrapper argument filter. 981 // Else install a guard that will trigger a relink when the argument becomes primitive. 982 if (!scopeCall && needsWrappedThis()) { 983 if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) { 984 boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER); 985 } else { 986 guard = getNonStrictFunctionGuard(this); 987 } 988 } 989 990 // Is this an unstable callsite which was earlier apply-to-call optimized? 991 // If so, earlier apply2call would have exploded arguments. We have to convert 992 // that as an array again! 993 if (isUnstable && NashornCallSiteDescriptor.isApplyToCall(desc)) { 994 boundHandle = MH.asCollector(boundHandle, Object[].class, type.parameterCount() - 2); 995 } 996 997 boundHandle = pairArguments(boundHandle, type); 998 999 if (bestInvoker.getSwitchPoints() != null) { 1000 sps.addAll(Arrays.asList(bestInvoker.getSwitchPoints())); 1001 } 1002 final SwitchPoint[] spsArray = sps.isEmpty() ? null : sps.toArray(new SwitchPoint[0]); 1003 1004 return new GuardedInvocation( 1005 boundHandle, 1006 guard == null ? 1007 getFunctionGuard( 1008 this, 1009 cf.getFlags()) : 1010 guard, 1011 spsArray, 1012 exceptionGuard); 1013 } 1014 getLookupPrivileged(final CallSiteDescriptor desc)1015 private static Lookup getLookupPrivileged(final CallSiteDescriptor desc) { 1016 // NOTE: we'd rather not make NashornCallSiteDescriptor.getLookupPrivileged public. 1017 return AccessController.doPrivileged((PrivilegedAction<Lookup>)()->desc.getLookup(), 1018 GET_LOOKUP_PERMISSION_CONTEXT); 1019 } 1020 createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args)1021 private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) { 1022 final MethodType descType = desc.getMethodType(); 1023 final int paramCount = descType.parameterCount(); 1024 if (descType.parameterType(paramCount - 1).isArray()) { 1025 // This is vararg invocation of apply or call. This can normally only happen when we do a recursive 1026 // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate 1027 // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader. 1028 return createVarArgApplyOrCallCall(isApply, desc, request, args); 1029 } 1030 1031 final boolean passesThis = paramCount > 2; 1032 final boolean passesArgs = paramCount > 3; 1033 final int realArgCount = passesArgs ? paramCount - 3 : 0; 1034 1035 final Object appliedFn = args[1]; 1036 final boolean appliedFnNeedsWrappedThis = needsWrappedThis(appliedFn); 1037 1038 //box call back to apply 1039 CallSiteDescriptor appliedDesc = desc; 1040 final SwitchPoint applyToCallSwitchPoint = Global.getBuiltinFunctionApplySwitchPoint(); 1041 //enough to change the proto switchPoint here 1042 1043 final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc); 1044 final boolean isFailedApplyToCall = isApplyToCall && applyToCallSwitchPoint.hasBeenInvalidated(); 1045 1046 // R(apply|call, ...) => R(...) 1047 MethodType appliedType = descType.dropParameterTypes(0, 1); 1048 if (!passesThis) { 1049 // R() => R(this) 1050 appliedType = appliedType.insertParameterTypes(1, Object.class); 1051 } else if (appliedFnNeedsWrappedThis) { 1052 appliedType = appliedType.changeParameterType(1, Object.class); 1053 } 1054 1055 /* 1056 * dropArgs is a synthetic method handle that contains any args that we need to 1057 * get rid of that come after the arguments array in the apply case. We adapt 1058 * the callsite to ask for 3 args only and then dropArguments on the method handle 1059 * to make it fit the extraneous args. 1060 */ 1061 MethodType dropArgs = MH.type(void.class); 1062 if (isApply && !isFailedApplyToCall) { 1063 final int pc = appliedType.parameterCount(); 1064 for (int i = 3; i < pc; i++) { 1065 dropArgs = dropArgs.appendParameterTypes(appliedType.parameterType(i)); 1066 } 1067 if (pc > 3) { 1068 appliedType = appliedType.dropParameterTypes(3, pc); 1069 } 1070 } 1071 1072 if (isApply || isFailedApplyToCall) { 1073 if (passesArgs) { 1074 // R(this, args) => R(this, Object[]) 1075 appliedType = appliedType.changeParameterType(2, Object[].class); 1076 // drop any extraneous arguments for the apply fail case 1077 if (isFailedApplyToCall) { 1078 appliedType = appliedType.dropParameterTypes(3, paramCount - 1); 1079 } 1080 } else { 1081 // R(this) => R(this, Object[]) 1082 appliedType = appliedType.insertParameterTypes(2, Object[].class); 1083 } 1084 } 1085 1086 appliedDesc = appliedDesc.changeMethodType(appliedType); //no extra args 1087 1088 // Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation 1089 final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()]; 1090 appliedArgs[0] = appliedFn; 1091 appliedArgs[1] = passesThis ? appliedFnNeedsWrappedThis ? ScriptFunctionData.wrapThis(args[2]) : args[2] : ScriptRuntime.UNDEFINED; 1092 if (isApply && !isFailedApplyToCall) { 1093 appliedArgs[2] = passesArgs ? NativeFunction.toApplyArgs(args[3]) : ScriptRuntime.EMPTY_ARRAY; 1094 } else { 1095 if (passesArgs) { 1096 if (isFailedApplyToCall) { 1097 final Object[] tmp = new Object[args.length - 3]; 1098 System.arraycopy(args, 3, tmp, 0, tmp.length); 1099 appliedArgs[2] = NativeFunction.toApplyArgs(tmp); 1100 } else { 1101 assert !isApply; 1102 System.arraycopy(args, 3, appliedArgs, 2, args.length - 3); 1103 } 1104 } else if (isFailedApplyToCall) { 1105 appliedArgs[2] = ScriptRuntime.EMPTY_ARRAY; 1106 } 1107 } 1108 1109 // Ask the linker machinery for an invocation of the target function 1110 final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs); 1111 1112 GuardedInvocation appliedInvocation; 1113 try { 1114 appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest); 1115 } catch (final RuntimeException | Error e) { 1116 throw e; 1117 } catch (final Exception e) { 1118 throw new RuntimeException(e); 1119 } 1120 assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage. 1121 1122 final Class<?> applyFnType = descType.parameterType(0); 1123 // Invocation and guard handles from apply invocation. 1124 MethodHandle inv = appliedInvocation.getInvocation(); 1125 MethodHandle guard = appliedInvocation.getGuard(); 1126 1127 if (isApply && !isFailedApplyToCall) { 1128 if (passesArgs) { 1129 // Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it. 1130 inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS); 1131 // Some guards (non-strict functions with non-primitive this) have a this-object parameter, so we 1132 // need to apply this transformations to them as well. 1133 if (guard.type().parameterCount() > 2) { 1134 guard = MH.filterArguments(guard, 2, NativeFunction.TO_APPLY_ARGS); 1135 } 1136 } else { 1137 // If the original call site doesn't pass argArray, pass in an empty array 1138 inv = MH.insertArguments(inv, 2, (Object) ScriptRuntime.EMPTY_ARRAY); 1139 } 1140 } 1141 1142 if (isApplyToCall) { 1143 if (isFailedApplyToCall) { 1144 //take the real arguments that were passed to a call and force them into the apply instead 1145 Context.getContextTrusted().getLogger(ApplySpecialization.class).info("Collection arguments to revert call to apply in " + appliedFn); 1146 inv = MH.asCollector(inv, Object[].class, realArgCount); 1147 } else { 1148 appliedInvocation = appliedInvocation.addSwitchPoint(applyToCallSwitchPoint); 1149 } 1150 } 1151 1152 if (!passesThis) { 1153 // If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed 1154 inv = bindImplicitThis(appliedFnNeedsWrappedThis, inv); 1155 // guard may have this-parameter that needs to be inserted 1156 if (guard.type().parameterCount() > 1) { 1157 guard = bindImplicitThis(appliedFnNeedsWrappedThis, guard); 1158 } 1159 } else if (appliedFnNeedsWrappedThis) { 1160 // target function needs a wrapped this, so make sure we filter for that 1161 inv = MH.filterArguments(inv, 1, WRAP_THIS); 1162 // guard may have this-parameter that needs to be wrapped 1163 if (guard.type().parameterCount() > 1) { 1164 guard = MH.filterArguments(guard, 1, WRAP_THIS); 1165 } 1166 } 1167 1168 final MethodType guardType = guard.type(); // Needed for combining guards below 1169 1170 // We need to account for the dropped (apply|call) function argument. 1171 inv = MH.dropArguments(inv, 0, applyFnType); 1172 guard = MH.dropArguments(guard, 0, applyFnType); 1173 1174 /* 1175 * Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which 1176 * is when we need to add arguments to the callsite to catch and ignore the synthetic 1177 * extra args that someone has added to the command line. 1178 */ 1179 for (int i = 0; i < dropArgs.parameterCount(); i++) { 1180 inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i)); 1181 } 1182 1183 // Take the "isApplyFunction" guard, and bind it to this function. 1184 MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); //TODO replace this with switchpoint 1185 // Adapt the guard to receive all the arguments that the original guard does. 1186 applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray()); 1187 // Fold the original function guard into our apply guard. 1188 guard = MH.foldArguments(applyFnGuard, guard); 1189 1190 return appliedInvocation.replaceMethods(inv, guard); 1191 } 1192 1193 /* 1194 * This method is used for linking nested apply. Specialized apply and call linking will create a variable arity 1195 * call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with 1196 * Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method. 1197 * This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back 1198 * to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to 1199 * invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity 1200 * invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already 1201 * solved by createApplyOrCallCall) non-vararg call site linking. 1202 */ createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args)1203 private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, 1204 final LinkRequest request, final Object[] args) { 1205 final MethodType descType = desc.getMethodType(); 1206 final int paramCount = descType.parameterCount(); 1207 final Object[] varArgs = (Object[]) args[paramCount - 1]; 1208 // -1 'cause we're not passing the vararg array itself 1209 final int copiedArgCount = args.length - 1; 1210 final int varArgCount = varArgs.length; 1211 1212 // Spread arguments for the delegate createApplyOrCallCall invocation. 1213 final Object[] spreadArgs = new Object[copiedArgCount + varArgCount]; 1214 System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount); 1215 System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount); 1216 1217 // Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and 1218 // replace it with a list of Object.class. 1219 final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes( 1220 Collections.<Class<?>>nCopies(varArgCount, Object.class)); 1221 final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType); 1222 1223 // Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/ 1224 final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs); 1225 final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs); 1226 1227 // Add spreader combinators to returned invocation and guard. 1228 return spreadInvocation.replaceMethods( 1229 // Use standard ScriptObject.pairArguments on the invocation 1230 pairArguments(spreadInvocation.getInvocation(), descType), 1231 // Use our specialized spreadGuardArguments on the guard (see below). 1232 spreadGuardArguments(spreadInvocation.getGuard(), descType)); 1233 } 1234 spreadGuardArguments(final MethodHandle guard, final MethodType descType)1235 private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) { 1236 final MethodType guardType = guard.type(); 1237 final int guardParamCount = guardType.parameterCount(); 1238 final int descParamCount = descType.parameterCount(); 1239 final int spreadCount = guardParamCount - descParamCount + 1; 1240 if (spreadCount <= 0) { 1241 // Guard doesn't dip into the varargs 1242 return guard; 1243 } 1244 1245 final MethodHandle arrayConvertingGuard; 1246 // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply 1247 // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail 1248 // with ClassCastException of NativeArray to Object[]. 1249 if (guardType.parameterType(guardParamCount - 1).isArray()) { 1250 arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS); 1251 } else { 1252 arrayConvertingGuard = guard; 1253 } 1254 1255 return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount); 1256 } 1257 bindImplicitThis(final boolean needsWrappedThis, final MethodHandle mh)1258 private static MethodHandle bindImplicitThis(final boolean needsWrappedThis, final MethodHandle mh) { 1259 final MethodHandle bound; 1260 if (needsWrappedThis) { 1261 bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER); 1262 } else { 1263 bound = mh; 1264 } 1265 return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED); 1266 } 1267 1268 /** 1269 * Used for noSuchMethod/noSuchProperty and JSAdapter hooks. 1270 * 1271 * These don't want a callee parameter, so bind that. Name binding is 1272 * optional. 1273 */ getCallMethodHandle(final MethodType type, final String bindName)1274 MethodHandle getCallMethodHandle(final MethodType type, final String bindName) { 1275 return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type); 1276 } 1277 bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName)1278 private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) { 1279 if (bindName == null) { 1280 return methodHandle; 1281 } 1282 1283 // if it is vararg method, we need to extend argument array with 1284 // a new zeroth element that is set to bindName value. 1285 final MethodType methodType = methodHandle.type(); 1286 final int parameterCount = methodType.parameterCount(); 1287 1288 if (parameterCount < 2) { 1289 return methodHandle; // method does not have enough parameters 1290 } 1291 final boolean isVarArg = methodType.parameterType(parameterCount - 1).isArray(); 1292 1293 if (isVarArg) { 1294 return MH.filterArguments(methodHandle, 1, MH.insertArguments(ADD_ZEROTH_ELEMENT, 1, bindName)); 1295 } 1296 return MH.insertArguments(methodHandle, 1, bindName); 1297 } 1298 1299 /** 1300 * Get the guard that checks if a {@link ScriptFunction} is equal to a known 1301 * ScriptFunction, using reference comparison 1302 * 1303 * @param function The ScriptFunction to check against. This will be bound 1304 * to the guard method handle 1305 * 1306 * @return method handle for guard 1307 */ getFunctionGuard(final ScriptFunction function, final int flags)1308 private static MethodHandle getFunctionGuard(final ScriptFunction function, final int flags) { 1309 assert function.data != null; 1310 // Built-in functions have a 1-1 correspondence to their ScriptFunctionData, so we can use a cheaper identity 1311 // comparison for them. 1312 if (function.data.isBuiltin()) { 1313 return Guards.getIdentityGuard(function); 1314 } 1315 return MH.insertArguments(IS_FUNCTION_MH, 1, function.data); 1316 } 1317 1318 /** 1319 * Get a guard that checks if a {@link ScriptFunction} is equal to a known 1320 * ScriptFunction using reference comparison, and whether the type of the 1321 * second argument (this-object) is not a JavaScript primitive type. 1322 * 1323 * @param function The ScriptFunction to check against. This will be bound 1324 * to the guard method handle 1325 * 1326 * @return method handle for guard 1327 */ getNonStrictFunctionGuard(final ScriptFunction function)1328 private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) { 1329 assert function.data != null; 1330 return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data); 1331 } 1332 1333 @SuppressWarnings("unused") isFunctionMH(final Object self, final ScriptFunctionData data)1334 private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) { 1335 return self instanceof ScriptFunction && ((ScriptFunction) self).data == data; 1336 } 1337 1338 @SuppressWarnings("unused") isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data)1339 private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) { 1340 return self instanceof ScriptFunction && ((ScriptFunction) self).data == data && arg instanceof ScriptObject; 1341 } 1342 1343 //TODO this can probably be removed given that we have builtin switchpoints in the context 1344 @SuppressWarnings("unused") isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf)1345 private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) { 1346 // NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call() 1347 return appliedFnCondition && self == expectedSelf; 1348 } 1349 1350 @SuppressWarnings("unused") addZerothElement(final Object[] args, final Object value)1351 private static Object[] addZerothElement(final Object[] args, final Object value) { 1352 // extends input array with by adding new zeroth element 1353 final Object[] src = args == null ? ScriptRuntime.EMPTY_ARRAY : args; 1354 final Object[] result = new Object[src.length + 1]; 1355 System.arraycopy(src, 0, result, 1, src.length); 1356 result[0] = value; 1357 return result; 1358 } 1359 1360 @SuppressWarnings("unused") invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args)1361 private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args) 1362 throws Throwable { 1363 final Object syncObj = sync == UNDEFINED ? self : sync; 1364 synchronized (syncObj) { 1365 return func.invoke(self, args); 1366 } 1367 } 1368 findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types)1369 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 1370 return MH.findStatic(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types)); 1371 } 1372 findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types)1373 private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) { 1374 return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types)); 1375 } 1376 } 1377