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 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
  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.linker.Lookup.MH;
  32 
  33 import java.lang.invoke.MethodHandle;
  34 import java.lang.invoke.MethodHandles;
  35 import java.lang.invoke.MethodType;
  36 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  37 import jdk.nashorn.internal.codegen.types.Type;
  38 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
  39 import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
  40 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
  41 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  42 import jdk.nashorn.internal.runtime.linker.NashornGuards;
  43 import jdk.nashorn.internal.runtime.options.Options;
  44 import org.dynalang.dynalink.CallSiteDescriptor;
  45 import org.dynalang.dynalink.linker.GuardedInvocation;
  46 import org.dynalang.dynalink.linker.LinkRequest;
  47 
  48 /**
  49  * Runtime representation of a JavaScript function.
  50  */
  51 public abstract class ScriptFunction extends ScriptObject {
  52 
  53     /** Method handle for prototype getter for this ScriptFunction */
  54     public static final MethodHandle G$PROTOTYPE  = findOwnMH("G$prototype",  Object.class, Object.class);
  55 
  56     /** Method handle for prototype setter for this ScriptFunction */
  57     public static final MethodHandle S$PROTOTYPE  = findOwnMH("S$prototype",  void.class, Object.class, Object.class);
  58 
  59     /** Method handle for length getter for this ScriptFunction */
  60     public static final MethodHandle G$LENGTH     = findOwnMH("G$length",     int.class, Object.class);
  61 
  62     /** Method handle for name getter for this ScriptFunction */
  63     public static final MethodHandle G$NAME       = findOwnMH("G$name",       Object.class, Object.class);
  64 
  65     /** Method handle for allocate function for this ScriptFunction */
  66     public static final MethodHandle ALLOCATE     = findOwnMH("allocate", Object.class);
  67 
  68     private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
  69 
  70     private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
  71 
  72     /** method handle to scope getter for this ScriptFunction */
  73     public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
  74 
  75     /** Should specialized function and specialized constructors for the builtin be used if available? */
  76     private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
  77 
  78     private final ScriptFunctionData data;
  79 
  80     /** Reference to constructor prototype. */
  81     protected Object prototype;
  82 
  83     /** The parent scope. */
  84     private final ScriptObject scope;
  85 
  86     /**
  87      * Constructor
  88      *
  89      * @param name         function name
  90      * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
  91      * @param map          property map
  92      * @param scope        scope
  93      * @param specs        specialized version of this function - other method handles
  94      *
  95      */
  96     protected ScriptFunction(
  97             final String name,
  98             final MethodHandle methodHandle,
  99             final PropertyMap map,
 100             final ScriptObject scope,
 101             final MethodHandle[] specs,
 102             final boolean strict,
 103             final boolean builtin) {
 104 
 105         this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin), map, scope);
 106     }
 107 
 108     /**
 109      * Constructor
 110      *
 111      * @param data          static function data
 112      * @param map           property map
 113      * @param scope         scope
 114      */
 115     protected ScriptFunction(
 116             final ScriptFunctionData data,
 117             final PropertyMap map,
 118             final ScriptObject scope) {
 119 
 120         super(map);
 121 
 122         if (Context.DEBUG) {
 123             constructorCount++;
 124         }
 125 
 126         this.data = data;
 127         this.scope  = scope;
 128     }
 129 
 130     @Override
 131     public String getClassName() {
 132         return "Function";
 133     }
 134 
 135     /**
 136      * ECMA 15.3.5.3 [[HasInstance]] (V)
 137      * Step 3 if "prototype" value is not an Object, throw TypeError
 138      */
 139     @Override
 140     public boolean isInstance(final ScriptObject instance) {
 141         if (!(prototype instanceof ScriptObject)) {
 142             typeError("prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype));
 143         }
 144 
 145         for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
 146             if (proto == prototype) {
 147                 return true;
 148             }
 149         }
 150 
 151         return false;
 152     }
 153 
 154     /**
 155      * Get the arity of this ScriptFunction
 156      * @return arity
 157      */
 158     public final int getArity() {
 159         return data.getArity();
 160     }
 161 
 162     /**
 163      * Set the arity of this ScriptFunction
 164      * @param arity arity
 165      */
 166     public final void setArity(final int arity) {
 167         data.setArity(arity);
 168     }
 169 
 170     /**
 171      * Is this a ECMAScript 'use strict' function?
 172      * @return true if function is in strict mode
 173      */
 174     public boolean isStrict() {
 175         return data.isStrict();
 176     }
 177 
 178     /**
 179      * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ?
 180      * @return true if built-in
 181      */
 182     public boolean isBuiltin() {
 183         return data.isBuiltin();
 184     }
 185 
 186     /**
 187      * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
 188      * according to ECMA 10.4.3.
 189      * @return true if this argument must be an object
 190      */
 191     public boolean needsWrappedThis() {
 192         return data.needsWrappedThis();
 193     }
 194 
 195     /**
 196      * Execute this script function.
 197      * @param self  Target object.
 198      * @param arguments  Call arguments.
 199      * @return ScriptFunction result.
 200      * @throws Throwable if there is an exception/error with the invocation or thrown from it
 201      */
 202     public Object invoke(final Object self, final Object... arguments) throws Throwable {
 203         if (Context.DEBUG) {
 204             invokes++;
 205         }
 206 
 207         final MethodHandle invoker = data.getGenericInvoker();
 208         final Object selfObj = convertThisObject(self);
 209         final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 210 
 211         if (data.isVarArg()) {
 212             if (data.needsCallee()) {
 213                 return invoker.invokeExact(selfObj, this, args);
 214             }
 215             return invoker.invokeExact(selfObj, args);
 216         }
 217 
 218         final int paramCount = invoker.type().parameterCount();
 219         if (data.needsCallee()) {
 220             switch (paramCount) {
 221             case 2:
 222                 return invoker.invokeExact(selfObj, this);
 223             case 3:
 224                 return invoker.invokeExact(selfObj, this, getArg(args, 0));
 225             case 4:
 226                 return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
 227             case 5:
 228                 return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 229             default:
 230                 return invoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
 231             }
 232         }
 233 
 234         switch (paramCount) {
 235         case 1:
 236             return invoker.invokeExact(selfObj);
 237         case 2:
 238             return invoker.invokeExact(selfObj, getArg(args, 0));
 239         case 3:
 240             return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
 241         case 4:
 242             return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 243         default:
 244             return invoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
 245         }
 246     }
 247 
 248     private static Object getArg(final Object[] args, final int i) {
 249         return i < args.length ? args[i] : UNDEFINED;
 250     }
 251 
 252     /**
 253      * Construct new object using this constructor.
 254      * @param self  Target object.
 255      * @param args  Call arguments.
 256      * @return ScriptFunction result.
 257      * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it
 258      */
 259     public Object construct(final Object self, final Object... args) throws Throwable {
 260         if (data.getConstructor() == null) {
 261             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
 262         }
 263 
 264         final MethodHandle constructor = data.getGenericConstructor();
 265         if (data.isVarArg()) {
 266             if (data.needsCallee()) {
 267                 return constructor.invokeExact(self, this, args);
 268             }
 269             return constructor.invokeExact(self, args);
 270         }
 271 
 272         final int paramCount = constructor.type().parameterCount();
 273         if (data.needsCallee()) {
 274             switch (paramCount) {
 275             case 2:
 276                 return constructor.invokeExact(self, this);
 277             case 3:
 278                 return constructor.invokeExact(self, this, getArg(args, 0));
 279             case 4:
 280                 return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
 281             case 5:
 282                 return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 283             default:
 284                 return constructor.invokeWithArguments(withArguments(self, this, args));
 285             }
 286         }
 287 
 288         switch(paramCount) {
 289         case 1:
 290             return constructor.invokeExact(self);
 291         case 2:
 292             return constructor.invokeExact(self, getArg(args, 0));
 293         case 3:
 294             return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1));
 295         case 4:
 296             return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 297         default:
 298             return constructor.invokeWithArguments(withArguments(self, null, args));
 299         }
 300     }
 301 
 302     private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) {
 303         return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function
 304     }
 305 
 306     private static Object[] withArguments(final Object self, final ScriptFunction function, final int paramCount, final Object... args) {
 307         final Object[] finalArgs = new Object[paramCount];
 308 
 309         finalArgs[0] = self;
 310         int nextArg = 1;
 311         if (function != null) {
 312             finalArgs[nextArg++] = function;
 313         }
 314 
 315         //don't add more args that there is paramcount in the handle (including self)
 316         final int maxArgs = Math.min(args.length, paramCount - (function == null ? 1 : 2));
 317         for (int i = 0; i < maxArgs;) {
 318             finalArgs[nextArg++] = args[i++];
 319         }
 320 
 321         //if we have fewer params than paramcount, pad undefined
 322         while (nextArg < paramCount) {
 323             finalArgs[nextArg++] = UNDEFINED;
 324         }
 325 
 326         return finalArgs;
 327     }
 328 
 329     /**
 330      * Allocate function. Called from generated {@link ScriptObject} code
 331      * for allocation as a factory method
 332      *
 333      * @return a new instance of the {@link ScriptObject} whose allocator this is
 334      */
 335     public Object allocate() {
 336         if (Context.DEBUG) {
 337             allocations++;
 338         }
 339 
 340         if (getConstructHandle() == null) {
 341             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
 342         }
 343 
 344         ScriptObject object = null;
 345 
 346         if (data.getAllocator() != null) {
 347             try {
 348                 object = (ScriptObject)data.getAllocator().invokeExact(data.getAllocatorMap());
 349             } catch (final RuntimeException | Error e) {
 350                 throw e;
 351             } catch (final Throwable t) {
 352                 throw new RuntimeException(t);
 353             }
 354         }
 355 
 356         if (object != null) {
 357             if (prototype instanceof ScriptObject) {
 358                 object.setProto((ScriptObject)prototype);
 359             }
 360 
 361             if (object.getProto() == null) {
 362                 object.setProto(getObjectPrototype());
 363             }
 364         }
 365 
 366         return object;
 367     }
 368 
 369     /**
 370      * Return Object.prototype - used by "allocate"
 371      * @return Object.prototype
 372      */
 373     protected abstract ScriptObject getObjectPrototype();
 374 
 375     /**
 376      * Creates a version of this function bound to a specific "self" and other argumentss
 377      * @param self the self to bind the function to
 378      * @param args other arguments (beside self) to bind the function to
 379      * @return the bound function
 380      */
 381     public abstract ScriptFunction makeBoundFunction(Object self, Object[] args);
 382 
 383 
 384     @Override
 385     public final String safeToString() {
 386         return toSource();
 387     }
 388 
 389     @Override
 390     public String toString() {
 391         return data.toString();
 392     }
 393 
 394     /**
 395      * Get this function as a String containing its source code. If no source code
 396      * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
 397      * @return string representation of this function's source
 398      */
 399     public final String toSource() {
 400         return data.toSource();
 401     }
 402 
 403     /**
 404      * Get the prototype object for this function
 405      * @return prototype
 406      */
 407     public final Object getPrototype() {
 408         return prototype;
 409     }
 410 
 411     /**
 412      * Set the prototype object for this function
 413      * @param prototype new prototype object
 414      * @return the prototype parameter
 415      */
 416     public final Object setPrototype(final Object prototype) {
 417         this.prototype = prototype;
 418         return prototype;
 419     }
 420 
 421     private static int weigh(final MethodType t) {
 422         int weight = Type.typeFor(t.returnType()).getWeight();
 423         for (final Class<?> paramType : t.parameterArray()) {
 424             final int pweight = Type.typeFor(paramType).getWeight();
 425             weight += pweight;
 426         }
 427         return weight;
 428     }
 429 
 430     private static boolean typeCompatible(final MethodType desc, final MethodType spec) {
 431         //spec must fit in desc
 432         final Class<?>[] dparray = desc.parameterArray();
 433         final Class<?>[] sparray = spec.parameterArray();
 434 
 435         if (dparray.length != sparray.length) {
 436             return false;
 437         }
 438 
 439         for (int i = 0; i < dparray.length; i++) {
 440             final Type dp = Type.typeFor(dparray[i]);
 441             final Type sp = Type.typeFor(sparray[i]);
 442 
 443             if (dp.isBoolean()) {
 444                 return false; //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
 445             }
 446 
 447             //specialization arguments must be at least as wide as dp, if not wider
 448             if (Type.widest(dp, sp) != sp) {
 449                 //e.g. specialization takes double and callsite says "object". reject.
 450                 //but if specialization says double and callsite says "int" or "long" or "double", that's fine
 451                 return false;
 452             }
 453         }
 454 
 455         return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
 456     }
 457 
 458     private MethodHandle candidateWithLowestWeight(final MethodType descType, final MethodHandle initialCandidate, final MethodHandle[] specs) {
 459         if (DISABLE_SPECIALIZATION || specs == null) {
 460             return initialCandidate;
 461         }
 462 
 463         int          minimumWeight = Integer.MAX_VALUE;
 464         MethodHandle candidate     = initialCandidate;
 465 
 466         for (final MethodHandle spec : specs) {
 467             final MethodType specType = spec.type();
 468 
 469             if (!typeCompatible(descType, specType)) {
 470                 continue;
 471             }
 472 
 473             //return type is ok. we want a wider or equal one for our callsite.
 474             final int specWeight = weigh(specType);
 475             if (specWeight < minimumWeight) {
 476                 candidate = spec;
 477                 minimumWeight = specWeight;
 478             }
 479         }
 480 
 481         if (DISABLE_SPECIALIZATION && candidate != initialCandidate) {
 482             Context.err("### Specializing builtin " + getName() + " -> " + candidate + "?");
 483         }
 484 
 485         return candidate;
 486     }
 487 
 488     /**
 489      * Return the most appropriate invoke handle if there are specializations
 490      * @param type most specific method type to look for invocation with
 491      * @return invoke method handle
 492      */
 493     public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) {
 494         return candidateWithLowestWeight(type, getInvokeHandle(), data.getInvokeSpecializations());
 495     }
 496 
 497     /**
 498      * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation
 499      * method handle for this ScriptFunction
 500      * @see SpecializedFunction
 501      * @return invokeHandle
 502      */
 503     public final MethodHandle getInvokeHandle() {
 504         return data.getInvoker();
 505     }
 506 
 507     /**
 508      * Return the invoke handle bound to a given ScriptObject self reference.
 509      * If callee parameter is required result is rebound to this.
 510      *
 511      * @param self self reference
 512      * @return bound invoke handle
 513      */
 514     public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
 515         final MethodHandle bound = MH.bindTo(getInvokeHandle(), self);
 516         return data.needsCallee() ? MH.bindTo(bound, this) : bound;
 517     }
 518 
 519 
 520     /**
 521      * Get the construct handle - the most generic (and if no specializations are in place, only) constructor
 522      * method handle for this ScriptFunction
 523      * @see SpecializedConstructor
 524      * @param type type for wanted constructor
 525      * @return construct handle
 526      */
 527     public final MethodHandle getConstructHandle(final MethodType type) {
 528         return candidateWithLowestWeight(type, getConstructHandle(), data.getConstructSpecializations());
 529     }
 530 
 531     /**
 532      * Get a method handle to the constructor for this function
 533      * @return constructor handle
 534      */
 535     public final MethodHandle getConstructHandle() {
 536         return data.getConstructor();
 537     }
 538 
 539     /**
 540      * Set a method handle to the constructor for this function
 541      * @param constructHandle constructor handle
 542      */
 543     public final void setConstructHandle(final MethodHandle constructHandle) {
 544         data.setConstructor(constructHandle);
 545     }
 546 
 547     /**
 548      * Get the name for this function
 549      * @return the name
 550      */
 551     public final String getName() {
 552         return data.getName();
 553     }
 554 
 555     /**
 556      * Does this script function need to be compiled. This determined by
 557      * null checking invokeHandle
 558      *
 559      * @return true if this needs compilation
 560      */
 561     public final boolean needsCompilation() {
 562         return data.getInvoker() == null;
 563     }
 564 
 565     /**
 566      * Get token for this function
 567      * @return token
 568      */
 569     public final long getToken() {
 570         return data.getToken();
 571     }
 572 
 573     /**
 574      * Get the scope for this function
 575      * @return the scope
 576      */
 577     public final ScriptObject getScope() {
 578         return scope;
 579     }
 580 
 581     /**
 582      * Prototype getter for this ScriptFunction - follows the naming convention
 583      * used by Nasgen and the code generator
 584      *
 585      * @param self  self reference
 586      * @return self's prototype
 587      */
 588     public static Object G$prototype(final Object self) {
 589         return (self instanceof ScriptFunction) ?
 590             ((ScriptFunction)self).getPrototype() :
 591             UNDEFINED;
 592     }
 593 
 594     /**
 595      * Prototype setter for this ScriptFunction - follows the naming convention
 596      * used by Nasgen and the code generator
 597      *
 598      * @param self  self reference
 599      * @param prototype prototype to set
 600      */
 601     public static void S$prototype(final Object self, final Object prototype) {
 602         if (self instanceof ScriptFunction) {
 603             ((ScriptFunction)self).setPrototype(prototype);
 604         }
 605     }
 606 
 607     /**
 608      * Length getter - ECMA 15.3.3.2: Function.length
 609      * @param self self reference
 610      * @return length
 611      */
 612     public static int G$length(final Object self) {
 613         if (self instanceof ScriptFunction) {
 614             return ((ScriptFunction)self).getArity();
 615         }
 616 
 617         return 0;
 618     }
 619 
 620     /**
 621      * Name getter - ECMA Function.name
 622      * @param self self refence
 623      * @return the name, or undefined if none
 624      */
 625     public static Object G$name(final Object self) {
 626         if (self instanceof ScriptFunction) {
 627             return ((ScriptFunction)self).getName();
 628         }
 629 
 630         return UNDEFINED;
 631     }
 632 
 633     /**
 634      * Get the prototype for this ScriptFunction
 635      * @param constructor constructor
 636      * @return prototype, or null if given constructor is not a ScriptFunction
 637      */
 638     public static ScriptObject getPrototype(final Object constructor) {
 639         if (constructor instanceof ScriptFunction) {
 640             final Object proto = ((ScriptFunction)constructor).getPrototype();
 641             if (proto instanceof ScriptObject) {
 642                 return (ScriptObject)proto;
 643             }
 644         }
 645 
 646         return null;
 647     }
 648 
 649     // These counters are updated only in debug mode.
 650     private static int constructorCount;
 651     private static int invokes;
 652     private static int allocations;
 653 
 654     /**
 655      * @return the constructorCount
 656      */
 657     public static int getConstructorCount() {
 658         return constructorCount;
 659     }
 660 
 661     /**
 662      * @return the invokes
 663      */
 664     public static int getInvokes() {
 665         return invokes;
 666     }
 667 
 668     /**
 669      * @return the allocations
 670      */
 671     public static int getAllocations() {
 672         return allocations;
 673     }
 674 
 675     @Override
 676     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
 677         final MethodType type = desc.getMethodType();
 678         MethodHandle constructor = getConstructHandle(type);
 679 
 680         if (constructor == null) {
 681             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
 682             return null;
 683         }
 684 
 685         final MethodType ctorType = constructor.type();
 686 
 687         // guard against primitive constructor return types
 688         constructor = MH.asType(constructor, constructor.type().changeReturnType(Object.class));
 689 
 690         // apply new filter
 691         final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self
 692         MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor);
 693 
 694         if (data.needsCallee()) {
 695             handle = MH.foldArguments(handle, ALLOCATE);
 696         } else {
 697             handle = MH.filterArguments(handle, 0, ALLOCATE);
 698         }
 699 
 700         final MethodHandle filterIn = MH.asType(pairArguments(handle, type), type);
 701         return new GuardedInvocation(filterIn, null, NashornGuards.getFunctionGuard(this));
 702     }
 703 
 704     @SuppressWarnings("unused")
 705     private static Object newFilter(final Object result, final Object allocation) {
 706         return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
 707     }
 708 
 709     @SuppressWarnings("unused")
 710     private static Object wrapFilter(final Object obj) {
 711         if (obj instanceof ScriptObject || !isPrimitiveThis(obj)) {
 712             return obj;
 713         }
 714         return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj);
 715     }
 716 
 717     /**
 718      * dyn:call call site signature: (callee, thiz, [args...])
 719      * generated method signature:   (thiz, callee, [args...])
 720      *
 721      * cases:
 722      * (a) method has callee parameter
 723      *   (1) for local/scope calls, we just bind thiz and drop the second argument.
 724      *   (2) for normal this-calls, we have to swap thiz and callee to get matching signatures.
 725      * (b) method doesn't have callee parameter (builtin functions)
 726      *   (3) for local/scope calls, bind thiz and drop both callee and thiz.
 727      *   (4) for normal this-calls, drop callee.
 728      */
 729     @Override
 730     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
 731         final MethodType type = desc.getMethodType();
 732 
 733         if (request.isCallSiteUnstable()) {
 734             // (this, callee, args...) => (this, callee, args[])
 735             final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class,
 736                     type.parameterCount() - 2);
 737 
 738             return new GuardedInvocation(collector,
 739                     desc.getMethodType().parameterType(0) == ScriptFunction.class ? null : NashornGuards.getScriptFunctionGuard());
 740         }
 741 
 742         MethodHandle boundHandle;
 743         MethodHandle guard = null;
 744 
 745         if (data.needsCallee()) {
 746             final MethodHandle callHandle = getBestSpecializedInvokeHandle(type);
 747 
 748             if(NashornCallSiteDescriptor.isScope(desc)) {
 749                 // (this, callee, args...) => (callee, args...) => (callee, [this], args...)
 750                 boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
 751                 boundHandle = MH.dropArguments(boundHandle, 1, Object.class);
 752             } else {
 753                 // (this, callee, args...) permute => (callee, this, args...) which is what we get in
 754                 final MethodType oldType = callHandle.type();
 755                 final int[] reorder = new int[oldType.parameterCount()];
 756                 for (int i = 2; i < reorder.length; i++) {
 757                     reorder[i] = i;
 758                 }
 759                 reorder[0] = 1;
 760                 assert reorder[1] == 0;
 761                 final MethodType newType = oldType.changeParameterType(0, oldType.parameterType(1)).changeParameterType(1, oldType.parameterType(0));
 762                 boundHandle = MethodHandles.permuteArguments(callHandle, newType, reorder);
 763 
 764                 // For non-strict functions, check whether this-object is primitive type.
 765                 // If so add a to-object-wrapper argument filter.
 766                 // Else install a guard that will trigger a relink when the argument becomes primitive.
 767                 if (needsWrappedThis()) {
 768                     if (isPrimitiveThis(request.getArguments()[1])) {
 769                         boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
 770                     } else {
 771                         guard = NashornGuards.getNonStrictFunctionGuard(this);
 772                     }
 773                 }
 774             }
 775         } else {
 776             final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1));
 777 
 778             if(NashornCallSiteDescriptor.isScope(desc)) {
 779                 boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
 780                 boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class);
 781             } else {
 782                 boundHandle = MH.dropArguments(callHandle, 0, Object.class);
 783             }
 784         }
 785 
 786         boundHandle = pairArguments(boundHandle, type);
 787         return new GuardedInvocation(boundHandle, guard == null ? NashornGuards.getFunctionGuard(this) : guard);
 788    }
 789 
 790     /**
 791      * Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
 792      *
 793      * These don't want a callee parameter, so bind that. Name binding is optional.
 794      */
 795     MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
 796         MethodHandle methodHandle = getBestSpecializedInvokeHandle(type);
 797 
 798         if (bindName != null) {
 799             if (data.needsCallee()) {
 800                 methodHandle = MH.insertArguments(methodHandle, 1, this, bindName);
 801             } else {
 802                 methodHandle = MH.insertArguments(methodHandle, 1, bindName);
 803             }
 804         } else {
 805             if (data.needsCallee()) {
 806                 methodHandle = MH.insertArguments(methodHandle, 1, this);
 807             }
 808         }
 809 
 810         return pairArguments(methodHandle, type);
 811     }
 812 
 813     /**
 814      * Convert this argument for non-strict functions according to ES 10.4.3
 815      *
 816      * @param thiz the this argument
 817      *
 818      * @return the converted this object
 819      */
 820     protected Object convertThisObject(final Object thiz) {
 821         if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
 822             if (JSType.nullOrUndefined(thiz)) {
 823                 return Context.getGlobalTrusted();
 824             }
 825 
 826             if (isPrimitiveThis(thiz)) {
 827                 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
 828             }
 829         }
 830 
 831         return thiz;
 832     }
 833 
 834     private static boolean isPrimitiveThis(Object obj) {
 835         return obj instanceof String || obj instanceof ConsString ||
 836                obj instanceof Number || obj instanceof Boolean;
 837     }
 838 
 839     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
 840         final Class<?>   own = ScriptFunction.class;
 841         final MethodType mt  = MH.type(rtype, types);
 842         try {
 843             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
 844         } catch (final MethodHandleFactory.LookupException e) {
 845             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
 846         }
 847     }
 848 }
 849