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