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 /** 725 * Check whether the ScriptFunction has callee parameter 726 * @return true if callee parameter 727 */ 728 protected abstract boolean hasCalleeParameter(); 729 730 /** 731 * Flag ScriptFunction as needing a callee parameter 732 */ 733 protected abstract void setHasCalleeParameter(); 734 735 /** 736 * Get the construct handle - the most generic (and if no specializations are in place, only) constructor 737 * method handle for this ScriptFunction 738 * @see SpecializedConstructor 739 * @param type type for wanted constructor 740 * @return construct handle 741 */ 742 public final MethodHandle getConstructHandle(final MethodType type) { 743 return candidateWithLowestWeight(type, getConstructHandle(), constructSpecializations); 744 } 745 746 /** 747 * Get a method handle to the constructor for this function 748 * @return constructor handle 749 */ 750 public final MethodHandle getConstructHandle() { 751 return constructHandle; 752 } 753 754 /** 755 * Set a method handle to the constructor for this function 756 * @param constructHandle constructor handle 757 */ 758 public final void setConstructHandle(final MethodHandle constructHandle) { 759 this.constructHandle = constructHandle; 760 this.constructSpecializations = null; 761 } 762 763 /** 764 * Get the name for this function 765 * @return the name 766 */ 767 public final String getName() { 768 return name; 769 } 770 771 /** 772 * Does this script function need to be compiled. This determined by 773 * null checking invokeHandle 774 * 775 * @return true if this needs compilation 776 */ 777 public final boolean needsCompilation() { 778 return invokeHandle == null; 779 } 780 781 /** 782 * Get token for this function 783 * @return token 784 */ 785 public final long getToken() { 786 return token; 787 } 788 789 /** 790 * Get the scope for this function 791 * @return the scope 792 */ 793 public final ScriptObject getScope() { 794 return scope; 795 } 796 797 /** 798 * Prototype getter for this ScriptFunction - follows the naming convention 799 * used by Nasgen and the code generator 800 * 801 * @param self self reference 802 * @return self's prototype 803 */ 804 public static Object G$prototype(final Object self) { 805 return (self instanceof ScriptFunction) ? 806 ((ScriptFunction)self).getPrototype() : 807 UNDEFINED; 808 } 809 810 /** 811 * Prototype setter for this ScriptFunction - follows the naming convention 812 * used by Nasgen and the code generator 813 * 814 * @param self self reference 815 * @param prototype prototype to set 816 */ 817 public static void S$prototype(final Object self, final Object prototype) { 818 if (self instanceof ScriptFunction) { 819 ((ScriptFunction)self).setPrototype(prototype); 820 } 821 } 822 823 /** 824 * Length getter - ECMA 15.3.3.2: Function.length 825 * @param self self reference 826 * @return length 827 */ 828 public static int G$length(final Object self) { 829 if (self instanceof ScriptFunction) { 830 return ((ScriptFunction)self).getArity(); 831 } 832 833 return 0; 834 } 835 836 /** 837 * Name getter - ECMA Function.name 838 * @param self self refence 839 * @return the name, or undefined if none 840 */ 841 public static Object G$name(final Object self) { 842 if (self instanceof ScriptFunction) { 843 return ((ScriptFunction)self).getName(); 844 } 845 846 return UNDEFINED; 847 } 848 849 /** 850 * Get the prototype for this ScriptFunction 851 * @param constructor constructor 852 * @return prototype, or null if given constructor is not a ScriptFunction 853 */ 854 public static ScriptObject getPrototype(final Object constructor) { 855 if (constructor instanceof ScriptFunction) { 856 final Object proto = ((ScriptFunction)constructor).getPrototype(); 857 if (proto instanceof ScriptObject) { 858 return (ScriptObject)proto; 859 } 860 } 861 862 return null; 863 } 864 865 // These counters are updated only in debug mode. 866 private static int constructorCount; 867 private static int invokes; 868 private static int allocations; 869 870 /** 871 * @return the constructorCount 872 */ 873 public static int getConstructorCount() { 874 return constructorCount; 875 } 876 877 /** 878 * @return the invokes 879 */ 880 public static int getInvokes() { 881 return invokes; 882 } 883 884 /** 885 * @return the allocations 886 */ 887 public static int getAllocations() { 888 return allocations; 889 } 890 891 @Override 892 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) { 893 final MethodType type = desc.getMethodType(); 894 MethodHandle constructor = getConstructHandle(type); 895 896 if (constructor == null) { 897 typeError("not.a.constructor", ScriptRuntime.safeToString(this)); 898 return null; 899 } 900 901 final MethodType ctorType = constructor.type(); 902 903 // guard against primitive constructor return types 904 constructor = MH.asType(constructor, constructor.type().changeReturnType(Object.class)); 905 906 // apply new filter 907 final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self 908 MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor); 909 910 if (hasCalleeParameter()) { 911 handle = MH.foldArguments(handle, ALLOCATE); 912 } else { 913 handle = MH.filterArguments(handle, 0, ALLOCATE); 914 } 915 916 final MethodHandle filterIn = MH.asType(pairArguments(handle, type), type); 917 return new GuardedInvocation(filterIn, null, NashornGuards.getFunctionGuard(this)); 918 } 919 920 @SuppressWarnings("unused") 921 private static Object newFilter(final Object result, final Object allocation) { 922 return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation; 923 } 924 925 @SuppressWarnings("unused") 926 private static Object wrapFilter(final Object obj) { 927 if (obj instanceof ScriptObject || !isPrimitiveThis(obj)) { 928 return obj; 929 } 930 return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj); 931 } 932 933 /** 934 * dyn:call call site signature: (callee, thiz, [args...]) 935 * generated method signature: (thiz, callee, [args...]) 936 * 937 * cases: 938 * (a) method has callee parameter 939 * (1) for local/scope calls, we just bind thiz and drop the second argument. 940 * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. 941 * (b) method doesn't have callee parameter (builtin functions) 942 * (3) for local/scope calls, bind thiz and drop both callee and thiz. 943 * (4) for normal this-calls, drop callee. 944 */ 945 @Override 946 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 947 final MethodType type = desc.getMethodType(); 948 949 if (request.isCallSiteUnstable()) { 950 // (this, callee, args...) => (this, callee, args[]) 951 final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, 952 type.parameterCount() - 2); 953 954 return new GuardedInvocation(collector, 955 desc.getMethodType().parameterType(0) == ScriptFunction.class ? null : NashornGuards.getScriptFunctionGuard()); 956 } 957 958 MethodHandle boundHandle; 959 MethodHandle guard = null; 960 961 if (hasCalleeParameter()) { 962 final MethodHandle callHandle = getBestSpecializedInvokeHandle(type); 963 964 if(NashornCallSiteDescriptor.isScope(desc)) { 965 // (this, callee, args...) => (callee, args...) => (callee, [this], args...) 966 boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); 967 boundHandle = MH.dropArguments(boundHandle, 1, Object.class); 968 } else { 969 // (this, callee, args...) permute => (callee, this, args...) which is what we get in 970 final MethodType oldType = callHandle.type(); 971 final int[] reorder = new int[oldType.parameterCount()]; 972 for (int i = 2; i < reorder.length; i++) { 973 reorder[i] = i; 974 } 975 reorder[0] = 1; 976 assert reorder[1] == 0; 977 final MethodType newType = oldType.changeParameterType(0, oldType.parameterType(1)).changeParameterType(1, oldType.parameterType(0)); 978 boundHandle = MethodHandles.permuteArguments(callHandle, newType, reorder); 979 980 // For non-strict functions, check whether this-object is primitive type. 981 // If so add a to-object-wrapper argument filter. 982 // Else install a guard that will trigger a relink when the argument becomes primitive. 983 if (isNonStrictFunction()) { 984 if (isPrimitiveThis(request.getArguments()[1])) { 985 boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER); 986 } else { 987 guard = NashornGuards.getNonStrictFunctionGuard(this); 988 } 989 } 990 } 991 } else { 992 final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1)); 993 994 if(NashornCallSiteDescriptor.isScope(desc)) { 995 boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); 996 boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class); 997 } else { 998 boundHandle = MH.dropArguments(callHandle, 0, Object.class); 999 } 1000 } 1001 1002 boundHandle = pairArguments(boundHandle, type); 1003 return new GuardedInvocation(boundHandle, guard == null ? NashornGuards.getFunctionGuard(this) : guard); 1004 } 1005 1006 /** 1007 * Used for noSuchMethod/noSuchProperty and JSAdapter hooks. 1008 * 1009 * These don't want a callee parameter, so bind that. Name binding is optional. 1010 */ 1011 MethodHandle getCallMethodHandle(final MethodType type, final String bindName) { 1012 MethodHandle methodHandle = getBestSpecializedInvokeHandle(type); 1013 1014 if (bindName != null) { 1015 if (hasCalleeParameter()) { 1016 methodHandle = MH.insertArguments(methodHandle, 1, this, bindName); 1017 } else { 1018 methodHandle = MH.insertArguments(methodHandle, 1, bindName); 1019 } 1020 } else { 1021 if (hasCalleeParameter()) { 1022 methodHandle = MH.insertArguments(methodHandle, 1, this); 1023 } 1024 } 1025 1026 return pairArguments(methodHandle, type); 1027 } 1028 1029 /** 1030 * Convert this argument for non-strict functions according to ES 10.4.3 1031 * 1032 * @param thiz the this argument 1033 * 1034 * @return the converted this object 1035 */ 1036 protected Object convertThisObject(final Object thiz) { 1037 if (!(thiz instanceof ScriptObject) && isNonStrictFunction()) { 1038 if (JSType.nullOrUndefined(thiz)) { 1039 return Context.getGlobalTrusted(); 1040 } 1041 1042 if (isPrimitiveThis(thiz)) { 1043 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz); 1044 } 1045 } 1046 1047 return thiz; 1048 } 1049 1050 private static boolean isPrimitiveThis(Object obj) { 1051 return obj instanceof String || obj instanceof ConsString || 1052 obj instanceof Number || obj instanceof Boolean; 1053 } 1054 1055 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 1056 final Class<?> own = ScriptFunction.class; 1057 final MethodType mt = MH.type(rtype, types); 1058 try { 1059 return MH.findStatic(MethodHandles.lookup(), own, name, mt); 1060 } catch (final MethodHandleFactory.LookupException e) { 1061 return MH.findVirtual(MethodHandles.lookup(), own, name, mt); 1062 } 1063 } 1064 } 1065 --- EOF ---