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++];
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
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() :
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);
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);
|
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++];
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
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() :
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);
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);
|