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