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