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.runtime.linker.MethodHandleFactory; 41 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 42 import jdk.nashorn.internal.runtime.linker.NashornGuards; 43 import jdk.nashorn.internal.runtime.options.Options; 44 import org.dynalang.dynalink.CallSiteDescriptor; 45 import org.dynalang.dynalink.linker.GuardedInvocation; 46 import org.dynalang.dynalink.linker.LinkRequest; 47 48 /** 49 * Runtime representation of a JavaScript function. 50 */ 51 public abstract class ScriptFunction extends ScriptObject { 52 53 /** Method handle for prototype getter for this ScriptFunction */ 54 public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class); 55 56 /** Method handle for prototype setter for this ScriptFunction */ 57 public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class); 58 59 /** Method handle for length getter for this ScriptFunction */ 60 public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class); 61 62 /** Method handle for name getter for this ScriptFunction */ 63 public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class); 64 65 /** Method handle for allocate function for this ScriptFunction */ 66 public static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class); 67 68 private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); 69 70 private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class); 71 72 /** method handle to scope getter for this ScriptFunction */ 73 public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); 74 75 /** Should specialized function and specialized constructors for the builtin be used if available? */ 76 private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable"); 77 78 private final ScriptFunctionData data; 79 80 /** Reference to constructor prototype. */ 81 protected Object prototype; 82 83 /** The parent scope. */ 84 private final ScriptObject scope; 85 86 /** 87 * Constructor 88 * 89 * @param name function name 90 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) 91 * @param map property map 92 * @param scope scope 93 * @param specs specialized version of this function - other method handles 94 * 95 */ 96 protected ScriptFunction( 97 final String name, 98 final MethodHandle methodHandle, 99 final PropertyMap map, 100 final ScriptObject scope, 101 final MethodHandle[] specs, 102 final boolean strict, 103 final boolean builtin) { 104 105 this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin), map, scope); 106 } 107 108 /** 109 * Constructor 110 * 111 * @param data static function data 112 * @param map property map 113 * @param scope scope 114 */ 115 protected ScriptFunction( 116 final ScriptFunctionData data, 117 final PropertyMap map, 118 final ScriptObject scope) { 119 120 super(map); 121 122 if (Context.DEBUG) { 123 constructorCount++; 124 } 125 126 this.data = data; 127 this.scope = scope; 128 } 129 130 @Override 131 public String getClassName() { 132 return "Function"; 133 } 134 135 /** 136 * ECMA 15.3.5.3 [[HasInstance]] (V) 137 * Step 3 if "prototype" value is not an Object, throw TypeError 138 */ 139 @Override 140 public boolean isInstance(final ScriptObject instance) { 141 if (!(prototype instanceof ScriptObject)) { 142 typeError("prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype)); 143 } 144 145 for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) { 146 if (proto == prototype) { 147 return true; 148 } 149 } 150 151 return false; 152 } 153 154 /** 155 * Get the arity of this ScriptFunction 156 * @return arity 157 */ 158 public final int getArity() { 159 return data.getArity(); 160 } 161 162 /** 163 * Set the arity of this ScriptFunction 164 * @param arity arity 165 */ 166 public final void setArity(final int arity) { 167 data.setArity(arity); 168 } 169 170 /** 171 * Is this a ECMAScript 'use strict' function? 172 * @return true if function is in strict mode 173 */ 174 public boolean isStrict() { 175 return data.isStrict(); 176 } 177 178 /** 179 * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ? 180 * @return true if built-in 181 */ 182 public boolean isBuiltin() { 183 return data.isBuiltin(); 184 } 185 186 /** 187 * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument 188 * according to ECMA 10.4.3. 189 * @return true if this argument must be an object 190 */ 191 public boolean needsWrappedThis() { 192 return data.needsWrappedThis(); 193 } 194 195 /** 196 * Execute this script function. 197 * @param self Target object. 198 * @param arguments Call arguments. 199 * @return ScriptFunction result. 200 * @throws Throwable if there is an exception/error with the invocation or thrown from it 201 */ 202 public Object invoke(final Object self, final Object... arguments) throws Throwable { 203 if (Context.DEBUG) { 204 invokes++; 205 } 206 207 final MethodHandle invoker = data.getGenericInvoker(); 208 final Object selfObj = convertThisObject(self); 209 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 210 211 if (data.isVarArg()) { 212 if (data.needsCallee()) { 213 return invoker.invokeExact(selfObj, this, args); 214 } 215 return invoker.invokeExact(selfObj, args); 216 } 217 218 final int paramCount = invoker.type().parameterCount(); 219 if (data.needsCallee()) { 220 switch (paramCount) { 221 case 2: 222 return invoker.invokeExact(selfObj, this); 223 case 3: 224 return invoker.invokeExact(selfObj, this, getArg(args, 0)); 225 case 4: 226 return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1)); 227 case 5: 228 return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 229 default: 230 return invoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args)); 231 } 232 } 233 234 switch (paramCount) { 235 case 1: 236 return invoker.invokeExact(selfObj); 237 case 2: 238 return invoker.invokeExact(selfObj, getArg(args, 0)); 239 case 3: 240 return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); 241 case 4: 242 return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 243 default: 244 return invoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args)); 245 } 246 } 247 248 private static Object getArg(final Object[] args, final int i) { 249 return i < args.length ? args[i] : UNDEFINED; 250 } 251 252 /** 253 * Construct new object using this constructor. 254 * @param self Target object. 255 * @param args Call arguments. 256 * @return ScriptFunction result. 257 * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it 258 */ 259 public Object construct(final Object self, final Object... args) throws Throwable { 260 if (data.getConstructor() == null) { 261 typeError("not.a.constructor", ScriptRuntime.safeToString(this)); 262 } 263 264 final MethodHandle constructor = data.getGenericConstructor(); 265 if (data.isVarArg()) { 266 if (data.needsCallee()) { 267 return constructor.invokeExact(self, this, args); 268 } 269 return constructor.invokeExact(self, args); 270 } 271 272 final int paramCount = constructor.type().parameterCount(); 273 if (data.needsCallee()) { 274 switch (paramCount) { 275 case 2: 276 return constructor.invokeExact(self, this); 277 case 3: 278 return constructor.invokeExact(self, this, getArg(args, 0)); 279 case 4: 280 return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1)); 281 case 5: 282 return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 283 default: 284 return constructor.invokeWithArguments(withArguments(self, this, args)); 285 } 286 } 287 288 switch(paramCount) { 289 case 1: 290 return constructor.invokeExact(self); 291 case 2: 292 return constructor.invokeExact(self, getArg(args, 0)); 293 case 3: 294 return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1)); 295 case 4: 296 return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 297 default: 298 return constructor.invokeWithArguments(withArguments(self, null, args)); 299 } 300 } 301 302 private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) { 303 return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function 304 } 305 306 private static Object[] withArguments(final Object self, final ScriptFunction function, final int paramCount, final Object... args) { 307 final Object[] finalArgs = new Object[paramCount]; 308 309 finalArgs[0] = self; 310 int nextArg = 1; 311 if (function != null) { 312 finalArgs[nextArg++] = function; 313 } 314 315 //don't add more args that there is paramcount in the handle (including self) 316 final int maxArgs = Math.min(args.length, paramCount - (function == null ? 1 : 2)); 317 for (int i = 0; i < maxArgs;) { 318 finalArgs[nextArg++] = args[i++]; 319 } 320 321 //if we have fewer params than paramcount, pad undefined 322 while (nextArg < paramCount) { 323 finalArgs[nextArg++] = UNDEFINED; 324 } 325 326 return finalArgs; 327 } 328 329 /** 330 * Allocate function. Called from generated {@link ScriptObject} code 331 * for allocation as a factory method 332 * 333 * @return a new instance of the {@link ScriptObject} whose allocator this is 334 */ 335 public Object allocate() { 336 if (Context.DEBUG) { 337 allocations++; 338 } 339 340 if (getConstructHandle() == null) { 341 typeError("not.a.constructor", ScriptRuntime.safeToString(this)); 342 } 343 344 ScriptObject object = null; 345 346 if (data.getAllocator() != null) { 347 try { 348 object = (ScriptObject)data.getAllocator().invokeExact(data.getAllocatorMap()); 349 } catch (final RuntimeException | Error e) { 350 throw e; 351 } catch (final Throwable t) { 352 throw new RuntimeException(t); 353 } 354 } 355 356 if (object != null) { 357 if (prototype instanceof ScriptObject) { 358 object.setProto((ScriptObject)prototype); 359 } 360 361 if (object.getProto() == null) { 362 object.setProto(getObjectPrototype()); 363 } 364 } 365 366 return object; 367 } 368 369 /** 370 * Return Object.prototype - used by "allocate" 371 * @return Object.prototype 372 */ 373 protected abstract ScriptObject getObjectPrototype(); 374 375 /** 376 * Creates a version of this function bound to a specific "self" and other argumentss 377 * @param self the self to bind the function to 378 * @param args other arguments (beside self) to bind the function to 379 * @return the bound function 380 */ 381 public abstract ScriptFunction makeBoundFunction(Object self, Object[] args); 382 383 384 @Override 385 public final String safeToString() { 386 return toSource(); 387 } 388 389 @Override 390 public String toString() { 391 return data.toString(); 392 } 393 394 /** 395 * Get this function as a String containing its source code. If no source code 396 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} 397 * @return string representation of this function's source 398 */ 399 public final String toSource() { 400 return data.toSource(); 401 } 402 403 /** 404 * Get the prototype object for this function 405 * @return prototype 406 */ 407 public final Object getPrototype() { 408 return prototype; 409 } 410 411 /** 412 * Set the prototype object for this function 413 * @param prototype new prototype object 414 * @return the prototype parameter 415 */ 416 public final Object setPrototype(final Object prototype) { 417 this.prototype = prototype; 418 return prototype; 419 } 420 421 private static int weigh(final MethodType t) { 422 int weight = Type.typeFor(t.returnType()).getWeight(); 423 for (final Class<?> paramType : t.parameterArray()) { 424 final int pweight = Type.typeFor(paramType).getWeight(); 425 weight += pweight; 426 } 427 return weight; 428 } 429 430 private static boolean typeCompatible(final MethodType desc, final MethodType spec) { 431 //spec must fit in desc 432 final Class<?>[] dparray = desc.parameterArray(); 433 final Class<?>[] sparray = spec.parameterArray(); 434 435 if (dparray.length != sparray.length) { 436 return false; 437 } 438 439 for (int i = 0; i < dparray.length; i++) { 440 final Type dp = Type.typeFor(dparray[i]); 441 final Type sp = Type.typeFor(sparray[i]); 442 443 if (dp.isBoolean()) { 444 return false; //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution 445 } 446 447 //specialization arguments must be at least as wide as dp, if not wider 448 if (Type.widest(dp, sp) != sp) { 449 //e.g. specialization takes double and callsite says "object". reject. 450 //but if specialization says double and callsite says "int" or "long" or "double", that's fine 451 return false; 452 } 453 } 454 455 return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic. 456 } 457 458 private MethodHandle candidateWithLowestWeight(final MethodType descType, final MethodHandle initialCandidate, final MethodHandle[] specs) { 459 if (DISABLE_SPECIALIZATION || specs == null) { 460 return initialCandidate; 461 } 462 463 int minimumWeight = Integer.MAX_VALUE; 464 MethodHandle candidate = initialCandidate; 465 466 for (final MethodHandle spec : specs) { 467 final MethodType specType = spec.type(); 468 469 if (!typeCompatible(descType, specType)) { 470 continue; 471 } 472 473 //return type is ok. we want a wider or equal one for our callsite. 474 final int specWeight = weigh(specType); 475 if (specWeight < minimumWeight) { 476 candidate = spec; 477 minimumWeight = specWeight; 478 } 479 } 480 481 if (DISABLE_SPECIALIZATION && candidate != initialCandidate) { 482 Context.err("### Specializing builtin " + getName() + " -> " + candidate + "?"); 483 } 484 485 return candidate; 486 } 487 488 /** 489 * Return the most appropriate invoke handle if there are specializations 490 * @param type most specific method type to look for invocation with 491 * @return invoke method handle 492 */ 493 public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) { 494 return candidateWithLowestWeight(type, getInvokeHandle(), data.getInvokeSpecializations()); 495 } 496 497 /** 498 * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation 499 * method handle for this ScriptFunction 500 * @see SpecializedFunction 501 * @return invokeHandle 502 */ 503 public final MethodHandle getInvokeHandle() { 504 return data.getInvoker(); 505 } 506 507 /** 508 * Return the invoke handle bound to a given ScriptObject self reference. 509 * If callee parameter is required result is rebound to this. 510 * 511 * @param self self reference 512 * @return bound invoke handle 513 */ 514 public final MethodHandle getBoundInvokeHandle(final ScriptObject self) { 515 final MethodHandle bound = MH.bindTo(getInvokeHandle(), self); 516 return data.needsCallee() ? MH.bindTo(bound, this) : bound; 517 } 518 519 520 /** 521 * Get the construct handle - the most generic (and if no specializations are in place, only) constructor 522 * method handle for this ScriptFunction 523 * @see SpecializedConstructor 524 * @param type type for wanted constructor 525 * @return construct handle 526 */ 527 public final MethodHandle getConstructHandle(final MethodType type) { 528 return candidateWithLowestWeight(type, getConstructHandle(), data.getConstructSpecializations()); 529 } 530 531 /** 532 * Get a method handle to the constructor for this function 533 * @return constructor handle 534 */ 535 public final MethodHandle getConstructHandle() { 536 return data.getConstructor(); 537 } 538 539 /** 540 * Set a method handle to the constructor for this function 541 * @param constructHandle constructor handle 542 */ 543 public final void setConstructHandle(final MethodHandle constructHandle) { 544 data.setConstructor(constructHandle); 545 } 546 547 /** 548 * Get the name for this function 549 * @return the name 550 */ 551 public final String getName() { 552 return data.getName(); 553 } 554 555 /** 556 * Does this script function need to be compiled. This determined by 557 * null checking invokeHandle 558 * 559 * @return true if this needs compilation 560 */ 561 public final boolean needsCompilation() { 562 return data.getInvoker() == null; 563 } 564 565 /** 566 * Get token for this function 567 * @return token 568 */ 569 public final long getToken() { 570 return data.getToken(); 571 } 572 573 /** 574 * Get the scope for this function 575 * @return the scope 576 */ 577 public final ScriptObject getScope() { 578 return scope; 579 } 580 581 /** 582 * Prototype getter for this ScriptFunction - follows the naming convention 583 * used by Nasgen and the code generator 584 * 585 * @param self self reference 586 * @return self's prototype 587 */ 588 public static Object G$prototype(final Object self) { 589 return (self instanceof ScriptFunction) ? 590 ((ScriptFunction)self).getPrototype() : 591 UNDEFINED; 592 } 593 594 /** 595 * Prototype setter for this ScriptFunction - follows the naming convention 596 * used by Nasgen and the code generator 597 * 598 * @param self self reference 599 * @param prototype prototype to set 600 */ 601 public static void S$prototype(final Object self, final Object prototype) { 602 if (self instanceof ScriptFunction) { 603 ((ScriptFunction)self).setPrototype(prototype); 604 } 605 } 606 607 /** 608 * Length getter - ECMA 15.3.3.2: Function.length 609 * @param self self reference 610 * @return length 611 */ 612 public static int G$length(final Object self) { 613 if (self instanceof ScriptFunction) { 614 return ((ScriptFunction)self).getArity(); 615 } 616 617 return 0; 618 } 619 620 /** 621 * Name getter - ECMA Function.name 622 * @param self self refence 623 * @return the name, or undefined if none 624 */ 625 public static Object G$name(final Object self) { 626 if (self instanceof ScriptFunction) { 627 return ((ScriptFunction)self).getName(); 628 } 629 630 return UNDEFINED; 631 } 632 633 /** 634 * Get the prototype for this ScriptFunction 635 * @param constructor constructor 636 * @return prototype, or null if given constructor is not a ScriptFunction 637 */ 638 public static ScriptObject getPrototype(final Object constructor) { 639 if (constructor instanceof ScriptFunction) { 640 final Object proto = ((ScriptFunction)constructor).getPrototype(); 641 if (proto instanceof ScriptObject) { 642 return (ScriptObject)proto; 643 } 644 } 645 646 return null; 647 } 648 649 // These counters are updated only in debug mode. 650 private static int constructorCount; 651 private static int invokes; 652 private static int allocations; 653 654 /** 655 * @return the constructorCount 656 */ 657 public static int getConstructorCount() { 658 return constructorCount; 659 } 660 661 /** 662 * @return the invokes 663 */ 664 public static int getInvokes() { 665 return invokes; 666 } 667 668 /** 669 * @return the allocations 670 */ 671 public static int getAllocations() { 672 return allocations; 673 } 674 675 @Override 676 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) { 677 final MethodType type = desc.getMethodType(); 678 MethodHandle constructor = getConstructHandle(type); 679 680 if (constructor == null) { 681 typeError("not.a.constructor", ScriptRuntime.safeToString(this)); 682 return null; 683 } 684 685 final MethodType ctorType = constructor.type(); 686 687 // guard against primitive constructor return types 688 constructor = MH.asType(constructor, constructor.type().changeReturnType(Object.class)); 689 690 // apply new filter 691 final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self 692 MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor); 693 694 if (data.needsCallee()) { 695 handle = MH.foldArguments(handle, ALLOCATE); 696 } else { 697 handle = MH.filterArguments(handle, 0, ALLOCATE); 698 } 699 700 final MethodHandle filterIn = MH.asType(pairArguments(handle, type), type); 701 return new GuardedInvocation(filterIn, null, NashornGuards.getFunctionGuard(this)); 702 } 703 704 @SuppressWarnings("unused") 705 private static Object newFilter(final Object result, final Object allocation) { 706 return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation; 707 } 708 709 @SuppressWarnings("unused") 710 private static Object wrapFilter(final Object obj) { 711 if (obj instanceof ScriptObject || !isPrimitiveThis(obj)) { 712 return obj; 713 } 714 return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj); 715 } 716 717 /** 718 * dyn:call call site signature: (callee, thiz, [args...]) 719 * generated method signature: (thiz, callee, [args...]) 720 * 721 * cases: 722 * (a) method has callee parameter 723 * (1) for local/scope calls, we just bind thiz and drop the second argument. 724 * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. 725 * (b) method doesn't have callee parameter (builtin functions) 726 * (3) for local/scope calls, bind thiz and drop both callee and thiz. 727 * (4) for normal this-calls, drop callee. 728 */ 729 @Override 730 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 731 final MethodType type = desc.getMethodType(); 732 733 if (request.isCallSiteUnstable()) { 734 // (this, callee, args...) => (this, callee, args[]) 735 final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, 736 type.parameterCount() - 2); 737 738 return new GuardedInvocation(collector, 739 desc.getMethodType().parameterType(0) == ScriptFunction.class ? null : NashornGuards.getScriptFunctionGuard()); 740 } 741 742 MethodHandle boundHandle; 743 MethodHandle guard = null; 744 745 if (data.needsCallee()) { 746 final MethodHandle callHandle = getBestSpecializedInvokeHandle(type); 747 748 if(NashornCallSiteDescriptor.isScope(desc)) { 749 // (this, callee, args...) => (callee, args...) => (callee, [this], args...) 750 boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); 751 boundHandle = MH.dropArguments(boundHandle, 1, Object.class); 752 } else { 753 // (this, callee, args...) permute => (callee, this, args...) which is what we get in 754 final MethodType oldType = callHandle.type(); 755 final int[] reorder = new int[oldType.parameterCount()]; 756 for (int i = 2; i < reorder.length; i++) { 757 reorder[i] = i; 758 } 759 reorder[0] = 1; 760 assert reorder[1] == 0; 761 final MethodType newType = oldType.changeParameterType(0, oldType.parameterType(1)).changeParameterType(1, oldType.parameterType(0)); 762 boundHandle = MethodHandles.permuteArguments(callHandle, newType, reorder); 763 764 // For non-strict functions, check whether this-object is primitive type. 765 // If so add a to-object-wrapper argument filter. 766 // Else install a guard that will trigger a relink when the argument becomes primitive. 767 if (needsWrappedThis()) { 768 if (isPrimitiveThis(request.getArguments()[1])) { 769 boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER); 770 } else { 771 guard = NashornGuards.getNonStrictFunctionGuard(this); 772 } 773 } 774 } 775 } else { 776 final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1)); 777 778 if(NashornCallSiteDescriptor.isScope(desc)) { 779 boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); 780 boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class); 781 } else { 782 boundHandle = MH.dropArguments(callHandle, 0, Object.class); 783 } 784 } 785 786 boundHandle = pairArguments(boundHandle, type); 787 return new GuardedInvocation(boundHandle, guard == null ? NashornGuards.getFunctionGuard(this) : guard); 788 } 789 790 /** 791 * Used for noSuchMethod/noSuchProperty and JSAdapter hooks. 792 * 793 * These don't want a callee parameter, so bind that. Name binding is optional. 794 */ 795 MethodHandle getCallMethodHandle(final MethodType type, final String bindName) { 796 MethodHandle methodHandle = getBestSpecializedInvokeHandle(type); 797 798 if (bindName != null) { 799 if (data.needsCallee()) { 800 methodHandle = MH.insertArguments(methodHandle, 1, this, bindName); 801 } else { 802 methodHandle = MH.insertArguments(methodHandle, 1, bindName); 803 } 804 } else { 805 if (data.needsCallee()) { 806 methodHandle = MH.insertArguments(methodHandle, 1, this); 807 } 808 } 809 810 return pairArguments(methodHandle, type); 811 } 812 813 /** 814 * Convert this argument for non-strict functions according to ES 10.4.3 815 * 816 * @param thiz the this argument 817 * 818 * @return the converted this object 819 */ 820 protected Object convertThisObject(final Object thiz) { 821 if (!(thiz instanceof ScriptObject) && needsWrappedThis()) { 822 if (JSType.nullOrUndefined(thiz)) { 823 return Context.getGlobalTrusted(); 824 } 825 826 if (isPrimitiveThis(thiz)) { 827 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz); 828 } 829 } 830 831 return thiz; 832 } 833 834 private static boolean isPrimitiveThis(Object obj) { 835 return obj instanceof String || obj instanceof ConsString || 836 obj instanceof Number || obj instanceof Boolean; 837 } 838 839 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 840 final Class<?> own = ScriptFunction.class; 841 final MethodType mt = MH.type(rtype, types); 842 try { 843 return MH.findStatic(MethodHandles.lookup(), own, name, mt); 844 } catch (final MethodHandleFactory.LookupException e) { 845 return MH.findVirtual(MethodHandles.lookup(), own, name, mt); 846 } 847 } 848 } 849 --- EOF ---