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.parser.Token;
  41 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
  42 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  43 import jdk.nashorn.internal.runtime.linker.NashornGuards;
  44 import jdk.nashorn.internal.runtime.options.Options;
  45 import org.dynalang.dynalink.CallSiteDescriptor;
  46 import org.dynalang.dynalink.linker.GuardedInvocation;
  47 import org.dynalang.dynalink.linker.LinkRequest;
  48 
  49 /**
  50  * Runtime representation of a JavaScript function.
  51  */
  52 public abstract class ScriptFunction extends ScriptObject {
  53 
  54     /** Method handle for prototype getter for this ScriptFunction */
  55     public static final MethodHandle G$PROTOTYPE  = findOwnMH("G$prototype",  Object.class, Object.class);
  56 
  57     /** Method handle for prototype setter for this ScriptFunction */
  58     public static final MethodHandle S$PROTOTYPE  = findOwnMH("S$prototype",  void.class, Object.class, Object.class);
  59 
  60     /** Method handle for length getter for this ScriptFunction */
  61     public static final MethodHandle G$LENGTH     = findOwnMH("G$length",     int.class, Object.class);
  62 
  63     /** Method handle for name getter for this ScriptFunction */
  64     public static final MethodHandle G$NAME       = findOwnMH("G$name",       Object.class, Object.class);
  65 
  66     /** Method handle for allocate function for this ScriptFunction */
  67     public static final MethodHandle ALLOCATE     = findOwnMH("allocate", Object.class);
  68 
  69     private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
  70 
  71     private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
  72 
  73     /** method handle to arity setter for this ScriptFunction */
  74     public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class);
  75     /** method handle to scope getter for this ScriptFunction */
  76     public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
  77 
  78     /** Should specialized function and specialized constructors for the builtin be used if available? */
  79     private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
  80 
  81     /** Name of function or null. */
  82     private final String name;
  83 
  84     /** Source of function. */
  85     private final Source source;
  86 
  87     /** Start position and length in source. */
  88     private final long token;
  89 
  90     /** Reference to code for this method. */
  91     private final MethodHandle invokeHandle;
  92 
  93     /** Reference to code for this method when called to create "new" object */
  94     protected MethodHandle constructHandle;






  95 
  96     /** Reference to constructor prototype. */
  97     protected Object prototype;
  98 
  99     /** Constructor to create a new instance. */
 100     private MethodHandle allocator;
 101 
 102     /** Map for new instance constructor. */
 103     private PropertyMap allocatorMap;
 104 
 105     /** The parent scope. */
 106     private final ScriptObject scope;
 107 
 108     /** Specializations - see @SpecializedFunction */
 109     private MethodHandle[] invokeSpecializations;
 110 
 111     /** Specializations - see @SpecializedFunction */
 112     private MethodHandle[] constructSpecializations;
 113 
 114     /** This field is either computed in constructor or set explicitly by calling setArity method. */
 115     private int arity;
 116 
 117     /**
 118      * Constructor
 119      *
 120      * @param name          function name
 121      * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
 122      * @param map           property map
 123      * @param scope         scope
 124      * @param specs         specialized version of this function - other method handles
 125      */
 126     protected ScriptFunction(
 127             final String name,
 128             final MethodHandle methodHandle,
 129             final PropertyMap map,
 130             final ScriptObject scope,
 131             final MethodHandle[] specs) {
 132         this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs);
 133     }
 134 
 135     /**
 136      * Heuristic to figure out if the method handle has a callee argument. If it's type is either
 137      * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
 138      * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
 139      * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
 140      * they also always receive a callee.
 141      * @param methodHandle the examined method handle
 142      * @return true if the method handle expects a callee, false otherwise
 143      */
 144     private static boolean needsCallee(MethodHandle methodHandle) {
 145         final MethodType type = methodHandle.type();
 146         final int len = type.parameterCount();
 147         if(len == 0) {
 148             return false;
 149         }
 150         if(type.parameterType(0) == boolean.class) {
 151             return len > 2 && type.parameterType(2) == ScriptFunction.class;
 152         }
 153         return len > 1 && type.parameterType(1) == ScriptFunction.class;
 154     }
 155 
 156     /**
 157      * Constructor
 158      *
 159      * @param name          function name
 160      * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
 161      * @param map           property map
 162      * @param scope         scope
 163      * @param source        the source
 164      * @param token         token
 165      * @param allocator     method handle to this function's allocator - see JO$ classes
 166      * @param allocatorMap  property map to be used for all constructors
 167      * @param needsCallee   does this method use the {@code callee} variable
 168      * @param specs         specialized version of this function - other method handles
 169      */
 170     protected ScriptFunction(
 171             final String name,
 172             final MethodHandle methodHandle,
 173             final PropertyMap map,
 174             final ScriptObject scope,
 175             final Source source,
 176             final long token,
 177             final MethodHandle allocator,
 178             final PropertyMap allocatorMap,
 179             final boolean needsCallee,
 180             final MethodHandle[] specs) {

 181 
 182         this(name, methodHandle, map, scope, source, token, needsCallee, specs);




 183 
 184         //this is the internal constructor


 185 


 186         this.allocator    = allocator;
 187         this.allocatorMap = allocatorMap;
 188     }
 189 
 190     /**
 191      * Constructor
 192      *
 193      * @param name              function name
 194      * @param methodHandle      method handle to function (if specializations are present, assumed to be most generic)
 195      * @param map               property map
 196      * @param scope             scope
 197      * @param source            the source
 198      * @param token             token
 199      * @param needsCallee       does this method use the {@code callee} variable
 200      * @param specs             specialized version of this function - other method handles
 201      */
 202     protected ScriptFunction(
 203             final String name,
 204             final MethodHandle methodHandle,
 205             final PropertyMap map,
 206             final ScriptObject scope,
 207             final Source source,
 208             final long token,
 209             final boolean needsCallee,
 210             final MethodHandle[] specs) {
 211 
 212         super(map);
 213 
 214         if (Context.DEBUG) {
 215             constructorCount++;
 216         }
 217 
 218         this.name   = name;
 219         this.source = source;
 220         this.token  = token;
 221         this.scope  = scope;
 222         if(needsCallee) {
 223             setHasCalleeParameter();
 224         }
 225 
 226         final MethodType type       = methodHandle.type();
 227         final int        paramCount = type.parameterCount();
 228         final boolean    isVarArg   = type.parameterType(paramCount - 1).isArray();
 229 
 230         final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg));
 231 
 232         this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
 233 
 234         if (needsCallee && !isVarArg) {
 235             this.arity--;
 236         }
 237 
 238         if (scope != null) {
 239             this.invokeHandle    = mh;
 240             this.constructHandle = mh;
 241         } else if (isConstructor(mh)) {
 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.invokeHandle    = MH.insertArguments(mh, 0, false);
 250             this.constructHandle = MH.insertArguments(mh, 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.invokeHandle             = mh;
 262             this.constructHandle          = mh;
 263             this.invokeSpecializations    = specs;
 264             this.constructSpecializations = specs;
 265         }
 266     }
 267 
 268     /**
 269      * Takes a method type, and returns a (potentially different method type) that the method handles used by
 270      * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and
 271      * {@link #construct(Object, Object...)}. The returned method type will be sure to return {@code Object}, and will
 272      * have all its parameters turned into {@code Object} as well, except for the following ones:
 273      * <ul>
 274      * <li>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor
 275      * invocation,</li>
 276      * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
 277      * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be
 278      * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li>
 279      * @param type the original type
 280      * @param hasCallee true if the function uses the callee argument
 281      * @param isVarArg if the function is a vararg
 282      * @return the new type, conforming to the rules above.
 283      */
 284     private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) {
 285         // Generify broadly
 286         MethodType newType = type.generic().changeReturnType(Object.class);
 287         if(isVarArg) {
 288             // Change back to vararg if we over-generified it
 289             newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
 290         }
 291         final boolean hasBoolean = type.parameterType(0) == boolean.class;
 292         if(hasBoolean) {
 293             // Restore the initial boolean argument
 294             newType = newType.changeParameterType(0, boolean.class);
 295         }
 296         if(hasCallee) {
 297             // Restore the ScriptFunction argument
 298             newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class);
 299         }
 300         return newType;
 301     }
 302 
 303     @Override
 304     public String getClassName() {
 305         return "Function";
 306     }
 307 
 308     /**
 309      * ECMA 15.3.5.3 [[HasInstance]] (V)
 310      * Step 3 if "prototype" value is not an Object, throw TypeError
 311      */
 312     @Override
 313     public boolean isInstance(final ScriptObject instance) {
 314         if (!(prototype instanceof ScriptObject)) {
 315             typeError("prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype));
 316         }
 317 
 318         for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
 319             if (proto == prototype) {
 320                 return true;
 321             }
 322         }
 323 
 324         return false;
 325     }
 326 
 327     /**
 328      * Get the arity of this ScriptFunction
 329      * @return arity
 330      */
 331     public final int getArity() {
 332         return arity;
 333     }
 334 
 335     /**
 336      * Set the arity of this ScriptFunction
 337      * @param arity arity
 338      */
 339     public final void setArity(final int arity) {
 340         this.arity = arity;
 341     }
 342 
 343     /**
 344      * Is this a ECMAScript 'use strict' function?
 345      * @return true if function is in strict mode
 346      */
 347     public abstract boolean isStrict();
 348 
 349     /**
 350      * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ?
 351      * @return true if built-in
 352      */
 353     public abstract boolean isBuiltin();
 354 
 355     /**
 356      * Is this a non-strict and not-built-in script function?
 357      * @return true if neither strict nor built-in
 358      */
 359     public boolean isNonStrictFunction() {
 360         return !isStrict() && !isBuiltin();
 361     }
 362 
 363     /**
 364      * Execute this script function.
 365      * @param self  Target object.
 366      * @param arguments  Call arguments.
 367      * @return ScriptFunction result.
 368      * @throws Throwable if there is an exception/error with the invocation or thrown from it
 369      */
 370     public Object invoke(final Object self, final Object... arguments) throws Throwable {
 371         if (Context.DEBUG) {
 372             invokes++;
 373         }
 374 




 375         final Object selfObj = convertThisObject(self);
 376         final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 377 
 378         if (isVarArg(invokeHandle)) {
 379             if (hasCalleeParameter()) {
 380                 return invokeHandle.invokeExact(selfObj, this, args);
 381             }
 382             return invokeHandle.invokeExact(selfObj, args);
 383         }
 384 
 385         final int paramCount = invokeHandle.type().parameterCount();
 386         if (hasCalleeParameter()) {
 387             switch (paramCount) {
 388             case 2:
 389                 return invokeHandle.invokeExact(selfObj, this);
 390             case 3:
 391                 return invokeHandle.invokeExact(selfObj, this, getArg(args, 0));
 392             case 4:
 393                 return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
 394             case 5:
 395                 return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 396             default:
 397                 return invokeHandle.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
 398             }
 399         }
 400 
 401         switch (paramCount) {
 402         case 1:
 403             return invokeHandle.invokeExact(selfObj);
 404         case 2:
 405             return invokeHandle.invokeExact(selfObj, getArg(args, 0));
 406         case 3:
 407             return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
 408         case 4:
 409             return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
 410         default:
 411             return invokeHandle.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
 412         }
 413     }
 414 
 415     private static Object getArg(final Object[] args, final int i) {
 416         return i < args.length ? args[i] : UNDEFINED;
 417     }
 418 
 419     /**
 420      * Construct new object using this constructor.
 421      * @param self  Target object.
 422      * @param args  Call arguments.
 423      * @return ScriptFunction result.
 424      * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it
 425      */
 426     public Object construct(final Object self, final Object... args) throws Throwable {
 427         if (constructHandle == null) {
 428             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
 429         }
 430 
 431         if (isVarArg(constructHandle)) {



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