17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.runtime;
27
28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
31 import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
32
33 import java.lang.invoke.MethodHandle;
34 import java.lang.invoke.MethodHandles;
35 import java.lang.invoke.MethodType;
36 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
37 import jdk.nashorn.internal.codegen.types.Type;
38 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
39 import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
40 import jdk.nashorn.internal.parser.Token;
41 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
42 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
43 import jdk.nashorn.internal.runtime.linker.NashornGuards;
44 import jdk.nashorn.internal.runtime.options.Options;
45 import org.dynalang.dynalink.CallSiteDescriptor;
46 import org.dynalang.dynalink.linker.GuardedInvocation;
47 import org.dynalang.dynalink.linker.LinkRequest;
48
49 /**
50 * Runtime representation of a JavaScript function.
51 */
52 public abstract class ScriptFunction extends ScriptObject {
53
54 /** Method handle for prototype getter for this ScriptFunction */
55 public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class);
56
57 /** Method handle for prototype setter for this ScriptFunction */
58 public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class);
59
60 /** Method handle for length getter for this ScriptFunction */
61 public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class);
62
63 /** Method handle for name getter for this ScriptFunction */
64 public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
65
66 /** Method handle for allocate function for this ScriptFunction */
67 public static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
68
69 private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
70
71 private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
72
73 /** method handle to arity setter for this ScriptFunction */
74 public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class);
75 /** method handle to scope getter for this ScriptFunction */
76 public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
77
78 /** Should specialized function and specialized constructors for the builtin be used if available? */
79 private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
80
81 /** Name of function or null. */
82 private final String name;
83
84 /** Source of function. */
85 private final Source source;
86
87 /** Start position and length in source. */
88 private final long token;
89
90 /** Reference to code for this method. */
91 private final MethodHandle invokeHandle;
92
93 /** Reference to code for this method when called to create "new" object */
94 protected MethodHandle constructHandle;
95
96 /** Reference to constructor prototype. */
97 protected Object prototype;
98
99 /** Constructor to create a new instance. */
100 private MethodHandle allocator;
101
102 /** Map for new instance constructor. */
103 private PropertyMap allocatorMap;
104
105 /** The parent scope. */
106 private final ScriptObject scope;
107
108 /** Specializations - see @SpecializedFunction */
109 private MethodHandle[] invokeSpecializations;
110
111 /** Specializations - see @SpecializedFunction */
112 private MethodHandle[] constructSpecializations;
113
114 /** This field is either computed in constructor or set explicitly by calling setArity method. */
115 private int arity;
116
117 /**
118 * Constructor
119 *
120 * @param name function name
121 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
122 * @param map property map
123 * @param scope scope
124 * @param specs specialized version of this function - other method handles
125 */
126 protected ScriptFunction(
127 final String name,
128 final MethodHandle methodHandle,
129 final PropertyMap map,
130 final ScriptObject scope,
131 final MethodHandle[] specs) {
132 this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs);
133 }
134
135 /**
136 * Heuristic to figure out if the method handle has a callee argument. If it's type is either
137 * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
138 * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
139 * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
140 * they also always receive a callee.
141 * @param methodHandle the examined method handle
142 * @return true if the method handle expects a callee, false otherwise
143 */
144 private static boolean needsCallee(MethodHandle methodHandle) {
145 final MethodType type = methodHandle.type();
146 final int len = type.parameterCount();
147 if(len == 0) {
148 return false;
149 }
150 if(type.parameterType(0) == boolean.class) {
151 return len > 2 && type.parameterType(2) == ScriptFunction.class;
152 }
153 return len > 1 && type.parameterType(1) == ScriptFunction.class;
154 }
155
156 /**
157 * Constructor
158 *
159 * @param name function name
160 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
161 * @param map property map
162 * @param scope scope
163 * @param source the source
164 * @param token token
165 * @param allocator method handle to this function's allocator - see JO$ classes
166 * @param allocatorMap property map to be used for all constructors
167 * @param needsCallee does this method use the {@code callee} variable
168 * @param specs specialized version of this function - other method handles
169 */
170 protected ScriptFunction(
171 final String name,
172 final MethodHandle methodHandle,
173 final PropertyMap map,
174 final ScriptObject scope,
175 final Source source,
176 final long token,
177 final MethodHandle allocator,
178 final PropertyMap allocatorMap,
179 final boolean needsCallee,
180 final MethodHandle[] specs) {
181
182 this(name, methodHandle, map, scope, source, token, needsCallee, specs);
183
184 //this is the internal constructor
185
186 this.allocator = allocator;
187 this.allocatorMap = allocatorMap;
188 }
189
190 /**
191 * Constructor
192 *
193 * @param name function name
194 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
195 * @param map property map
196 * @param scope scope
197 * @param source the source
198 * @param token token
199 * @param needsCallee does this method use the {@code callee} variable
200 * @param specs specialized version of this function - other method handles
201 */
202 protected ScriptFunction(
203 final String name,
204 final MethodHandle methodHandle,
205 final PropertyMap map,
206 final ScriptObject scope,
207 final Source source,
208 final long token,
209 final boolean needsCallee,
210 final MethodHandle[] specs) {
211
212 super(map);
213
214 if (Context.DEBUG) {
215 constructorCount++;
216 }
217
218 this.name = name;
219 this.source = source;
220 this.token = token;
221 this.scope = scope;
222 if(needsCallee) {
223 setHasCalleeParameter();
224 }
225
226 final MethodType type = methodHandle.type();
227 final int paramCount = type.parameterCount();
228 final boolean isVarArg = type.parameterType(paramCount - 1).isArray();
229
230 final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg));
231
232 this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
233
234 if (needsCallee && !isVarArg) {
235 this.arity--;
236 }
237
238 if (scope != null) {
239 this.invokeHandle = mh;
240 this.constructHandle = mh;
241 } else if (isConstructor(mh)) {
242 if (!isVarArg) {
243 this.arity--; // drop the boolean flag for arity
244 }
245 /*
246 * We insert a boolean argument to tell if the method was invoked as
247 * constructor or not if the method handle's first argument is boolean.
248 */
249 this.invokeHandle = MH.insertArguments(mh, 0, false);
250 this.constructHandle = MH.insertArguments(mh, 0, true);
251
252 if (specs != null) {
253 this.invokeSpecializations = new MethodHandle[specs.length];
254 this.constructSpecializations = new MethodHandle[specs.length];
255 for (int i = 0; i < specs.length; i++) {
256 this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false);
257 this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
258 }
259 }
260 } else {
261 this.invokeHandle = mh;
262 this.constructHandle = mh;
263 this.invokeSpecializations = specs;
264 this.constructSpecializations = specs;
265 }
266 }
267
268 /**
269 * Takes a method type, and returns a (potentially different method type) that the method handles used by
270 * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and
271 * {@link #construct(Object, Object...)}. The returned method type will be sure to return {@code Object}, and will
272 * have all its parameters turned into {@code Object} as well, except for the following ones:
273 * <ul>
274 * <li>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor
275 * invocation,</li>
276 * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
277 * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be
278 * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li>
279 * @param type the original type
280 * @param hasCallee true if the function uses the callee argument
281 * @param isVarArg if the function is a vararg
282 * @return the new type, conforming to the rules above.
283 */
284 private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) {
285 // Generify broadly
286 MethodType newType = type.generic().changeReturnType(Object.class);
287 if(isVarArg) {
288 // Change back to vararg if we over-generified it
289 newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
290 }
291 final boolean hasBoolean = type.parameterType(0) == boolean.class;
292 if(hasBoolean) {
293 // Restore the initial boolean argument
294 newType = newType.changeParameterType(0, boolean.class);
295 }
296 if(hasCallee) {
297 // Restore the ScriptFunction argument
298 newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class);
299 }
300 return newType;
301 }
302
303 @Override
304 public String getClassName() {
305 return "Function";
306 }
307
308 /**
309 * ECMA 15.3.5.3 [[HasInstance]] (V)
310 * Step 3 if "prototype" value is not an Object, throw TypeError
311 */
312 @Override
313 public boolean isInstance(final ScriptObject instance) {
314 if (!(prototype instanceof ScriptObject)) {
315 typeError("prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype));
316 }
317
318 for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
319 if (proto == prototype) {
320 return true;
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++];
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() {
689
690 return candidate;
691 }
692
693 /**
694 * Return the most appropriate invoke handle if there are specializations
695 * @param type most specific method type to look for invocation with
696 * @return invoke method handle
697 */
698 public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) {
699 return candidateWithLowestWeight(type, getInvokeHandle(), invokeSpecializations);
700 }
701
702 /**
703 * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation
704 * method handle for this ScriptFunction
705 * @see SpecializedFunction
706 * @return invokeHandle
707 */
708 public final MethodHandle getInvokeHandle() {
709 return invokeHandle;
710 }
711
712 /**
713 * Return the invoke handle bound to a given ScriptObject self reference.
714 * If callee parameter is required result is rebound to this.
715 *
716 * @param self self reference
717 * @return bound invoke handle
718 */
719 public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
720 final MethodHandle bound = MH.bindTo(getInvokeHandle(), self);
721 return hasCalleeParameter() ? MH.bindTo(bound, this) : bound;
722 }
723
724 protected abstract boolean hasCalleeParameter();
725 protected abstract void setHasCalleeParameter();
726
727 /**
728 * Get the construct handle - the most generic (and if no specializations are in place, only) constructor
729 * method handle for this ScriptFunction
730 * @see SpecializedConstructor
731 * @param type type for wanted constructor
732 * @return construct handle
733 */
734 public final MethodHandle getConstructHandle(final MethodType type) {
735 return candidateWithLowestWeight(type, getConstructHandle(), constructSpecializations);
736 }
737
738 /**
739 * Get a method handle to the constructor for this function
740 * @return constructor handle
741 */
742 public final MethodHandle getConstructHandle() {
743 return constructHandle;
744 }
745
746 /**
747 * Set a method handle to the constructor for this function
748 * @param constructHandle constructor handle
749 */
750 public final void setConstructHandle(final MethodHandle constructHandle) {
751 this.constructHandle = constructHandle;
752 this.constructSpecializations = null;
753 }
754
755 /**
756 * Get the name for this function
757 * @return the name
758 */
759 public final String getName() {
760 return name;
761 }
762
763 /**
764 * Does this script function need to be compiled. This determined by
765 * null checking invokeHandle
766 *
767 * @return true if this needs compilation
768 */
769 public final boolean needsCompilation() {
770 return invokeHandle == null;
771 }
772
773 /**
774 * Get token for this function
775 * @return token
776 */
777 public final long getToken() {
778 return token;
779 }
780
781 /**
782 * Get the scope for this function
783 * @return the scope
784 */
785 public final ScriptObject getScope() {
786 return scope;
787 }
788
789 /**
790 * Prototype getter for this ScriptFunction - follows the naming convention
|
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;
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++];
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() {
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
|