1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime; 27 28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 31 import static jdk.nashorn.internal.runtime.linker.Lookup.MH; 32 33 import java.lang.invoke.MethodHandle; 34 import java.lang.invoke.MethodHandles; 35 import java.lang.invoke.MethodType; 36 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 37 import jdk.nashorn.internal.codegen.ScriptFunctionData; 38 import jdk.nashorn.internal.codegen.types.Type; 39 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor; 40 import jdk.nashorn.internal.objects.annotations.SpecializedFunction; 41 import jdk.nashorn.internal.parser.Token; 42 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory; 43 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 44 import jdk.nashorn.internal.runtime.linker.NashornGuards; 45 import jdk.nashorn.internal.runtime.options.Options; 46 import org.dynalang.dynalink.CallSiteDescriptor; 47 import org.dynalang.dynalink.linker.GuardedInvocation; 48 import org.dynalang.dynalink.linker.LinkRequest; 49 50 /** 51 * Runtime representation of a JavaScript function. 52 */ 53 public abstract class ScriptFunction extends ScriptObject { 54 55 /** Method handle for prototype getter for this ScriptFunction */ 56 public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class); 57 58 /** Method handle for prototype setter for this ScriptFunction */ 59 public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class); 60 61 /** Method handle for length getter for this ScriptFunction */ 62 public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class); 63 64 /** Method handle for name getter for this ScriptFunction */ 65 public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class); 66 67 /** Method handle for allocate function for this ScriptFunction */ 68 public static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class); 69 70 private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); 71 72 private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class); 73 74 /** method handle to scope getter for this ScriptFunction */ 75 public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); 76 77 /** Should specialized function and specialized constructors for the builtin be used if available? */ 78 private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable"); 79 80 /** Name of function or null. */ 81 private final String name; 82 83 /** Source of function. */ 84 private final Source source; 85 86 /** Start position and length in source. */ 87 private final long token; 88 89 /** Reference to code for this method. */ 90 private final MethodHandle invoker; 91 92 /** Reference to code for this method when called to create "new" object */ 93 protected MethodHandle constructor; 94 95 /** Generic invoker to used in {@link #invoke(Object, Object...)}. */ 96 private MethodHandle genericInvoker; 97 98 /** Generic constructor used in {@link #construct(Object, Object...)}. */ 99 private MethodHandle genericConstructor; 100 101 /** Reference to constructor prototype. */ 102 protected Object prototype; 103 104 /** Constructor to create a new instance. */ 105 private MethodHandle allocator; 106 107 /** Map for new instance constructor. */ 108 private PropertyMap allocatorMap; 109 110 /** The parent scope. */ 111 private final ScriptObject scope; 112 113 /** Specializations - see @SpecializedFunction */ 114 private MethodHandle[] invokeSpecializations; 115 116 /** Specializations - see @SpecializedFunction */ 117 private MethodHandle[] constructSpecializations; 118 119 /** This field is either computed in constructor or set explicitly by calling setArity method. */ 120 private int arity; 121 122 /** 123 * Constructor 124 * 125 * @param name function name 126 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) 127 * @param map property map 128 * @param scope scope 129 * @param specs specialized version of this function - other method handles 130 */ 131 protected ScriptFunction( 132 final String name, 133 final MethodHandle methodHandle, 134 final PropertyMap map, 135 final ScriptObject scope, 136 final MethodHandle[] specs) { 137 this(name, methodHandle, map, scope, needsCallee(methodHandle), specs); 138 } 139 140 /** 141 * Heuristic to figure out if the method handle has a callee argument. If it's type is either 142 * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has 143 * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly 144 * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore 145 * they also always receive a callee. 146 * @param methodHandle the examined method handle 147 * @return true if the method handle expects a callee, false otherwise 148 */ 149 private static boolean needsCallee(MethodHandle methodHandle) { 150 final MethodType type = methodHandle.type(); 151 final int len = type.parameterCount(); 152 if(len == 0) { 153 return false; 154 } 155 if(type.parameterType(0) == boolean.class) { 156 return len > 2 && type.parameterType(2) == ScriptFunction.class; 157 } 158 return len > 1 && type.parameterType(1) == ScriptFunction.class; 159 } 160 161 /** 162 * Constructor 163 * 164 * @param data static function data 165 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) 166 * @param map property map 167 * @param scope scope 168 * @param allocator method handle to this function's allocator - see JO$ classes 169 */ 170 protected ScriptFunction( 171 final ScriptFunctionData data, 172 final MethodHandle methodHandle, 173 final PropertyMap map, 174 final ScriptObject scope, 175 final MethodHandle allocator) { 176 177 super(map); 178 179 if (Context.DEBUG) { 180 constructorCount++; 181 } 182 183 this.name = data.getName(); 184 this.source = data.getSource(); 185 this.token = data.getToken(); 186 this.arity = data.getArity(); 187 this.scope = scope; 188 189 if(data.needsCallee()) { 190 setHasCalleeParameter(); 191 } 192 193 this.invoker = methodHandle; 194 this.constructor = methodHandle; 195 this.allocator = allocator; 196 this.allocatorMap = data.getAllocatorMap(); 197 } 198 199 /** 200 * Constructor 201 * 202 * @param name function name 203 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) 204 * @param map property map 205 * @param scope scope 206 * @param needsCallee does this method use the {@code callee} variable 207 * @param specs specialized version of this function - other method handles 208 */ 209 protected ScriptFunction( 210 final String name, 211 final MethodHandle methodHandle, 212 final PropertyMap map, 213 final ScriptObject scope, 214 final boolean needsCallee, 215 final MethodHandle[] specs) { 216 217 super(map); 218 219 if (Context.DEBUG) { 220 constructorCount++; 221 } 222 223 this.name = name; 224 this.source = null; 225 this.token = 0; 226 this.scope = scope; 227 if(needsCallee) { 228 setHasCalleeParameter(); 229 } 230 231 final MethodType type = methodHandle.type(); 232 final int paramCount = type.parameterCount(); 233 final boolean isVarArg = type.parameterType(paramCount - 1).isArray(); 234 235 this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity 236 237 if (needsCallee && !isVarArg) { 238 this.arity--; 239 } 240 241 if (isConstructor(methodHandle)) { 242 if (!isVarArg) { 243 this.arity--; // drop the boolean flag for arity 244 } 245 /* 246 * We insert a boolean argument to tell if the method was invoked as 247 * constructor or not if the method handle's first argument is boolean. 248 */ 249 this.invoker = MH.insertArguments(methodHandle, 0, false); 250 this.constructor = MH.insertArguments(methodHandle, 0, true); 251 252 if (specs != null) { 253 this.invokeSpecializations = new MethodHandle[specs.length]; 254 this.constructSpecializations = new MethodHandle[specs.length]; 255 for (int i = 0; i < specs.length; i++) { 256 this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false); 257 this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true); 258 } 259 } 260 } else { 261 this.invoker = methodHandle; 262 this.constructor = methodHandle; 263 this.invokeSpecializations = specs; 264 this.constructSpecializations = specs; 265 } 266 } 267 268 /** 269 * Takes a method handle, and returns a potentially different method handle that can be used in 270 * {@link #invoke(Object, Object...)} or {@link #construct(Object, Object...)}. The returned method handle 271 * will be sure to return {@code Object}, and will have all its parameters turned into {@code Object} as well, 272 * except for the following ones: 273 * <ul> 274 * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> 275 * <li>the second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself 276 * (callee) as an argument</li> 277 * </ul> 278 * 279 * @param handle the original method handle 280 * @return the new handle, conforming to the rules above. 281 */ 282 private MethodHandle adaptMethodType(final MethodHandle handle) { 283 final MethodType type = handle.type(); 284 MethodType newType = type.generic(); 285 if (isVarArg(handle)) { 286 newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); 287 } 288 if (hasCalleeParameter()) { 289 newType = newType.changeParameterType(1, ScriptFunction.class); 290 } 291 return type.equals(newType) ? handle : handle.asType(newType); 292 } 293 294 @Override 295 public String getClassName() { 296 return "Function"; 297 } 298 299 /** 300 * ECMA 15.3.5.3 [[HasInstance]] (V) 301 * Step 3 if "prototype" value is not an Object, throw TypeError 302 */ 303 @Override 304 public boolean isInstance(final ScriptObject instance) { 305 if (!(prototype instanceof ScriptObject)) { 306 typeError("prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype)); 307 } 308 309 for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) { 310 if (proto == prototype) { 311 return true; 312 } 313 } 314 315 return false; 316 } 317 318 /** 319 * Get the arity of this ScriptFunction 320 * @return arity 321 */ 322 public final int getArity() { 323 return arity; 324 } 325 326 /** 327 * Set the arity of this ScriptFunction 328 * @param arity arity 329 */ 330 public final void setArity(final int arity) { 331 this.arity = arity; 332 } 333 334 /** 335 * Is this a ECMAScript 'use strict' function? 336 * @return true if function is in strict mode 337 */ 338 public abstract boolean isStrict(); 339 340 /** 341 * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ? 342 * @return true if built-in 343 */ 344 public abstract boolean isBuiltin(); 345 346 /** 347 * Is this a non-strict and not-built-in script function? 348 * @return true if neither strict nor built-in 349 */ 350 public boolean isNonStrictFunction() { 351 return !isStrict() && !isBuiltin(); 352 } 353 354 /** 355 * Execute this script function. 356 * @param self Target object. 357 * @param arguments Call arguments. 358 * @return ScriptFunction result. 359 * @throws Throwable if there is an exception/error with the invocation or thrown from it 360 */ 361 public Object invoke(final Object self, final Object... arguments) throws Throwable { 362 if (Context.DEBUG) { 363 invokes++; 364 } 365 366 if (genericInvoker == null) { 367 genericInvoker = adaptMethodType(invoker); 368 } 369 370 final Object selfObj = convertThisObject(self); 371 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 372 373 if (isVarArg(genericInvoker)) { 374 if (hasCalleeParameter()) { 375 return genericInvoker.invokeExact(selfObj, this, args); 376 } 377 return genericInvoker.invokeExact(selfObj, args); 378 } 379 380 final int paramCount = genericInvoker.type().parameterCount(); 381 if (hasCalleeParameter()) { 382 switch (paramCount) { 383 case 2: 384 return genericInvoker.invokeExact(selfObj, this); 385 case 3: 386 return genericInvoker.invokeExact(selfObj, this, getArg(args, 0)); 387 case 4: 388 return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1)); 389 case 5: 390 return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 391 default: 392 return genericInvoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args)); 393 } 394 } 395 396 switch (paramCount) { 397 case 1: 398 return genericInvoker.invokeExact(selfObj); 399 case 2: 400 return genericInvoker.invokeExact(selfObj, getArg(args, 0)); 401 case 3: 402 return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); 403 case 4: 404 return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 405 default: 406 return genericInvoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args)); 407 } 408 } 409 410 private static Object getArg(final Object[] args, final int i) { 411 return i < args.length ? args[i] : UNDEFINED; 412 } 413 414 /** 415 * Construct new object using this constructor. 416 * @param self Target object. 417 * @param args Call arguments. 418 * @return ScriptFunction result. 419 * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it 420 */ 421 public Object construct(final Object self, final Object... args) throws Throwable { 422 if (constructor == null) { 423 typeError("not.a.constructor", ScriptRuntime.safeToString(this)); 424 } 425 426 if (genericConstructor == null) { 427 genericConstructor = adaptMethodType(constructor); 428 } 429 if (isVarArg(genericConstructor)) { 430 if (hasCalleeParameter()) { 431 return genericConstructor.invokeExact(self, this, args); 432 } 433 return genericConstructor.invokeExact(self, args); 434 } 435 436 final int paramCount = genericConstructor.type().parameterCount(); 437 if (hasCalleeParameter()) { 438 switch (paramCount) { 439 case 2: 440 return genericConstructor.invokeExact(self, this); 441 case 3: 442 return genericConstructor.invokeExact(self, this, getArg(args, 0)); 443 case 4: 444 return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1)); 445 case 5: 446 return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 447 default: 448 return genericConstructor.invokeWithArguments(withArguments(self, this, args)); 449 } 450 } 451 452 switch(paramCount) { 453 case 1: 454 return genericConstructor.invokeExact(self); 455 case 2: 456 return genericConstructor.invokeExact(self, getArg(args, 0)); 457 case 3: 458 return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1)); 459 case 4: 460 return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 461 default: 462 return genericConstructor.invokeWithArguments(withArguments(self, null, args)); 463 } 464 } 465 466 private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) { 467 return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function 468 } 469 470 private static Object[] withArguments(final Object self, final ScriptFunction function, final int paramCount, final Object... args) { 471 final Object[] finalArgs = new Object[paramCount]; 472 473 finalArgs[0] = self; 474 int nextArg = 1; 475 if (function != null) { 476 finalArgs[nextArg++] = function; 477 } 478 479 //don't add more args that there is paramcount in the handle (including self) 480 final int maxArgs = Math.min(args.length, paramCount - (function == null ? 1 : 2)); 481 for (int i = 0; i < maxArgs;) { 482 finalArgs[nextArg++] = args[i++]; 483 } 484 485 //if we have fewer params than paramcount, pad undefined 486 while (nextArg < paramCount) { 487 finalArgs[nextArg++] = UNDEFINED; 488 } 489 490 return finalArgs; 491 } 492 493 /** 494 * Allocate function. Called from generated {@link ScriptObject} code 495 * for allocation as a factory method 496 * 497 * @return a new instance of the {@link ScriptObject} whose allocator this is 498 */ 499 public Object allocate() { 500 if (Context.DEBUG) { 501 allocations++; 502 } 503 504 if (getConstructHandle() == null) { 505 typeError("not.a.constructor", ScriptRuntime.safeToString(this)); 506 } 507 508 ScriptObject object = null; 509 510 if (allocator != null) { 511 try { 512 object = (ScriptObject)allocator.invokeExact(allocatorMap); 513 } catch (final RuntimeException | Error e) { 514 throw e; 515 } catch (final Throwable t) { 516 throw new RuntimeException(t); 517 } 518 } 519 520 if (object != null) { 521 if (prototype instanceof ScriptObject) { 522 object.setProto((ScriptObject)prototype); 523 } 524 525 if (object.getProto() == null) { 526 object.setProto(getObjectPrototype()); 527 } 528 } 529 530 return object; 531 } 532 533 /** 534 * Return Object.prototype - used by "allocate" 535 * @return Object.prototype 536 */ 537 protected abstract ScriptObject getObjectPrototype(); 538 539 /** 540 * Creates a version of this function bound to a specific "self" and other argumentss 541 * @param self the self to bind the function to 542 * @param args other arguments (beside self) to bind the function to 543 * @return the bound function 544 */ 545 public abstract ScriptFunction makeBoundFunction(Object self, Object[] args); 546 547 /** 548 * Test if a methodHandle refers to a constructor. 549 * @param methodHandle MethodHandle to test. 550 * @return True if method is a constructor. 551 */ 552 private static boolean isConstructor(final MethodHandle methodHandle) { 553 return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class; 554 } 555 556 /** 557 * Test if a methodHandle refers to a variable argument method. 558 * @param methodHandle MethodHandle to test. 559 * @return True if variable arguments. 560 */ 561 public boolean isVarArg(final MethodHandle methodHandle) { 562 return hasCalleeParameter() 563 ? methodHandle.type().parameterCount() == 3 && methodHandle.type().parameterType(2).isArray() 564 : methodHandle.type().parameterCount() == 2 && methodHandle.type().parameterType(1).isArray(); 565 } 566 567 @Override 568 public final String safeToString() { 569 return toSource(); 570 } 571 572 @Override 573 public String toString() { 574 final StringBuilder sb = new StringBuilder(); 575 576 sb.append(super.toString()) 577 .append(" [ ") 578 .append(invoker) 579 .append(", ") 580 .append((name == null || name.isEmpty()) ? "<anonymous>" : name); 581 582 if (source != null) { 583 sb.append(" @ ") 584 .append(source.getName()) 585 .append(':') 586 .append(source.getLine(Token.descPosition(token))); 587 } 588 sb.append(" ]"); 589 590 return sb.toString(); 591 } 592 593 /** 594 * Get this function as a String containing its source code. If no source code 595 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} 596 * @return string representation of this function's source 597 */ 598 public final String toSource() { 599 if (source != null && token != 0) { 600 return source.getString(Token.descPosition(token), Token.descLength(token)); 601 } 602 603 return "function " + (name == null ? "" : name) + "() { [native code] }"; 604 } 605 606 /** 607 * Get the prototype object for this function 608 * @return prototype 609 */ 610 public final Object getPrototype() { 611 return prototype; 612 } 613 614 /** 615 * Set the prototype object for this function 616 * @param prototype new prototype object 617 * @return the prototype parameter 618 */ 619 public final Object setPrototype(final Object prototype) { 620 this.prototype = prototype; 621 return prototype; 622 } 623 624 private static int weigh(final MethodType t) { 625 int weight = Type.typeFor(t.returnType()).getWeight(); 626 for (final Class<?> paramType : t.parameterArray()) { 627 final int pweight = Type.typeFor(paramType).getWeight(); 628 weight += pweight; 629 } 630 return weight; 631 } 632 633 private static boolean typeCompatible(final MethodType desc, final MethodType spec) { 634 //spec must fit in desc 635 final Class<?>[] dparray = desc.parameterArray(); 636 final Class<?>[] sparray = spec.parameterArray(); 637 638 if (dparray.length != sparray.length) { 639 return false; 640 } 641 642 for (int i = 0; i < dparray.length; i++) { 643 final Type dp = Type.typeFor(dparray[i]); 644 final Type sp = Type.typeFor(sparray[i]); 645 646 if (dp.isBoolean()) { 647 return false; //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution 648 } 649 650 //specialization arguments must be at least as wide as dp, if not wider 651 if (Type.widest(dp, sp) != sp) { 652 //e.g. specialization takes double and callsite says "object". reject. 653 //but if specialization says double and callsite says "int" or "long" or "double", that's fine 654 return false; 655 } 656 } 657 658 return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic. 659 } 660 661 private MethodHandle candidateWithLowestWeight(final MethodType descType, final MethodHandle initialCandidate, final MethodHandle[] specs) { 662 if (DISABLE_SPECIALIZATION || specs == null) { 663 return initialCandidate; 664 } 665 666 int minimumWeight = Integer.MAX_VALUE; 667 MethodHandle candidate = initialCandidate; 668 669 for (final MethodHandle spec : specs) { 670 final MethodType specType = spec.type(); 671 672 if (!typeCompatible(descType, specType)) { 673 continue; 674 } 675 676 //return type is ok. we want a wider or equal one for our callsite. 677 final int specWeight = weigh(specType); 678 if (specWeight < minimumWeight) { 679 candidate = spec; 680 minimumWeight = specWeight; 681 } 682 } 683 684 if (DISABLE_SPECIALIZATION && candidate != initialCandidate) { 685 Context.err("### Specializing builtin " + getName() + " -> " + candidate + "?"); 686 } 687 688 return candidate; 689 } 690 691 /** 692 * Return the most appropriate invoke handle if there are specializations 693 * @param type most specific method type to look for invocation with 694 * @return invoke method handle 695 */ 696 public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) { 697 return candidateWithLowestWeight(type, getInvokeHandle(), invokeSpecializations); 698 } 699 700 /** 701 * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation 702 * method handle for this ScriptFunction 703 * @see SpecializedFunction 704 * @return invokeHandle 705 */ 706 public final MethodHandle getInvokeHandle() { 707 return invoker; 708 } 709 710 /** 711 * Return the invoke handle bound to a given ScriptObject self reference. 712 * If callee parameter is required result is rebound to this. 713 * 714 * @param self self reference 715 * @return bound invoke handle 716 */ 717 public final MethodHandle getBoundInvokeHandle(final ScriptObject self) { 718 final MethodHandle bound = MH.bindTo(getInvokeHandle(), self); 719 return hasCalleeParameter() ? MH.bindTo(bound, this) : bound; 720 } 721 722 protected abstract boolean hasCalleeParameter(); 723 protected abstract void setHasCalleeParameter(); 724 725 /** 726 * Get the construct handle - the most generic (and if no specializations are in place, only) constructor 727 * method handle for this ScriptFunction 728 * @see SpecializedConstructor 729 * @param type type for wanted constructor 730 * @return construct handle 731 */ 732 public final MethodHandle getConstructHandle(final MethodType type) { 733 return candidateWithLowestWeight(type, getConstructHandle(), constructSpecializations); 734 } 735 736 /** 737 * Get a method handle to the constructor for this function 738 * @return constructor handle 739 */ 740 public final MethodHandle getConstructHandle() { 741 return constructor; 742 } 743 744 /** 745 * Set a method handle to the constructor for this function 746 * @param constructHandle constructor handle 747 */ 748 public final void setConstructHandle(final MethodHandle constructHandle) { 749 this.constructor = constructHandle; 750 this.constructSpecializations = null; 751 } 752 753 /** 754 * Get the name for this function 755 * @return the name 756 */ 757 public final String getName() { 758 return name; 759 } 760 761 /** 762 * Does this script function need to be compiled. This determined by 763 * null checking invokeHandle 764 * 765 * @return true if this needs compilation 766 */ 767 public final boolean needsCompilation() { 768 return invoker == null; 769 } 770 771 /** 772 * Get token for this function 773 * @return token 774 */ 775 public final long getToken() { 776 return token; 777 } 778 779 /** 780 * Get the scope for this function 781 * @return the scope 782 */ 783 public final ScriptObject getScope() { 784 return scope; 785 } 786 787 /** 788 * Prototype getter for this ScriptFunction - follows the naming convention 789 * used by Nasgen and the code generator 790 * 791 * @param self self reference 792 * @return self's prototype 793 */ 794 public static Object G$prototype(final Object self) { 795 return (self instanceof ScriptFunction) ? 796 ((ScriptFunction)self).getPrototype() : 797 UNDEFINED; 798 } 799 800 /** 801 * Prototype setter for this ScriptFunction - follows the naming convention 802 * used by Nasgen and the code generator 803 * 804 * @param self self reference 805 * @param prototype prototype to set 806 */ 807 public static void S$prototype(final Object self, final Object prototype) { 808 if (self instanceof ScriptFunction) { 809 ((ScriptFunction)self).setPrototype(prototype); 810 } 811 } 812 813 /** 814 * Length getter - ECMA 15.3.3.2: Function.length 815 * @param self self reference 816 * @return length 817 */ 818 public static int G$length(final Object self) { 819 if (self instanceof ScriptFunction) { 820 return ((ScriptFunction)self).getArity(); 821 } 822 823 return 0; 824 } 825 826 /** 827 * Name getter - ECMA Function.name 828 * @param self self refence 829 * @return the name, or undefined if none 830 */ 831 public static Object G$name(final Object self) { 832 if (self instanceof ScriptFunction) { 833 return ((ScriptFunction)self).getName(); 834 } 835 836 return UNDEFINED; 837 } 838 839 /** 840 * Get the prototype for this ScriptFunction 841 * @param constructor constructor 842 * @return prototype, or null if given constructor is not a ScriptFunction 843 */ 844 public static ScriptObject getPrototype(final Object constructor) { 845 if (constructor instanceof ScriptFunction) { 846 final Object proto = ((ScriptFunction)constructor).getPrototype(); 847 if (proto instanceof ScriptObject) { 848 return (ScriptObject)proto; 849 } 850 } 851 852 return null; 853 } 854 855 // These counters are updated only in debug mode. 856 private static int constructorCount; 857 private static int invokes; 858 private static int allocations; 859 860 /** 861 * @return the constructorCount 862 */ 863 public static int getConstructorCount() { 864 return constructorCount; 865 } 866 867 /** 868 * @return the invokes 869 */ 870 public static int getInvokes() { 871 return invokes; 872 } 873 874 /** 875 * @return the allocations 876 */ 877 public static int getAllocations() { 878 return allocations; 879 } 880 881 @Override 882 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) { 883 final MethodType type = desc.getMethodType(); 884 MethodHandle constructor = getConstructHandle(type); 885 886 if (constructor == null) { 887 typeError("not.a.constructor", ScriptRuntime.safeToString(this)); 888 return null; 889 } 890 891 final MethodType ctorType = constructor.type(); 892 893 // guard against primitive constructor return types 894 constructor = MH.asType(constructor, constructor.type().changeReturnType(Object.class)); 895 896 // apply new filter 897 final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self 898 MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor); 899 900 if (hasCalleeParameter()) { 901 handle = MH.foldArguments(handle, ALLOCATE); 902 } else { 903 handle = MH.filterArguments(handle, 0, ALLOCATE); 904 } 905 906 final MethodHandle filterIn = MH.asType(pairArguments(handle, type), type); 907 return new GuardedInvocation(filterIn, null, NashornGuards.getFunctionGuard(this)); 908 } 909 910 @SuppressWarnings("unused") 911 private static Object newFilter(final Object result, final Object allocation) { 912 return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation; 913 } 914 915 @SuppressWarnings("unused") 916 private static Object wrapFilter(final Object obj) { 917 if (obj instanceof ScriptObject || !isPrimitiveThis(obj)) { 918 return obj; 919 } 920 return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj); 921 } 922 923 /** 924 * dyn:call call site signature: (callee, thiz, [args...]) 925 * generated method signature: (thiz, callee, [args...]) 926 * 927 * cases: 928 * (a) method has callee parameter 929 * (1) for local/scope calls, we just bind thiz and drop the second argument. 930 * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. 931 * (b) method doesn't have callee parameter (builtin functions) 932 * (3) for local/scope calls, bind thiz and drop both callee and thiz. 933 * (4) for normal this-calls, drop callee. 934 */ 935 @Override 936 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 937 final MethodType type = desc.getMethodType(); 938 939 if (request.isCallSiteUnstable()) { 940 // (this, callee, args...) => (this, callee, args[]) 941 final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, 942 type.parameterCount() - 2); 943 944 return new GuardedInvocation(collector, 945 desc.getMethodType().parameterType(0) == ScriptFunction.class ? null : NashornGuards.getScriptFunctionGuard()); 946 } 947 948 MethodHandle boundHandle; 949 MethodHandle guard = null; 950 951 if (hasCalleeParameter()) { 952 final MethodHandle callHandle = getBestSpecializedInvokeHandle(type); 953 954 if(NashornCallSiteDescriptor.isScope(desc)) { 955 // (this, callee, args...) => (callee, args...) => (callee, [this], args...) 956 boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); 957 boundHandle = MH.dropArguments(boundHandle, 1, Object.class); 958 } else { 959 // (this, callee, args...) permute => (callee, this, args...) which is what we get in 960 final MethodType oldType = callHandle.type(); 961 final int[] reorder = new int[oldType.parameterCount()]; 962 for (int i = 2; i < reorder.length; i++) { 963 reorder[i] = i; 964 } 965 reorder[0] = 1; 966 assert reorder[1] == 0; 967 final MethodType newType = oldType.changeParameterType(0, oldType.parameterType(1)).changeParameterType(1, oldType.parameterType(0)); 968 boundHandle = MethodHandles.permuteArguments(callHandle, newType, reorder); 969 970 // For non-strict functions, check whether this-object is primitive type. 971 // If so add a to-object-wrapper argument filter. 972 // Else install a guard that will trigger a relink when the argument becomes primitive. 973 if (isNonStrictFunction()) { 974 if (isPrimitiveThis(request.getArguments()[1])) { 975 boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER); 976 } else { 977 guard = NashornGuards.getNonStrictFunctionGuard(this); 978 } 979 } 980 } 981 } else { 982 final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1)); 983 984 if(NashornCallSiteDescriptor.isScope(desc)) { 985 boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); 986 boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class); 987 } else { 988 boundHandle = MH.dropArguments(callHandle, 0, Object.class); 989 } 990 } 991 992 boundHandle = pairArguments(boundHandle, type); 993 return new GuardedInvocation(boundHandle, guard == null ? NashornGuards.getFunctionGuard(this) : guard); 994 } 995 996 /** 997 * Used for noSuchMethod/noSuchProperty and JSAdapter hooks. 998 * 999 * These don't want a callee parameter, so bind that. Name binding is optional. 1000 */ 1001 MethodHandle getCallMethodHandle(final MethodType type, final String bindName) { 1002 MethodHandle methodHandle = getBestSpecializedInvokeHandle(type); 1003 1004 if (bindName != null) { 1005 if (hasCalleeParameter()) { 1006 methodHandle = MH.insertArguments(methodHandle, 1, this, bindName); 1007 } else { 1008 methodHandle = MH.insertArguments(methodHandle, 1, bindName); 1009 } 1010 } else { 1011 if (hasCalleeParameter()) { 1012 methodHandle = MH.insertArguments(methodHandle, 1, this); 1013 } 1014 } 1015 1016 return pairArguments(methodHandle, type); 1017 } 1018 1019 /** 1020 * Convert this argument for non-strict functions according to ES 10.4.3 1021 * 1022 * @param thiz the this argument 1023 * 1024 * @return the converted this object 1025 */ 1026 protected Object convertThisObject(final Object thiz) { 1027 if (!(thiz instanceof ScriptObject) && isNonStrictFunction()) { 1028 if (JSType.nullOrUndefined(thiz)) { 1029 return Context.getGlobalTrusted(); 1030 } 1031 1032 if (isPrimitiveThis(thiz)) { 1033 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz); 1034 } 1035 } 1036 1037 return thiz; 1038 } 1039 1040 private static boolean isPrimitiveThis(Object obj) { 1041 return obj instanceof String || obj instanceof ConsString || 1042 obj instanceof Number || obj instanceof Boolean; 1043 } 1044 1045 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 1046 final Class<?> own = ScriptFunction.class; 1047 final MethodType mt = MH.type(rtype, types); 1048 try { 1049 return MH.findStatic(MethodHandles.lookup(), own, name, mt); 1050 } catch (final MethodHandleFactory.LookupException e) { 1051 return MH.findVirtual(MethodHandles.lookup(), own, name, mt); 1052 } 1053 } 1054 } 1055