1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.nashorn.internal.runtime;
  26 
  27 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
  28 import static jdk.nashorn.internal.lookup.Lookup.MH;
  29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  31 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
  32 import java.lang.invoke.MethodHandle;
  33 import java.lang.invoke.MethodHandles;
  34 import java.lang.invoke.MethodHandles.Lookup;
  35 import java.lang.invoke.MethodType;
  36 import java.lang.invoke.SwitchPoint;
  37 import java.security.AccessControlContext;
  38 import java.security.AccessController;
  39 import java.security.PrivilegedAction;
  40 import java.util.ArrayList;
  41 import java.util.Arrays;
  42 import java.util.Collection;
  43 import java.util.Collections;
  44 import java.util.HashSet;
  45 import java.util.List;
  46 import java.util.concurrent.atomic.LongAdder;
  47 import jdk.dynalink.CallSiteDescriptor;
  48 import jdk.dynalink.linker.GuardedInvocation;
  49 import jdk.dynalink.linker.LinkRequest;
  50 import jdk.dynalink.linker.support.Guards;
  51 import jdk.nashorn.internal.codegen.ApplySpecialization;
  52 import jdk.nashorn.internal.codegen.Compiler;
  53 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  54 import jdk.nashorn.internal.ir.FunctionNode;
  55 import jdk.nashorn.internal.objects.Global;
  56 import jdk.nashorn.internal.objects.NativeFunction;
  57 import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
  58 import jdk.nashorn.internal.runtime.linker.Bootstrap;
  59 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  60 import 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  */
  67 public 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 synchronized 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      * Set the documentation for this function
 659      *
 660      * @param doc documentation String for this function
 661      */
 662     public final void setDocumentation(final String doc) {
 663         data.setDocumentation(doc);
 664     }
 665 
 666     /**
 667      * Get the name for this function
 668      *
 669      * @return the name
 670      */
 671     public final String getName() {
 672         return data.getName();
 673     }
 674 
 675     /**
 676      * Get the scope for this function
 677      *
 678      * @return the scope
 679      */
 680     public final ScriptObject getScope() {
 681         return scope;
 682     }
 683 
 684     /**
 685      * Prototype getter for this ScriptFunction - follows the naming convention
 686      * used by Nasgen and the code generator
 687      *
 688      * @param self self reference
 689      * @return self's prototype
 690      */
 691     public static Object G$prototype(final Object self) {
 692         return self instanceof ScriptFunction
 693                 ? ((ScriptFunction) self).getPrototype()
 694                 : UNDEFINED;
 695     }
 696 
 697     /**
 698      * Prototype setter for this ScriptFunction - follows the naming convention
 699      * used by Nasgen and the code generator
 700      *
 701      * @param self self reference
 702      * @param prototype prototype to set
 703      */
 704     public static void S$prototype(final Object self, final Object prototype) {
 705         if (self instanceof ScriptFunction) {
 706             ((ScriptFunction) self).setPrototype(prototype);
 707         }
 708     }
 709 
 710     /**
 711      * Length getter - ECMA 15.3.3.2: Function.length
 712      *
 713      * @param self self reference
 714      * @return length
 715      */
 716     public static int G$length(final Object self) {
 717         if (self instanceof ScriptFunction) {
 718             return ((ScriptFunction) self).data.getArity();
 719         }
 720 
 721         return 0;
 722     }
 723 
 724     /**
 725      * Name getter - ECMA Function.name
 726      *
 727      * @param self self refence
 728      * @return the name, or undefined if none
 729      */
 730     public static Object G$name(final Object self) {
 731         if (self instanceof ScriptFunction) {
 732             return ((ScriptFunction) self).getName();
 733         }
 734 
 735         return UNDEFINED;
 736     }
 737 
 738     /**
 739      * Get the prototype for this ScriptFunction
 740      *
 741      * @param constructor constructor
 742      * @return prototype, or null if given constructor is not a ScriptFunction
 743      */
 744     public static ScriptObject getPrototype(final ScriptFunction constructor) {
 745         if (constructor != null) {
 746             final Object proto = constructor.getPrototype();
 747             if (proto instanceof ScriptObject) {
 748                 return (ScriptObject) proto;
 749             }
 750         }
 751 
 752         return null;
 753     }
 754 
 755     // These counters are updated only in debug mode.
 756     private static LongAdder constructorCount;
 757     private static LongAdder invokes;
 758     private static LongAdder allocations;
 759 
 760     static {
 761         if (Context.DEBUG) {
 762             constructorCount = new LongAdder();
 763             invokes = new LongAdder();
 764             allocations = new LongAdder();
 765         }
 766     }
 767 
 768     /**
 769      * @return the constructorCount
 770      */
 771     public static long getConstructorCount() {
 772         return constructorCount.longValue();
 773     }
 774 
 775     /**
 776      * @return the invokes
 777      */
 778     public static long getInvokes() {
 779         return invokes.longValue();
 780     }
 781 
 782     /**
 783      * @return the allocations
 784      */
 785     public static long getAllocations() {
 786         return allocations.longValue();
 787     }
 788 
 789     @Override
 790     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
 791         final MethodType type = desc.getMethodType();
 792         assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
 793         final CompiledFunction cf = data.getBestConstructor(type, scope, CompiledFunction.NO_FUNCTIONS);
 794         final GuardedInvocation bestCtorInv = cf.createConstructorInvocation();
 795         //TODO - ClassCastException
 796         return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null);
 797     }
 798 
 799     private static Object wrapFilter(final Object obj) {
 800         if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
 801             return obj;
 802         }
 803         return Context.getGlobal().wrapAsObject(obj);
 804     }
 805 
 806     @SuppressWarnings("unused")
 807     private static Object globalFilter(final Object object) {
 808         // replace whatever we get with the current global object
 809         return Context.getGlobal();
 810     }
 811 
 812     /**
 813      * Some receivers are primitive, in that case, according to the Spec we
 814      * create a new native object per callsite with the wrap filter. We can only
 815      * apply optimistic builtins if there is no per instance state saved for
 816      * these wrapped objects (e.g. currently NativeStrings), otherwise we can't
 817      * create optimistic versions
 818      *
 819      * @param self receiver
 820      * @param linkLogicClass linkLogicClass, or null if no link logic exists
 821      * @return link logic instance, or null if one could not be constructed for
 822      * this receiver
 823      */
 824     private static LinkLogic getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass) {
 825         if (linkLogicClass == null) {
 826             return LinkLogic.EMPTY_INSTANCE; //always OK to link this, specialization but without special linking logic
 827         }
 828 
 829         if (!Context.getContextTrusted().getEnv()._optimistic_types) {
 830             return null; //if optimistic types are off, optimistic builtins are too
 831         }
 832 
 833         final Object wrappedSelf = wrapFilter(self);
 834         if (wrappedSelf instanceof OptimisticBuiltins) {
 835             if (wrappedSelf != self && ((OptimisticBuiltins) wrappedSelf).hasPerInstanceAssumptions()) {
 836                 return null; //pessimistic - we created a wrapped object different from the primitive, but the assumptions have instance state
 837             }
 838             return ((OptimisticBuiltins) wrappedSelf).getLinkLogic(linkLogicClass);
 839         }
 840         return null;
 841     }
 842 
 843     /**
 844      * StandardOperation.CALL call site signature: (callee, thiz, [args...]) generated method
 845      * signature: (callee, thiz, [args...])
 846      *
 847      * cases:
 848      * (a) method has callee parameter
 849      *     (1) for local/scope calls, we just bind thiz and drop the second argument.
 850      *     (2) for normal this-calls, we have to swap thiz and callee to get matching signatures.
 851      * (b) method doesn't have callee parameter (builtin functions)
 852      *     (3) for local/scope calls, bind thiz and drop both callee and thiz.
 853      *     (4) for normal this-calls, drop callee.
 854      *
 855      * @return guarded invocation for call
 856      */
 857     @Override
 858     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
 859         final MethodType type = desc.getMethodType();
 860 
 861         final String name = getName();
 862         final boolean isUnstable = request.isCallSiteUnstable();
 863         final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
 864         final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name);
 865         final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name);
 866 
 867         final boolean isApplyOrCall = isCall | isApply;
 868 
 869         if (isUnstable && !isApplyOrCall) {
 870             //megamorphic - replace call with apply
 871             final MethodHandle handle;
 872             //ensure that the callsite is vararg so apply can consume it
 873             if (type.parameterCount() == 3 && type.parameterType(2) == Object[].class) {
 874                 // Vararg call site
 875                 handle = ScriptRuntime.APPLY.methodHandle();
 876             } else {
 877                 // (callee, this, args...) => (callee, this, args[])
 878                 handle = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2);
 879             }
 880 
 881             // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a
 882             // generic "is this a ScriptFunction?" guard.
 883             return new GuardedInvocation(
 884                     handle,
 885                     null,
 886                     (SwitchPoint) null,
 887                     ClassCastException.class);
 888         }
 889 
 890         MethodHandle boundHandle;
 891         MethodHandle guard = null;
 892 
 893         // Special handling of Function.apply and Function.call. Note we must be invoking
 894         if (isApplyOrCall && !isUnstable) {
 895             final Object[] args = request.getArguments();
 896             if (Bootstrap.isCallable(args[1])) {
 897                 return createApplyOrCallCall(isApply, desc, request, args);
 898             }
 899         } //else just fall through and link as ordinary function or unstable apply
 900 
 901         int programPoint = INVALID_PROGRAM_POINT;
 902         if (NashornCallSiteDescriptor.isOptimistic(desc)) {
 903             programPoint = NashornCallSiteDescriptor.getProgramPoint(desc);
 904         }
 905 
 906         CompiledFunction cf = data.getBestInvoker(type, scope, CompiledFunction.NO_FUNCTIONS);
 907         final Object self = request.getArguments()[1];
 908         final Collection<CompiledFunction> forbidden = new HashSet<>();
 909 
 910         //check for special fast versions of the compiled function
 911         final List<SwitchPoint> sps = new ArrayList<>();
 912         Class<? extends Throwable> exceptionGuard = null;
 913 
 914         while (cf.isSpecialization()) {
 915             final Class<? extends LinkLogic> linkLogicClass = cf.getLinkLogicClass();
 916             //if linklogic is null, we can always link with the standard mechanism, it's still a specialization
 917             final LinkLogic linkLogic = getLinkLogic(self, linkLogicClass);
 918 
 919             if (linkLogic != null && linkLogic.checkLinkable(self, desc, request)) {
 920                 final DebugLogger log = Context.getContextTrusted().getLogger(Compiler.class);
 921 
 922                 if (log.isEnabled()) {
 923                     log.info("Linking optimistic builtin function: '", name, "' args=", Arrays.toString(request.getArguments()), " desc=", desc);
 924                 }
 925 
 926                 exceptionGuard = linkLogic.getRelinkException();
 927 
 928                 break;
 929             }
 930 
 931             //could not link this specialization because link check failed
 932             forbidden.add(cf);
 933             final CompiledFunction oldCf = cf;
 934             cf = data.getBestInvoker(type, scope, forbidden);
 935             assert oldCf != cf;
 936         }
 937 
 938         final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint);
 939         final MethodHandle callHandle = bestInvoker.getInvocation();
 940 
 941         if (data.needsCallee()) {
 942             if (scopeCall && needsWrappedThis()) {
 943                 // (callee, this, args...) => (callee, [this], args...)
 944                 boundHandle = MH.filterArguments(callHandle, 1, SCRIPTFUNCTION_GLOBALFILTER);
 945             } else {
 946                 // It's already (callee, this, args...), just what we need
 947                 boundHandle = callHandle;
 948             }
 949         } else if (data.isBuiltin() && "extend".equals(data.getName())) {
 950             // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
 951             // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
 952             boundHandle = MH.dropArguments(MH.bindTo(callHandle, getLookupPrivileged(desc)), 0, type.parameterType(0), type.parameterType(1));
 953         } else if (scopeCall && needsWrappedThis()) {
 954             // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
 955             // (this, args...) => ([this], args...)
 956             boundHandle = MH.filterArguments(callHandle, 0, SCRIPTFUNCTION_GLOBALFILTER);
 957             // ([this], args...) => ([callee], [this], args...)
 958             boundHandle = MH.dropArguments(boundHandle, 0, type.parameterType(0));
 959         } else {
 960             // (this, args...) => ([callee], this, args...)
 961             boundHandle = MH.dropArguments(callHandle, 0, type.parameterType(0));
 962         }
 963 
 964         // For non-strict functions, check whether this-object is primitive type.
 965         // If so add a to-object-wrapper argument filter.
 966         // Else install a guard that will trigger a relink when the argument becomes primitive.
 967         if (!scopeCall && needsWrappedThis()) {
 968             if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) {
 969                 boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
 970             } else {
 971                 guard = getNonStrictFunctionGuard(this);
 972             }
 973         }
 974 
 975         boundHandle = pairArguments(boundHandle, type);
 976 
 977         if (bestInvoker.getSwitchPoints() != null) {
 978             sps.addAll(Arrays.asList(bestInvoker.getSwitchPoints()));
 979         }
 980         final SwitchPoint[] spsArray = sps.isEmpty() ? null : sps.toArray(new SwitchPoint[sps.size()]);
 981 
 982         return new GuardedInvocation(
 983                 boundHandle,
 984                 guard == null ?
 985                         getFunctionGuard(
 986                                 this,
 987                                 cf.getFlags()) :
 988                         guard,
 989                 spsArray,
 990                 exceptionGuard);
 991     }
 992 
 993     private static Lookup getLookupPrivileged(final CallSiteDescriptor desc) {
 994         // NOTE: we'd rather not make NashornCallSiteDescriptor.getLookupPrivileged public.
 995         return AccessController.doPrivileged((PrivilegedAction<Lookup>)()->desc.getLookup(),
 996                 GET_LOOKUP_PERMISSION_CONTEXT);
 997     }
 998 
 999     private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
1000         final MethodType descType = desc.getMethodType();
1001         final int paramCount = descType.parameterCount();
1002         if (descType.parameterType(paramCount - 1).isArray()) {
1003             // This is vararg invocation of apply or call. This can normally only happen when we do a recursive
1004             // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate
1005             // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader.
1006             return createVarArgApplyOrCallCall(isApply, desc, request, args);
1007         }
1008 
1009         final boolean passesThis = paramCount > 2;
1010         final boolean passesArgs = paramCount > 3;
1011         final int realArgCount = passesArgs ? paramCount - 3 : 0;
1012 
1013         final Object appliedFn = args[1];
1014         final boolean appliedFnNeedsWrappedThis = needsWrappedThis(appliedFn);
1015 
1016         //box call back to apply
1017         CallSiteDescriptor appliedDesc = desc;
1018         final SwitchPoint applyToCallSwitchPoint = Global.getBuiltinFunctionApplySwitchPoint();
1019         //enough to change the proto switchPoint here
1020 
1021         final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
1022         final boolean isFailedApplyToCall = isApplyToCall && applyToCallSwitchPoint.hasBeenInvalidated();
1023 
1024         // R(apply|call, ...) => R(...)
1025         MethodType appliedType = descType.dropParameterTypes(0, 1);
1026         if (!passesThis) {
1027             // R() => R(this)
1028             appliedType = appliedType.insertParameterTypes(1, Object.class);
1029         } else if (appliedFnNeedsWrappedThis) {
1030             appliedType = appliedType.changeParameterType(1, Object.class);
1031         }
1032 
1033         /*
1034          * dropArgs is a synthetic method handle that contains any args that we need to
1035          * get rid of that come after the arguments array in the apply case. We adapt
1036          * the callsite to ask for 3 args only and then dropArguments on the method handle
1037          * to make it fit the extraneous args.
1038          */
1039         MethodType dropArgs = MH.type(void.class);
1040         if (isApply && !isFailedApplyToCall) {
1041             final int pc = appliedType.parameterCount();
1042             for (int i = 3; i < pc; i++) {
1043                 dropArgs = dropArgs.appendParameterTypes(appliedType.parameterType(i));
1044             }
1045             if (pc > 3) {
1046                 appliedType = appliedType.dropParameterTypes(3, pc);
1047             }
1048         }
1049 
1050         if (isApply || isFailedApplyToCall) {
1051             if (passesArgs) {
1052                 // R(this, args) => R(this, Object[])
1053                 appliedType = appliedType.changeParameterType(2, Object[].class);
1054                 // drop any extraneous arguments for the apply fail case
1055                 if (isFailedApplyToCall) {
1056                     appliedType = appliedType.dropParameterTypes(3, paramCount - 1);
1057                 }
1058             } else {
1059                 // R(this) => R(this, Object[])
1060                 appliedType = appliedType.insertParameterTypes(2, Object[].class);
1061             }
1062         }
1063 
1064         appliedDesc = appliedDesc.changeMethodType(appliedType); //no extra args
1065 
1066         // Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation
1067         final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()];
1068         appliedArgs[0] = appliedFn;
1069         appliedArgs[1] = passesThis ? appliedFnNeedsWrappedThis ? ScriptFunctionData.wrapThis(args[2]) : args[2] : ScriptRuntime.UNDEFINED;
1070         if (isApply && !isFailedApplyToCall) {
1071             appliedArgs[2] = passesArgs ? NativeFunction.toApplyArgs(args[3]) : ScriptRuntime.EMPTY_ARRAY;
1072         } else {
1073             if (passesArgs) {
1074                 if (isFailedApplyToCall) {
1075                     final Object[] tmp = new Object[args.length - 3];
1076                     System.arraycopy(args, 3, tmp, 0, tmp.length);
1077                     appliedArgs[2] = NativeFunction.toApplyArgs(tmp);
1078                 } else {
1079                     assert !isApply;
1080                     System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
1081                 }
1082             } else if (isFailedApplyToCall) {
1083                 appliedArgs[2] = ScriptRuntime.EMPTY_ARRAY;
1084             }
1085         }
1086 
1087         // Ask the linker machinery for an invocation of the target function
1088         final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs);
1089 
1090         GuardedInvocation appliedInvocation;
1091         try {
1092             appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest);
1093         } catch (final RuntimeException | Error e) {
1094             throw e;
1095         } catch (final Exception e) {
1096             throw new RuntimeException(e);
1097         }
1098         assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage.
1099 
1100         final Class<?> applyFnType = descType.parameterType(0);
1101         MethodHandle inv = appliedInvocation.getInvocation(); //method handle from apply invocation. the applied function invocation
1102 
1103         if (isApply && !isFailedApplyToCall) {
1104             if (passesArgs) {
1105                 // Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it.
1106                 inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS);
1107             } else {
1108                 // If the original call site doesn't pass argArray, pass in an empty array
1109                 inv = MH.insertArguments(inv, 2, (Object) ScriptRuntime.EMPTY_ARRAY);
1110             }
1111         }
1112 
1113         if (isApplyToCall) {
1114             if (isFailedApplyToCall) {
1115                 //take the real arguments that were passed to a call and force them into the apply instead
1116                 Context.getContextTrusted().getLogger(ApplySpecialization.class).info("Collection arguments to revert call to apply in " + appliedFn);
1117                 inv = MH.asCollector(inv, Object[].class, realArgCount);
1118             } else {
1119                 appliedInvocation = appliedInvocation.addSwitchPoint(applyToCallSwitchPoint);
1120             }
1121         }
1122 
1123         if (!passesThis) {
1124             // If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed
1125             inv = bindImplicitThis(appliedFn, inv);
1126         } else if (appliedFnNeedsWrappedThis) {
1127             // target function needs a wrapped this, so make sure we filter for that
1128             inv = MH.filterArguments(inv, 1, WRAP_THIS);
1129         }
1130         inv = MH.dropArguments(inv, 0, applyFnType);
1131 
1132         /*
1133          * Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which
1134          * is when we need to add arguments to the callsite to catch and ignore the synthetic
1135          * extra args that someone has added to the command line.
1136          */
1137         for (int i = 0; i < dropArgs.parameterCount(); i++) {
1138             inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i));
1139         }
1140 
1141         MethodHandle guard = appliedInvocation.getGuard();
1142         // If the guard checks the value of "this" but we aren't passing thisArg, insert the default one
1143         if (!passesThis && guard.type().parameterCount() > 1) {
1144             guard = bindImplicitThis(appliedFn, guard);
1145         }
1146         final MethodType guardType = guard.type();
1147 
1148         // We need to account for the dropped (apply|call) function argument.
1149         guard = MH.dropArguments(guard, 0, descType.parameterType(0));
1150         // Take the "isApplyFunction" guard, and bind it to this function.
1151         MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); //TODO replace this with switchpoint
1152         // Adapt the guard to receive all the arguments that the original guard does.
1153         applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray());
1154         // Fold the original function guard into our apply guard.
1155         guard = MH.foldArguments(applyFnGuard, guard);
1156 
1157         return appliedInvocation.replaceMethods(inv, guard);
1158     }
1159 
1160     /*
1161      * This method is used for linking nested apply. Specialized apply and call linking will create a variable arity
1162      * call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with
1163      * Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method.
1164      * This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back
1165      * to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to
1166      * invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity
1167      * invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already
1168      * solved by createApplyOrCallCall) non-vararg call site linking.
1169      */
1170     private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc,
1171             final LinkRequest request, final Object[] args) {
1172         final MethodType descType = desc.getMethodType();
1173         final int paramCount = descType.parameterCount();
1174         final Object[] varArgs = (Object[]) args[paramCount - 1];
1175         // -1 'cause we're not passing the vararg array itself
1176         final int copiedArgCount = args.length - 1;
1177         final int varArgCount = varArgs.length;
1178 
1179         // Spread arguments for the delegate createApplyOrCallCall invocation.
1180         final Object[] spreadArgs = new Object[copiedArgCount + varArgCount];
1181         System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount);
1182         System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount);
1183 
1184         // Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and
1185         // replace it with a list of Object.class.
1186         final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes(
1187                 Collections.<Class<?>>nCopies(varArgCount, Object.class));
1188         final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType);
1189 
1190         // Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/
1191         final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs);
1192         final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs);
1193 
1194         // Add spreader combinators to returned invocation and guard.
1195         return spreadInvocation.replaceMethods(
1196                 // Use standard ScriptObject.pairArguments on the invocation
1197                 pairArguments(spreadInvocation.getInvocation(), descType),
1198                 // Use our specialized spreadGuardArguments on the guard (see below).
1199                 spreadGuardArguments(spreadInvocation.getGuard(), descType));
1200     }
1201 
1202     private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) {
1203         final MethodType guardType = guard.type();
1204         final int guardParamCount = guardType.parameterCount();
1205         final int descParamCount = descType.parameterCount();
1206         final int spreadCount = guardParamCount - descParamCount + 1;
1207         if (spreadCount <= 0) {
1208             // Guard doesn't dip into the varargs
1209             return guard;
1210         }
1211 
1212         final MethodHandle arrayConvertingGuard;
1213         // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply
1214         // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail
1215         // with ClassCastException of NativeArray to Object[].
1216         if (guardType.parameterType(guardParamCount - 1).isArray()) {
1217             arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS);
1218         } else {
1219             arrayConvertingGuard = guard;
1220         }
1221 
1222         return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount);
1223     }
1224 
1225     private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
1226         final MethodHandle bound;
1227         if (fn instanceof ScriptFunction && ((ScriptFunction) fn).needsWrappedThis()) {
1228             bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER);
1229         } else {
1230             bound = mh;
1231         }
1232         return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED);
1233     }
1234 
1235     /**
1236      * Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
1237      *
1238      * These don't want a callee parameter, so bind that. Name binding is
1239      * optional.
1240      */
1241     MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
1242         return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type);
1243     }
1244 
1245     private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
1246         if (bindName == null) {
1247             return methodHandle;
1248         }
1249 
1250         // if it is vararg method, we need to extend argument array with
1251         // a new zeroth element that is set to bindName value.
1252         final MethodType methodType = methodHandle.type();
1253         final int parameterCount = methodType.parameterCount();
1254         final boolean isVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
1255 
1256         if (isVarArg) {
1257             return MH.filterArguments(methodHandle, 1, MH.insertArguments(ADD_ZEROTH_ELEMENT, 1, bindName));
1258         }
1259         return MH.insertArguments(methodHandle, 1, bindName);
1260     }
1261 
1262     /**
1263      * Get the guard that checks if a {@link ScriptFunction} is equal to a known
1264      * ScriptFunction, using reference comparison
1265      *
1266      * @param function The ScriptFunction to check against. This will be bound
1267      * to the guard method handle
1268      *
1269      * @return method handle for guard
1270      */
1271     private static MethodHandle getFunctionGuard(final ScriptFunction function, final int flags) {
1272         assert function.data != null;
1273         // Built-in functions have a 1-1 correspondence to their ScriptFunctionData, so we can use a cheaper identity
1274         // comparison for them.
1275         if (function.data.isBuiltin()) {
1276             return Guards.getIdentityGuard(function);
1277         }
1278         return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
1279     }
1280 
1281     /**
1282      * Get a guard that checks if a {@link ScriptFunction} is equal to a known
1283      * ScriptFunction using reference comparison, and whether the type of the
1284      * second argument (this-object) is not a JavaScript primitive type.
1285      *
1286      * @param function The ScriptFunction to check against. This will be bound
1287      * to the guard method handle
1288      *
1289      * @return method handle for guard
1290      */
1291     private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) {
1292         assert function.data != null;
1293         return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data);
1294     }
1295 
1296     @SuppressWarnings("unused")
1297     private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) {
1298         return self instanceof ScriptFunction && ((ScriptFunction) self).data == data;
1299     }
1300 
1301     @SuppressWarnings("unused")
1302     private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) {
1303         return self instanceof ScriptFunction && ((ScriptFunction) self).data == data && arg instanceof ScriptObject;
1304     }
1305 
1306     //TODO this can probably be removed given that we have builtin switchpoints in the context
1307     @SuppressWarnings("unused")
1308     private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) {
1309         // NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call()
1310         return appliedFnCondition && self == expectedSelf;
1311     }
1312 
1313     @SuppressWarnings("unused")
1314     private static Object[] addZerothElement(final Object[] args, final Object value) {
1315         // extends input array with by adding new zeroth element
1316         final Object[] src = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
1317         final Object[] result = new Object[src.length + 1];
1318         System.arraycopy(src, 0, result, 1, src.length);
1319         result[0] = value;
1320         return result;
1321     }
1322 
1323     @SuppressWarnings("unused")
1324     private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args)
1325             throws Throwable {
1326         final Object syncObj = sync == UNDEFINED ? self : sync;
1327         synchronized (syncObj) {
1328             return func.invoke(self, args);
1329         }
1330     }
1331 
1332     private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
1333         return MH.findStatic(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
1334     }
1335 
1336     private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
1337         return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
1338     }
1339 }