Mon, 24 Jun 2013 19:06:01 +0530
8015959: Can't call foreign constructor
Reviewed-by: jlaskey, hannesw
1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package jdk.nashorn.internal.runtime;
28 import static jdk.nashorn.internal.lookup.Lookup.MH;
29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
37 /**
38 * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
39 * Instances of this class are created during codegen and stored in script classes'
40 * constants array to reduce function instantiation overhead during runtime.
41 */
42 public abstract class ScriptFunctionData {
44 /** Name of the function or "" for anonynous functions */
45 protected final String name;
47 /** All versions of this function that have been generated to code */
48 protected final CompiledFunctions code;
50 private int arity;
52 private final boolean isStrict;
54 private final boolean isBuiltin;
56 private final boolean isConstructor;
58 private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
59 private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
61 /**
62 * Constructor
63 *
64 * @param name script function name
65 * @param arity arity
66 * @param isStrict is the function strict
67 * @param isBuiltin is the function built in
68 * @param isConstructor is the function a constructor
69 */
70 protected ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
71 this.name = name;
72 this.arity = arity;
73 this.code = new CompiledFunctions();
74 this.isStrict = isStrict;
75 this.isBuiltin = isBuiltin;
76 this.isConstructor = isConstructor;
77 }
79 final int getArity() {
80 return arity;
81 }
83 /**
84 * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final
85 * @param arity new arity
86 */
87 void setArity(final int arity) {
88 this.arity = arity;
89 }
91 CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
92 final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args);
94 //TODO the boundinvoker.type() could actually be more specific here
95 if (isConstructor()) {
96 ensureConstructor(originalInv);
97 return new CompiledFunction(boundInvoker.type(), boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args));
98 }
100 return new CompiledFunction(boundInvoker.type(), boundInvoker);
101 }
103 /**
104 * Is this a ScriptFunction generated with strict semantics?
105 * @return true if strict, false otherwise
106 */
107 public boolean isStrict() {
108 return isStrict;
109 }
111 boolean isBuiltin() {
112 return isBuiltin;
113 }
115 boolean isConstructor() {
116 return isConstructor;
117 }
119 boolean needsCallee() {
120 // we don't know if we need a callee or not unless we are generated
121 ensureCodeGenerated();
122 return code.needsCallee();
123 }
125 /**
126 * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
127 * according to ECMA 10.4.3.
128 * @return true if this argument must be an object
129 */
130 boolean needsWrappedThis() {
131 return !isStrict && !isBuiltin;
132 }
134 String toSource() {
135 return "function " + (name == null ? "" : name) + "() { [native code] }";
136 }
138 String getName() {
139 return name;
140 }
142 /**
143 * Get this function as a String containing its source code. If no source code
144 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
145 *
146 * @return string representation of this function
147 */
148 @Override
149 public String toString() {
150 final StringBuilder sb = new StringBuilder();
152 sb.append("name='").
153 append(name.isEmpty() ? "<anonymous>" : name).
154 append("' ").
155 append(code.size()).
156 append(" invokers=").
157 append(code);
159 return sb.toString();
160 }
162 /**
163 * Pick the best invoker, i.e. the one version of this method with as narrow and specific
164 * types as possible. If the call site arguments are objects, but boxed primitives we can
165 * also try to get a primitive version of the method and do an unboxing filter, but then
166 * we need to insert a guard that checks the argument is really always a boxed primitive
167 * and not suddenly a "real" object
168 *
169 * @param callSiteType callsite type
170 * @param args arguments at callsite on first trampoline invocation
171 * @return method handle to best invoker
172 */
173 MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
174 return getBest(callSiteType).getInvoker();
175 }
177 MethodHandle getBestInvoker(final MethodType callSiteType) {
178 return getBestInvoker(callSiteType, null);
179 }
181 MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) {
182 if (!isConstructor()) {
183 throw typeError("not.a.constructor", toSource());
184 }
185 ensureCodeGenerated();
187 final CompiledFunction best = getBest(callSiteType);
188 ensureConstructor(best);
189 return best.getConstructor();
190 }
192 MethodHandle getBestConstructor(final MethodType callSiteType) {
193 return getBestConstructor(callSiteType, null);
194 }
196 /**
197 * Subclass responsibility. If we can have lazy code generation, this is a hook to ensure that
198 * code exists before performing an operation.
199 */
200 protected void ensureCodeGenerated() {
201 //empty
202 }
204 /**
205 * Return a generic Object/Object invoker for this method. It will ensure code
206 * is generated, get the most generic of all versions of this function and adapt it
207 * to Objects.
208 *
209 * TODO this is only public because {@link JavaAdapterFactory} can't supply us with
210 * a MethodType that we can use for lookup due to boostrapping problems. Can be fixed
211 *
212 * @return generic invoker of this script function
213 */
214 public final MethodHandle getGenericInvoker() {
215 ensureCodeGenerated();
216 return composeGenericMethod(code.mostGeneric().getInvoker());
217 }
219 final MethodHandle getGenericConstructor() {
220 ensureCodeGenerated();
221 ensureConstructor(code.mostGeneric());
222 return composeGenericMethod(code.mostGeneric().getConstructor());
223 }
225 private CompiledFunction getBest(final MethodType callSiteType) {
226 ensureCodeGenerated();
227 return code.best(callSiteType);
228 }
230 /**
231 * Allocates an object using this function's allocator.
232 * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
233 */
234 ScriptObject allocate() {
235 return null;
236 }
238 /**
239 * This method is used to create the immutable portion of a bound function.
240 * See {@link ScriptFunction#makeBoundFunction(Object, Object[])}
241 *
242 * @param fn the original function being bound
243 * @param self this reference to bind. Can be null.
244 * @param args additional arguments to bind. Can be null.
245 */
246 ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
247 ensureCodeGenerated();
249 final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
250 final int length = args == null ? 0 : args.length;
252 CompiledFunctions boundList = new CompiledFunctions();
253 for (final CompiledFunction inv : code) {
254 boundList.add(bind(inv, fn, self, allArgs));
255 }
256 ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor());
257 return boundData;
258 }
260 /**
261 * Compose a constructor given a primordial constructor handle
262 *
263 * @param ctor primordial constructor handle
264 * @param needsCallee do we need to pass a callee
265 *
266 * @return the composed constructor
267 */
268 protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) {
269 // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
270 // "this" in the first argument position is what allows the elegant folded composition of
271 // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
272 // always returns Object.
273 MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
275 composedCtor = changeReturnTypeToObject(composedCtor);
277 final MethodType ctorType = composedCtor.type();
279 // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
280 // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
281 // (this, [callee, ]args...) => ([callee, ]args...)
282 final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
284 // Fold constructor into newFilter that replaces the return value from the constructor with the originally
285 // allocated value when the originally allocated value is a primitive.
286 // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
287 composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor);
289 // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
290 if (needsCallee) {
291 // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
292 // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
293 // or...
294 return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE);
295 }
297 // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
298 // (this, args...) filter (callee) => (callee, args...)
299 return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE);
300 }
302 /**
303 * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
304 * method handle. If this function's method handles don't need a callee parameter, returns the original method
305 * handle unchanged.
306 *
307 * @param mh a method handle with order of arguments {@code (callee, this, args...)}
308 *
309 * @return a method handle with order of arguments {@code (this, callee, args...)}
310 */
311 private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
312 final MethodType type = mh.type();
313 assert type.parameterType(0) == ScriptFunction.class : type;
314 assert type.parameterType(1) == Object.class : type;
315 final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
316 final int[] reorder = new int[type.parameterCount()];
317 reorder[0] = 1;
318 assert reorder[1] == 0;
319 for (int i = 2; i < reorder.length; ++i) {
320 reorder[i] = i;
321 }
322 return MethodHandles.permuteArguments(mh, newType, reorder);
323 }
325 /**
326 * Convert this argument for non-strict functions according to ES 10.4.3
327 *
328 * @param thiz the this argument
329 *
330 * @return the converted this object
331 */
332 private Object convertThisObject(final Object thiz) {
333 if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
334 if (JSType.nullOrUndefined(thiz)) {
335 return Context.getGlobalTrusted();
336 }
338 if (isPrimitiveThis(thiz)) {
339 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
340 }
341 }
343 return thiz;
344 }
346 static boolean isPrimitiveThis(final Object obj) {
347 return obj instanceof String || obj instanceof ConsString ||
348 obj instanceof Number || obj instanceof Boolean;
349 }
351 /**
352 * Creates an invoker method handle for a bound function.
353 *
354 * @param targetFn the function being bound
355 * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
356 * any of its specializations.
357 * @param self the "this" value being bound
358 * @param args additional arguments being bound
359 *
360 * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
361 * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
362 * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
363 * to the original invoker on invocation.
364 */
365 private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
366 // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
367 // in the target and will be ignored anyway.
368 final boolean isTargetBound = targetFn.isBoundFunction();
370 final boolean needsCallee = needsCallee(originalInvoker);
371 assert needsCallee == needsCallee() : "callee contract violation 2";
372 assert !(isTargetBound && needsCallee); // already bound functions don't need a callee
374 final Object boundSelf = isTargetBound ? null : convertThisObject(self);
375 final MethodHandle boundInvoker;
377 if (isVarArg(originalInvoker)) {
378 // First, bind callee and this without arguments
379 final MethodHandle noArgBoundInvoker;
381 if (isTargetBound) {
382 // Don't bind either callee or this
383 noArgBoundInvoker = originalInvoker;
384 } else if (needsCallee) {
385 // Bind callee and this
386 noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
387 } else {
388 // Only bind this
389 noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
390 }
391 // Now bind arguments
392 if (args.length > 0) {
393 boundInvoker = varArgBinder(noArgBoundInvoker, args);
394 } else {
395 boundInvoker = noArgBoundInvoker;
396 }
397 } else {
398 // If target is already bound, insert additional bound arguments after "this" argument, at position 1.
399 final int argInsertPos = isTargetBound ? 1 : 0;
400 final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))];
401 int next = 0;
402 if (!isTargetBound) {
403 if (needsCallee) {
404 boundArgs[next++] = targetFn;
405 }
406 boundArgs[next++] = boundSelf;
407 }
408 // If more bound args were specified than the function can take, we'll just drop those.
409 System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
410 // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
411 // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
412 // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
413 // start at position 1. If the function is not bound, we start inserting arguments at position 0.
414 boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs);
415 }
417 if (isTargetBound) {
418 return boundInvoker;
419 }
421 // If the target is not already bound, add a dropArguments that'll throw away the passed this
422 return MH.dropArguments(boundInvoker, 0, Object.class);
423 }
425 /**
426 * Creates a constructor method handle for a bound function using the passed constructor handle.
427 *
428 * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
429 * @param fn the function being bound
430 * @param args arguments being bound
431 *
432 * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
433 * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
434 * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
435 * this script function data object has no constructor handle, null is returned.
436 */
437 private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
438 assert originalConstructor != null;
440 // If target function is already bound, don't bother binding the callee.
441 final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
442 MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
444 if (args.length == 0) {
445 return calleeBoundConstructor;
446 }
448 if (isVarArg(calleeBoundConstructor)) {
449 return varArgBinder(calleeBoundConstructor, args);
450 }
452 final Object[] boundArgs;
454 final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
455 if (args.length <= maxArgCount) {
456 boundArgs = args;
457 } else {
458 boundArgs = new Object[maxArgCount];
459 System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
460 }
462 return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
463 }
465 /**
466 * Takes a method handle, and returns a potentially different method handle that can be used in
467 * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
468 * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
469 * {@code Object} as well, except for the following ones:
470 * <ul>
471 * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
472 * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
473 * (callee) as an argument.</li>
474 * </ul>
475 *
476 * @param mh the original method handle
477 *
478 * @return the new handle, conforming to the rules above.
479 */
480 protected MethodHandle composeGenericMethod(final MethodHandle mh) {
481 final MethodType type = mh.type();
482 MethodType newType = type.generic();
483 if (isVarArg(mh)) {
484 newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
485 }
486 if (needsCallee(mh)) {
487 newType = newType.changeParameterType(0, ScriptFunction.class);
488 }
489 return type.equals(newType) ? mh : mh.asType(newType);
490 }
492 /**
493 * Execute this script function.
494 *
495 * @param self Target object.
496 * @param arguments Call arguments.
497 * @return ScriptFunction result.
498 *
499 * @throws Throwable if there is an exception/error with the invocation or thrown from it
500 */
501 Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
502 final MethodHandle mh = getGenericInvoker();
504 final Object selfObj = convertThisObject(self);
505 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
507 if (isVarArg(mh)) {
508 if (needsCallee(mh)) {
509 return mh.invokeExact(fn, selfObj, args);
510 }
511 return mh.invokeExact(selfObj, args);
512 }
514 final int paramCount = mh.type().parameterCount();
515 if (needsCallee(mh)) {
516 switch (paramCount) {
517 case 2:
518 return mh.invokeExact(fn, selfObj);
519 case 3:
520 return mh.invokeExact(fn, selfObj, getArg(args, 0));
521 case 4:
522 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
523 case 5:
524 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
525 default:
526 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
527 }
528 }
530 switch (paramCount) {
531 case 1:
532 return mh.invokeExact(selfObj);
533 case 2:
534 return mh.invokeExact(selfObj, getArg(args, 0));
535 case 3:
536 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
537 case 4:
538 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
539 default:
540 return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
541 }
542 }
544 Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
545 final MethodHandle mh = getGenericConstructor();
547 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
549 if (isVarArg(mh)) {
550 if (needsCallee(mh)) {
551 return mh.invokeExact(fn, args);
552 }
553 return mh.invokeExact(args);
554 }
556 final int paramCount = mh.type().parameterCount();
557 if (needsCallee(mh)) {
558 switch (paramCount) {
559 case 1:
560 return mh.invokeExact(fn);
561 case 2:
562 return mh.invokeExact(fn, getArg(args, 0));
563 case 3:
564 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1));
565 case 4:
566 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2));
567 default:
568 return mh.invokeWithArguments(withArguments(fn, paramCount, args));
569 }
570 }
572 switch (paramCount) {
573 case 0:
574 return mh.invokeExact();
575 case 1:
576 return mh.invokeExact(getArg(args, 0));
577 case 2:
578 return mh.invokeExact(getArg(args, 0), getArg(args, 1));
579 case 3:
580 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2));
581 default:
582 return mh.invokeWithArguments(withArguments(null, paramCount, args));
583 }
584 }
586 private static Object getArg(final Object[] args, final int i) {
587 return i < args.length ? args[i] : UNDEFINED;
588 }
590 private static Object[] withArguments(final ScriptFunction fn, final int argCount, final Object[] args) {
591 final Object[] finalArgs = new Object[argCount];
593 int nextArg = 0;
594 if (fn != null) {
595 //needs callee
596 finalArgs[nextArg++] = fn;
597 }
599 // Don't add more args that there is argCount in the handle (including self and callee).
600 for (int i = 0; i < args.length && nextArg < argCount;) {
601 finalArgs[nextArg++] = args[i++];
602 }
604 // If we have fewer args than argCount, pad with undefined.
605 while (nextArg < argCount) {
606 finalArgs[nextArg++] = UNDEFINED;
607 }
609 return finalArgs;
610 }
612 private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
613 final Object[] finalArgs = new Object[argCount];
615 int nextArg = 0;
616 if (fn != null) {
617 //needs callee
618 finalArgs[nextArg++] = fn;
619 }
620 finalArgs[nextArg++] = self;
622 // Don't add more args that there is argCount in the handle (including self and callee).
623 for (int i = 0; i < args.length && nextArg < argCount;) {
624 finalArgs[nextArg++] = args[i++];
625 }
627 // If we have fewer args than argCount, pad with undefined.
628 while (nextArg < argCount) {
629 finalArgs[nextArg++] = UNDEFINED;
630 }
632 return finalArgs;
633 }
634 /**
635 * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the
636 * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on
637 * invocation
638 *
639 * @param mh the handle
640 * @param args the bound arguments
641 *
642 * @return the bound method handle
643 */
644 private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) {
645 assert args != null;
646 assert args.length > 0;
647 return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args));
648 }
650 /**
651 * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
652 * {@code Object}, the handle is returned unchanged.
653 *
654 * @param mh the handle to adapt
655 * @return the adapted handle
656 */
657 private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
658 return MH.asType(mh, mh.type().changeReturnType(Object.class));
659 }
661 private void ensureConstructor(final CompiledFunction inv) {
662 if (!inv.hasConstructor()) {
663 inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker())));
664 }
665 }
667 /**
668 * Heuristic to figure out if the method handle has a callee argument. If it's type is either
669 * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
670 * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
671 * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
672 * they also always receive a callee).
673 *
674 * @param mh the examined method handle
675 *
676 * @return true if the method handle expects a callee, false otherwise
677 */
678 protected static boolean needsCallee(final MethodHandle mh) {
679 final MethodType type = mh.type();
680 final int length = type.parameterCount();
682 if (length == 0) {
683 return false;
684 }
686 if (type.parameterType(0) == boolean.class) {
687 return length > 1 && type.parameterType(1) == ScriptFunction.class;
688 }
690 return type.parameterType(0) == ScriptFunction.class;
691 }
693 /**
694 * Check if a javascript function methodhandle is a vararg handle
695 *
696 * @param mh method handle to check
697 *
698 * @return true if vararg
699 */
700 protected static boolean isVarArg(final MethodHandle mh) {
701 final MethodType type = mh.type();
702 return type.parameterType(type.parameterCount() - 1).isArray();
703 }
705 @SuppressWarnings("unused")
706 private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
707 if (array2 == null) {
708 // Must clone it, as we can't allow the receiving method to alter the array
709 return array1.clone();
710 }
712 final int l2 = array2.length;
713 if (l2 == 0) {
714 return array1.clone();
715 }
717 final int l1 = array1.length;
718 final Object[] concat = new Object[l1 + l2];
719 System.arraycopy(array1, 0, concat, 0, l1);
720 System.arraycopy(array2, 0, concat, l1, l2);
722 return concat;
723 }
725 @SuppressWarnings("unused")
726 private static Object newFilter(final Object result, final Object allocation) {
727 return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
728 }
730 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
731 return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
732 }
733 }