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.ScriptFunctionData;
  38 import jdk.nashorn.internal.codegen.types.Type;
  39 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
  40 import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
  41 import jdk.nashorn.internal.parser.Token;
  42 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
  43 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  44 import jdk.nashorn.internal.runtime.linker.NashornGuards;
  45 import jdk.nashorn.internal.runtime.options.Options;
  46 import org.dynalang.dynalink.CallSiteDescriptor;
  47 import org.dynalang.dynalink.linker.GuardedInvocation;
  48 import org.dynalang.dynalink.linker.LinkRequest;
  49 
  50 /**
  51  * Runtime representation of a JavaScript function.
  52  */
  53 public abstract class ScriptFunction extends ScriptObject {
  54 
  55     /** Method handle for prototype getter for this ScriptFunction */
  56     public static final MethodHandle G$PROTOTYPE  = findOwnMH("G$prototype",  Object.class, Object.class);
  57 
  58     /** Method handle for prototype setter for this ScriptFunction */
  59     public static final MethodHandle S$PROTOTYPE  = findOwnMH("S$prototype",  void.class, Object.class, Object.class);
  60 
  61     /** Method handle for length getter for this ScriptFunction */
  62     public static final MethodHandle G$LENGTH     = findOwnMH("G$length",     int.class, Object.class);
  63 
  64     /** Method handle for name getter for this ScriptFunction */
  65     public static final MethodHandle G$NAME       = findOwnMH("G$name",       Object.class, Object.class);
  66 
  67     /** Method handle for allocate function for this ScriptFunction */
  68     public static final MethodHandle ALLOCATE     = findOwnMH("allocate", Object.class);
  69 
  70     private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
  71 
  72     private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
  73 


  74     /** method handle to scope getter for this ScriptFunction */
  75     public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
  76 
  77     /** Should specialized function and specialized constructors for the builtin be used if available? */
  78     private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
  79 
  80     /** Name of function or null. */
  81     private final String name;
  82 
  83     /** Source of function. */
  84     private final Source source;
  85 
  86     /** Start position and length in source. */
  87     private final long token;
  88 
  89     /** Reference to code for this method. */
  90     private final MethodHandle invoker;
  91 
  92     /** Reference to code for this method when called to create "new" object */
  93     protected MethodHandle constructor;
  94 
  95     /** Generic invoker to used in {@link #invoke(Object, Object...)}. */
  96     private MethodHandle genericInvoker;
  97 
  98     /** Generic constructor used in {@link #construct(Object, Object...)}. */
  99     private MethodHandle genericConstructor;
 100 
 101     /** Reference to constructor prototype. */
 102     protected Object prototype;
 103 
 104     /** Constructor to create a new instance. */
 105     private MethodHandle allocator;
 106 
 107     /** Map for new instance constructor. */
 108     private PropertyMap allocatorMap;
 109 
 110     /** The parent scope. */
 111     private final ScriptObject scope;
 112 
 113     /** Specializations - see @SpecializedFunction */
 114     private MethodHandle[] invokeSpecializations;
 115 
 116     /** Specializations - see @SpecializedFunction */
 117     private MethodHandle[] constructSpecializations;
 118 
 119     /** This field is either computed in constructor or set explicitly by calling setArity method. */
 120     private int arity;
 121 
 122     /**
 123      * Constructor
 124      *
 125      * @param name          function name
 126      * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
 127      * @param map           property map
 128      * @param scope         scope
 129      * @param specs         specialized version of this function - other method handles
 130      */
 131     protected ScriptFunction(
 132             final String name,
 133             final MethodHandle methodHandle,
 134             final PropertyMap map,
 135             final ScriptObject scope,
 136             final MethodHandle[] specs) {
 137         this(name, methodHandle, map, scope, needsCallee(methodHandle), specs);
 138     }
 139 
 140     /**
 141      * Heuristic to figure out if the method handle has a callee argument. If it's type is either
 142      * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
 143      * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
 144      * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
 145      * they also always receive a callee.
 146      * @param methodHandle the examined method handle
 147      * @return true if the method handle expects a callee, false otherwise
 148      */
 149     private static boolean needsCallee(MethodHandle methodHandle) {
 150         final MethodType type = methodHandle.type();
 151         final int len = type.parameterCount();
 152         if(len == 0) {
 153             return false;
 154         }
 155         if(type.parameterType(0) == boolean.class) {
 156             return len > 2 && type.parameterType(2) == ScriptFunction.class;
 157         }
 158         return len > 1 && type.parameterType(1) == ScriptFunction.class;
 159     }
 160 
 161     /**
 162      * Constructor
 163      *
 164      * @param data          static function data
 165      * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
 166      * @param map           property map
 167      * @param scope         scope


 168      * @param allocator     method handle to this function's allocator - see JO$ classes



 169      */
 170     protected ScriptFunction(
 171             final ScriptFunctionData data,
 172             final MethodHandle methodHandle,
 173             final PropertyMap map,
 174             final ScriptObject scope,
 175             final MethodHandle allocator) {
 176 
 177         super(map);
 178 
 179         if (Context.DEBUG) {
 180             constructorCount++;
 181         }
 182 
 183         this.name   = data.getName();
 184         this.source = data.getSource();
 185         this.token  = data.getToken();
 186         this.arity  = data.getArity();
 187         this.scope  = scope;
 188 
 189         if(data.needsCallee()) {
 190             setHasCalleeParameter();
 191         }
 192 
 193         this.invoker      = methodHandle;
 194         this.constructor  = methodHandle;
 195         this.allocator    = allocator;
 196         this.allocatorMap = data.getAllocatorMap();
 197     }
 198 
 199     /**
 200      * Constructor
 201      *
 202      * @param name              function name
 203      * @param methodHandle      method handle to function (if specializations are present, assumed to be most generic)
 204      * @param map               property map
 205      * @param scope             scope


 206      * @param needsCallee       does this method use the {@code callee} variable
 207      * @param specs             specialized version of this function - other method handles
 208      */
 209     protected ScriptFunction(
 210             final String name,
 211             final MethodHandle methodHandle,
 212             final PropertyMap map,
 213             final ScriptObject scope,


 214             final boolean needsCallee,
 215             final MethodHandle[] specs) {
 216 
 217         super(map);
 218 
 219         if (Context.DEBUG) {
 220             constructorCount++;
 221         }
 222 
 223         this.name   = name;
 224         this.source = null;
 225         this.token  = 0;
 226         this.scope  = scope;
 227         if(needsCallee) {
 228             setHasCalleeParameter();
 229         }
 230 
 231         final MethodType type       = methodHandle.type();
 232         final int        paramCount = type.parameterCount();
 233         final boolean    isVarArg   = type.parameterType(paramCount - 1).isArray();
 234 


 235         this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
 236 
 237         if (needsCallee && !isVarArg) {
 238             this.arity--;
 239         }
 240 
 241         if (isConstructor(methodHandle)) {



 242             if (!isVarArg) {
 243                 this.arity--;    // drop the boolean flag for arity
 244             }
 245             /*
 246              * We insert a boolean argument to tell if the method was invoked as
 247              * constructor or not if the method handle's first argument is boolean.
 248              */
 249             this.invoker     = MH.insertArguments(methodHandle, 0, false);
 250             this.constructor = MH.insertArguments(methodHandle, 0, true);
 251 
 252             if (specs != null) {
 253                 this.invokeSpecializations    = new MethodHandle[specs.length];
 254                 this.constructSpecializations = new MethodHandle[specs.length];
 255                 for (int i = 0; i < specs.length; i++) {
 256                     this.invokeSpecializations[i]    = MH.insertArguments(specs[i], 0, false);
 257                     this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
 258                 }
 259             }
 260         } else {
 261             this.invoker                  = methodHandle;
 262             this.constructor              = methodHandle;
 263             this.invokeSpecializations    = specs;
 264             this.constructSpecializations = specs;
 265         }
 266     }
 267 
 268     /**
 269      * Takes a method handle, and returns a potentially different method handle that can be used in
 270      * {@link #invoke(Object, Object...)} or {@link #construct(Object, Object...)}. The returned method handle
 271      * will be sure to return {@code Object}, and will have all its parameters turned into {@code Object} as well,
 272      * except for the following ones:
 273      * <ul>


 274      *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
 275      *   <li>the second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
 276      *   (callee) as an argument</li>
 277      * </ul>
 278      *
 279      * @param handle the original method handle
 280      * @return the new handle, conforming to the rules above.
 281      */
 282     private MethodHandle adaptMethodType(final MethodHandle handle) {
 283         final MethodType type = handle.type();
 284         MethodType newType = type.generic();
 285         if (isVarArg(handle)) {

 286             newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
 287         }
 288         if (hasCalleeParameter()) {
 289             newType = newType.changeParameterType(1, ScriptFunction.class);






 290         }
 291         return type.equals(newType) ? handle : handle.asType(newType);
 292     }
 293 
 294     @Override
 295     public String getClassName() {
 296         return "Function";
 297     }
 298 
 299     /**
 300      * ECMA 15.3.5.3 [[HasInstance]] (V)
 301      * Step 3 if "prototype" value is not an Object, throw TypeError
 302      */
 303     @Override
 304     public boolean isInstance(final ScriptObject instance) {
 305         if (!(prototype instanceof ScriptObject)) {
 306             typeError("prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype));
 307         }
 308 
 309         for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
 310             if (proto == prototype) {
 311                 return true;
 312             }
 313         }
 314 
 315         return false;
 316     }
 317 
 318     /**
 319      * Get the arity of this ScriptFunction
 320      * @return arity
 321      */
 322     public final int getArity() {
 323         return arity;
 324     }
 325 
 326     /**
 327      * Set the arity of this ScriptFunction
 328      * @param arity arity
 329      */
 330     public final void setArity(final int arity) {
 331         this.arity = arity;
 332     }
 333 
 334     /**
 335      * Is this a ECMAScript 'use strict' function?
 336      * @return true if function is in strict mode
 337      */
 338     public abstract boolean isStrict();
 339 
 340     /**
 341      * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ?
 342      * @return true if built-in
 343      */
 344     public abstract boolean isBuiltin();
 345 
 346     /**
 347      * Is this a non-strict and not-built-in script function?
 348      * @return true if neither strict nor built-in
 349      */
 350     public boolean isNonStrictFunction() {
 351         return !isStrict() && !isBuiltin();
 352     }
 353 
 354     /**
 355      * Execute this script function.
 356      * @param self  Target object.
 357      * @param arguments  Call arguments.
 358      * @return ScriptFunction result.
 359      * @throws Throwable if there is an exception/error with the invocation or thrown from it
 360      */
 361     public Object invoke(final Object self, final Object... arguments) throws Throwable {
 362         if (Context.DEBUG) {
 363             invokes++;
 364         }
 365 
 366         if (genericInvoker == null) {
 367             genericInvoker = adaptMethodType(invoker);
 368         }
 369 
 370         final Object selfObj = convertThisObject(self);
 371         final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 372 
 373         if (isVarArg(genericInvoker)) {
 374             if (hasCalleeParameter()) {
 375                 return genericInvoker.invokeExact(selfObj, this, args);
 376             }
 377             return genericInvoker.invokeExact(selfObj, args);
 378         }
 379 
 380         final int paramCount = genericInvoker.type().parameterCount();
 381         if (hasCalleeParameter()) {
 382             switch (paramCount) {
 383             case 2:
 384                 return genericInvoker.invokeExact(selfObj, this);
 385             case 3:
 386                 return genericInvoker.invokeExact(selfObj, this, getArg(args, 0));
 387             case 4:
 388                 return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
 389             case 5:
 390                 return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 391             default:
 392                 return genericInvoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
 393             }
 394         }
 395 
 396         switch (paramCount) {
 397         case 1:
 398             return genericInvoker.invokeExact(selfObj);
 399         case 2:
 400             return genericInvoker.invokeExact(selfObj, getArg(args, 0));
 401         case 3:
 402             return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
 403         case 4:
 404             return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 405         default:
 406             return genericInvoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
 407         }
 408     }
 409 
 410     private static Object getArg(final Object[] args, final int i) {
 411         return i < args.length ? args[i] : UNDEFINED;
 412     }
 413 
 414     /**
 415      * Construct new object using this constructor.
 416      * @param self  Target object.
 417      * @param args  Call arguments.
 418      * @return ScriptFunction result.
 419      * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it
 420      */
 421     public Object construct(final Object self, final Object... args) throws Throwable {
 422         if (constructor == null) {
 423             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
 424         }
 425 
 426         if (genericConstructor == null) {
 427             genericConstructor = adaptMethodType(constructor);
 428         }
 429         if (isVarArg(genericConstructor)) {
 430             if (hasCalleeParameter()) {
 431                 return genericConstructor.invokeExact(self, this, args);
 432             }
 433             return genericConstructor.invokeExact(self, args);
 434         }
 435 
 436         final int paramCount = genericConstructor.type().parameterCount();
 437         if (hasCalleeParameter()) {
 438             switch (paramCount) {
 439             case 2:
 440                 return genericConstructor.invokeExact(self, this);
 441             case 3:
 442                 return genericConstructor.invokeExact(self, this, getArg(args, 0));
 443             case 4:
 444                 return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
 445             case 5:
 446                 return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 447             default:
 448                 return genericConstructor.invokeWithArguments(withArguments(self, this, args));
 449             }
 450         }
 451 
 452         switch(paramCount) {
 453         case 1:
 454             return genericConstructor.invokeExact(self);
 455         case 2:
 456             return genericConstructor.invokeExact(self, getArg(args, 0));
 457         case 3:
 458             return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1));
 459         case 4:
 460             return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 461         default:
 462             return genericConstructor.invokeWithArguments(withArguments(self, null, args));
 463         }
 464     }
 465 
 466     private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) {
 467         return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function
 468     }
 469 
 470     private static Object[] withArguments(final Object self, final ScriptFunction function, final int paramCount, final Object... args) {
 471         final Object[] finalArgs = new Object[paramCount];
 472 
 473         finalArgs[0] = self;
 474         int nextArg = 1;
 475         if (function != null) {
 476             finalArgs[nextArg++] = function;
 477         }
 478 
 479         //don't add more args that there is paramcount in the handle (including self)
 480         final int maxArgs = Math.min(args.length, paramCount - (function == null ? 1 : 2));
 481         for (int i = 0; i < maxArgs;) {
 482             finalArgs[nextArg++] = args[i++];
 483         }
 484 
 485         //if we have fewer params than paramcount, pad undefined
 486         while (nextArg < paramCount) {
 487             finalArgs[nextArg++] = UNDEFINED;
 488         }
 489 
 490         return finalArgs;
 491     }
 492 
 493     /**
 494      * Allocate function. Called from generated {@link ScriptObject} code
 495      * for allocation as a factory method
 496      *
 497      * @return a new instance of the {@link ScriptObject} whose allocator this is
 498      */
 499     public Object allocate() {
 500         if (Context.DEBUG) {
 501             allocations++;
 502         }
 503 
 504         if (getConstructHandle() == null) {
 505             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
 506         }
 507 
 508         ScriptObject object = null;
 509 
 510         if (allocator != null) {
 511             try {
 512                 object = (ScriptObject)allocator.invokeExact(allocatorMap);
 513             } catch (final RuntimeException | Error e) {
 514                 throw e;
 515             } catch (final Throwable t) {
 516                 throw new RuntimeException(t);
 517             }
 518         }
 519 
 520         if (object != null) {
 521             if (prototype instanceof ScriptObject) {
 522                 object.setProto((ScriptObject)prototype);
 523             }
 524 
 525             if (object.getProto() == null) {
 526                 object.setProto(getObjectPrototype());
 527             }
 528         }
 529 
 530         return object;
 531     }
 532 
 533     /**
 534      * Return Object.prototype - used by "allocate"
 535      * @return Object.prototype
 536      */
 537     protected abstract ScriptObject getObjectPrototype();
 538 
 539     /**
 540      * Creates a version of this function bound to a specific "self" and other argumentss
 541      * @param self the self to bind the function to
 542      * @param args other arguments (beside self) to bind the function to
 543      * @return the bound function
 544      */
 545     public abstract ScriptFunction makeBoundFunction(Object self, Object[] args);
 546 
 547     /**
 548      * Test if a methodHandle refers to a constructor.
 549      * @param methodHandle MethodHandle to test.
 550      * @return True if method is a constructor.
 551      */
 552     private static boolean isConstructor(final MethodHandle methodHandle) {
 553         return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
 554     }
 555 
 556     /**
 557      * Test if a methodHandle refers to a variable argument method.
 558      * @param methodHandle MethodHandle to test.
 559      * @return True if variable arguments.
 560      */
 561     public boolean isVarArg(final MethodHandle methodHandle) {
 562         return hasCalleeParameter()
 563                 ? methodHandle.type().parameterCount() == 3 && methodHandle.type().parameterType(2).isArray()
 564                 : methodHandle.type().parameterCount() == 2 && methodHandle.type().parameterType(1).isArray();
 565     }
 566 
 567     @Override
 568     public final String safeToString() {
 569         return toSource();
 570     }
 571 
 572     @Override
 573     public String toString() {
 574         final StringBuilder sb = new StringBuilder();
 575 
 576         sb.append(super.toString())
 577             .append(" [ ")
 578             .append(invoker)
 579             .append(", ")
 580             .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
 581 
 582         if (source != null) {
 583             sb.append(" @ ")
 584                .append(source.getName())
 585                .append(':')
 586                .append(source.getLine(Token.descPosition(token)));
 587         }
 588         sb.append(" ]");
 589 
 590         return sb.toString();
 591     }
 592 
 593     /**
 594      * Get this function as a String containing its source code. If no source code
 595      * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
 596      * @return string representation of this function's source
 597      */
 598     public final String toSource() {
 599         if (source != null && token != 0) {
 600             return source.getString(Token.descPosition(token), Token.descLength(token));
 601         }
 602 
 603         return "function " + (name == null ? "" : name) + "() { [native code] }";
 604     }
 605 
 606     /**
 607      * Get the prototype object for this function
 608      * @return prototype
 609      */
 610     public final Object getPrototype() {
 611         return prototype;
 612     }
 613 
 614     /**
 615      * Set the prototype object for this function
 616      * @param prototype new prototype object
 617      * @return the prototype parameter
 618      */
 619     public final Object setPrototype(final Object prototype) {
 620         this.prototype = prototype;
 621         return prototype;
 622     }
 623 
 624     private static int weigh(final MethodType t) {
 625         int weight = Type.typeFor(t.returnType()).getWeight();
 626         for (final Class<?> paramType : t.parameterArray()) {
 627             final int pweight = Type.typeFor(paramType).getWeight();
 628             weight += pweight;
 629         }
 630         return weight;
 631     }
 632 
 633     private static boolean typeCompatible(final MethodType desc, final MethodType spec) {
 634         //spec must fit in desc
 635         final Class<?>[] dparray = desc.parameterArray();
 636         final Class<?>[] sparray = spec.parameterArray();
 637 
 638         if (dparray.length != sparray.length) {
 639             return false;
 640         }
 641 
 642         for (int i = 0; i < dparray.length; i++) {
 643             final Type dp = Type.typeFor(dparray[i]);
 644             final Type sp = Type.typeFor(sparray[i]);
 645 
 646             if (dp.isBoolean()) {
 647                 return false; //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
 648             }
 649 
 650             //specialization arguments must be at least as wide as dp, if not wider
 651             if (Type.widest(dp, sp) != sp) {
 652                 //e.g. specialization takes double and callsite says "object". reject.
 653                 //but if specialization says double and callsite says "int" or "long" or "double", that's fine
 654                 return false;
 655             }
 656         }
 657 
 658         return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
 659     }
 660 
 661     private MethodHandle candidateWithLowestWeight(final MethodType descType, final MethodHandle initialCandidate, final MethodHandle[] specs) {
 662         if (DISABLE_SPECIALIZATION || specs == null) {
 663             return initialCandidate;
 664         }
 665 
 666         int          minimumWeight = Integer.MAX_VALUE;
 667         MethodHandle candidate     = initialCandidate;
 668 
 669         for (final MethodHandle spec : specs) {
 670             final MethodType specType = spec.type();
 671 
 672             if (!typeCompatible(descType, specType)) {
 673                 continue;
 674             }
 675 
 676             //return type is ok. we want a wider or equal one for our callsite.
 677             final int specWeight = weigh(specType);
 678             if (specWeight < minimumWeight) {
 679                 candidate = spec;
 680                 minimumWeight = specWeight;
 681             }
 682         }
 683 
 684         if (DISABLE_SPECIALIZATION && candidate != initialCandidate) {
 685             Context.err("### Specializing builtin " + getName() + " -> " + candidate + "?");
 686         }
 687 
 688         return candidate;
 689     }
 690 
 691     /**
 692      * Return the most appropriate invoke handle if there are specializations
 693      * @param type most specific method type to look for invocation with
 694      * @return invoke method handle
 695      */
 696     public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) {
 697         return candidateWithLowestWeight(type, getInvokeHandle(), invokeSpecializations);
 698     }
 699 
 700     /**
 701      * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation
 702      * method handle for this ScriptFunction
 703      * @see SpecializedFunction
 704      * @return invokeHandle
 705      */
 706     public final MethodHandle getInvokeHandle() {
 707         return invoker;
 708     }
 709 
 710     /**
 711      * Return the invoke handle bound to a given ScriptObject self reference.
 712      * If callee parameter is required result is rebound to this.
 713      *
 714      * @param self self reference
 715      * @return bound invoke handle
 716      */
 717     public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
 718         final MethodHandle bound = MH.bindTo(getInvokeHandle(), self);
 719         return hasCalleeParameter() ? MH.bindTo(bound, this) : bound;
 720     }
 721 
 722     protected abstract boolean hasCalleeParameter();
 723     protected abstract void setHasCalleeParameter();
 724 
 725     /**
 726      * Get the construct handle - the most generic (and if no specializations are in place, only) constructor
 727      * method handle for this ScriptFunction
 728      * @see SpecializedConstructor
 729      * @param type type for wanted constructor
 730      * @return construct handle
 731      */
 732     public final MethodHandle getConstructHandle(final MethodType type) {
 733         return candidateWithLowestWeight(type, getConstructHandle(), constructSpecializations);
 734     }
 735 
 736     /**
 737      * Get a method handle to the constructor for this function
 738      * @return constructor handle
 739      */
 740     public final MethodHandle getConstructHandle() {
 741         return constructor;
 742     }
 743 
 744     /**
 745      * Set a method handle to the constructor for this function
 746      * @param constructHandle constructor handle
 747      */
 748     public final void setConstructHandle(final MethodHandle constructHandle) {
 749         this.constructor = constructHandle;
 750         this.constructSpecializations = null;
 751     }
 752 
 753     /**
 754      * Get the name for this function
 755      * @return the name
 756      */
 757     public final String getName() {
 758         return name;
 759     }
 760 
 761     /**
 762      * Does this script function need to be compiled. This determined by
 763      * null checking invokeHandle
 764      *
 765      * @return true if this needs compilation
 766      */
 767     public final boolean needsCompilation() {
 768         return invoker == null;
 769     }
 770 
 771     /**
 772      * Get token for this function
 773      * @return token
 774      */
 775     public final long getToken() {
 776         return token;
 777     }
 778 
 779     /**
 780      * Get the scope for this function
 781      * @return the scope
 782      */
 783     public final ScriptObject getScope() {
 784         return scope;
 785     }
 786 
 787     /**
 788      * Prototype getter for this ScriptFunction - follows the naming convention
 789      * used by Nasgen and the code generator
 790      *
 791      * @param self  self reference
 792      * @return self's prototype
 793      */
 794     public static Object G$prototype(final Object self) {
 795         return (self instanceof ScriptFunction) ?
 796             ((ScriptFunction)self).getPrototype() :
 797             UNDEFINED;
 798     }
 799 
 800     /**
 801      * Prototype setter for this ScriptFunction - follows the naming convention
 802      * used by Nasgen and the code generator
 803      *
 804      * @param self  self reference
 805      * @param prototype prototype to set
 806      */
 807     public static void S$prototype(final Object self, final Object prototype) {
 808         if (self instanceof ScriptFunction) {
 809             ((ScriptFunction)self).setPrototype(prototype);
 810         }
 811     }
 812 
 813     /**
 814      * Length getter - ECMA 15.3.3.2: Function.length
 815      * @param self self reference
 816      * @return length
 817      */
 818     public static int G$length(final Object self) {
 819         if (self instanceof ScriptFunction) {
 820             return ((ScriptFunction)self).getArity();
 821         }
 822 
 823         return 0;
 824     }
 825 
 826     /**
 827      * Name getter - ECMA Function.name
 828      * @param self self refence
 829      * @return the name, or undefined if none
 830      */
 831     public static Object G$name(final Object self) {
 832         if (self instanceof ScriptFunction) {
 833             return ((ScriptFunction)self).getName();
 834         }
 835 
 836         return UNDEFINED;
 837     }
 838 
 839     /**
 840      * Get the prototype for this ScriptFunction
 841      * @param constructor constructor
 842      * @return prototype, or null if given constructor is not a ScriptFunction
 843      */
 844     public static ScriptObject getPrototype(final Object constructor) {
 845         if (constructor instanceof ScriptFunction) {
 846             final Object proto = ((ScriptFunction)constructor).getPrototype();
 847             if (proto instanceof ScriptObject) {
 848                 return (ScriptObject)proto;
 849             }
 850         }
 851 
 852         return null;
 853     }
 854 
 855     // These counters are updated only in debug mode.
 856     private static int constructorCount;
 857     private static int invokes;
 858     private static int allocations;
 859 
 860     /**
 861      * @return the constructorCount
 862      */
 863     public static int getConstructorCount() {
 864         return constructorCount;
 865     }
 866 
 867     /**
 868      * @return the invokes
 869      */
 870     public static int getInvokes() {
 871         return invokes;
 872     }
 873 
 874     /**
 875      * @return the allocations
 876      */
 877     public static int getAllocations() {
 878         return allocations;
 879     }
 880 
 881     @Override
 882     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
 883         final MethodType type = desc.getMethodType();
 884         MethodHandle constructor = getConstructHandle(type);
 885 
 886         if (constructor == null) {
 887             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
 888             return null;
 889         }
 890 
 891         final MethodType ctorType = constructor.type();
 892 
 893         // guard against primitive constructor return types
 894         constructor = MH.asType(constructor, constructor.type().changeReturnType(Object.class));
 895 
 896         // apply new filter
 897         final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self
 898         MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor);
 899 
 900         if (hasCalleeParameter()) {
 901             handle = MH.foldArguments(handle, ALLOCATE);
 902         } else {
 903             handle = MH.filterArguments(handle, 0, ALLOCATE);
 904         }
 905 
 906         final MethodHandle filterIn = MH.asType(pairArguments(handle, type), type);
 907         return new GuardedInvocation(filterIn, null, NashornGuards.getFunctionGuard(this));
 908     }
 909 
 910     @SuppressWarnings("unused")
 911     private static Object newFilter(final Object result, final Object allocation) {
 912         return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
 913     }
 914 
 915     @SuppressWarnings("unused")
 916     private static Object wrapFilter(final Object obj) {
 917         if (obj instanceof ScriptObject || !isPrimitiveThis(obj)) {
 918             return obj;
 919         }
 920         return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj);
 921     }
 922 
 923     /**
 924      * dyn:call call site signature: (callee, thiz, [args...])
 925      * generated method signature:   (thiz, callee, [args...])
 926      *
 927      * cases:
 928      * (a) method has callee parameter
 929      *   (1) for local/scope calls, we just bind thiz and drop the second argument.
 930      *   (2) for normal this-calls, we have to swap thiz and callee to get matching signatures.
 931      * (b) method doesn't have callee parameter (builtin functions)
 932      *   (3) for local/scope calls, bind thiz and drop both callee and thiz.
 933      *   (4) for normal this-calls, drop callee.
 934      */
 935     @Override
 936     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
 937         final MethodType type = desc.getMethodType();
 938 
 939         if (request.isCallSiteUnstable()) {
 940             // (this, callee, args...) => (this, callee, args[])
 941             final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class,
 942                     type.parameterCount() - 2);
 943 
 944             return new GuardedInvocation(collector,
 945                     desc.getMethodType().parameterType(0) == ScriptFunction.class ? null : NashornGuards.getScriptFunctionGuard());
 946         }
 947 
 948         MethodHandle boundHandle;
 949         MethodHandle guard = null;
 950 
 951         if (hasCalleeParameter()) {
 952             final MethodHandle callHandle = getBestSpecializedInvokeHandle(type);
 953 
 954             if(NashornCallSiteDescriptor.isScope(desc)) {
 955                 // (this, callee, args...) => (callee, args...) => (callee, [this], args...)
 956                 boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
 957                 boundHandle = MH.dropArguments(boundHandle, 1, Object.class);
 958             } else {
 959                 // (this, callee, args...) permute => (callee, this, args...) which is what we get in
 960                 final MethodType oldType = callHandle.type();
 961                 final int[] reorder = new int[oldType.parameterCount()];
 962                 for (int i = 2; i < reorder.length; i++) {
 963                     reorder[i] = i;
 964                 }
 965                 reorder[0] = 1;
 966                 assert reorder[1] == 0;
 967                 final MethodType newType = oldType.changeParameterType(0, oldType.parameterType(1)).changeParameterType(1, oldType.parameterType(0));
 968                 boundHandle = MethodHandles.permuteArguments(callHandle, newType, reorder);
 969 
 970                 // For non-strict functions, check whether this-object is primitive type.
 971                 // If so add a to-object-wrapper argument filter.
 972                 // Else install a guard that will trigger a relink when the argument becomes primitive.
 973                 if (isNonStrictFunction()) {
 974                     if (isPrimitiveThis(request.getArguments()[1])) {
 975                         boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
 976                     } else {
 977                         guard = NashornGuards.getNonStrictFunctionGuard(this);
 978                     }
 979                 }
 980             }
 981         } else {
 982             final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1));
 983 
 984             if(NashornCallSiteDescriptor.isScope(desc)) {
 985                 boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
 986                 boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class);
 987             } else {
 988                 boundHandle = MH.dropArguments(callHandle, 0, Object.class);
 989             }
 990         }
 991 
 992         boundHandle = pairArguments(boundHandle, type);
 993         return new GuardedInvocation(boundHandle, guard == null ? NashornGuards.getFunctionGuard(this) : guard);
 994    }
 995 
 996     /**
 997      * Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
 998      *
 999      * These don't want a callee parameter, so bind that. Name binding is optional.
1000      */
1001     MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
1002         MethodHandle methodHandle = getBestSpecializedInvokeHandle(type);
1003 
1004         if (bindName != null) {
1005             if (hasCalleeParameter()) {
1006                 methodHandle = MH.insertArguments(methodHandle, 1, this, bindName);
1007             } else {
1008                 methodHandle = MH.insertArguments(methodHandle, 1, bindName);
1009             }
1010         } else {
1011             if (hasCalleeParameter()) {
1012                 methodHandle = MH.insertArguments(methodHandle, 1, this);
1013             }
1014         }
1015 
1016         return pairArguments(methodHandle, type);
1017     }
1018 
1019     /**
1020      * Convert this argument for non-strict functions according to ES 10.4.3
1021      *
1022      * @param thiz the this argument
1023      *
1024      * @return the converted this object
1025      */
1026     protected Object convertThisObject(final Object thiz) {
1027         if (!(thiz instanceof ScriptObject) && isNonStrictFunction()) {
1028             if (JSType.nullOrUndefined(thiz)) {
1029                 return Context.getGlobalTrusted();
1030             }
1031 
1032             if (isPrimitiveThis(thiz)) {
1033                 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
1034             }
1035         }
1036 
1037         return thiz;
1038     }
1039 
1040     private static boolean isPrimitiveThis(Object obj) {
1041         return obj instanceof String || obj instanceof ConsString ||
1042                obj instanceof Number || obj instanceof Boolean;
1043     }
1044 
1045     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
1046         final Class<?>   own = ScriptFunction.class;
1047         final MethodType mt  = MH.type(rtype, types);
1048         try {
1049             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
1050         } catch (final MethodHandleFactory.LookupException e) {
1051             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
1052         }
1053     }
1054 }
1055 
--- EOF ---