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