| /* |
| * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.nashorn.internal.runtime; |
| |
| import static jdk.nashorn.internal.lookup.Lookup.MH; |
| import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; |
| import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; |
| |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodType; |
| import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; |
| |
| /** |
| * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime. |
| * Instances of this class are created during codegen and stored in script classes' |
| * constants array to reduce function instantiation overhead during runtime. |
| */ |
| public abstract class ScriptFunctionData { |
| |
| /** Name of the function or "" for anonynous functions */ |
| protected final String name; |
| |
| /** All versions of this function that have been generated to code */ |
| protected final CompiledFunctions code; |
| |
| private int arity; |
| |
| private final boolean isStrict; |
| |
| private final boolean isBuiltin; |
| |
| private final boolean isConstructor; |
| |
| private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); |
| private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class); |
| |
| /** |
| * Constructor |
| * |
| * @param name script function name |
| * @param arity arity |
| * @param isStrict is the function strict |
| * @param isBuiltin is the function built in |
| * @param isConstructor is the function a constructor |
| */ |
| protected ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { |
| this.name = name; |
| this.arity = arity; |
| this.code = new CompiledFunctions(); |
| this.isStrict = isStrict; |
| this.isBuiltin = isBuiltin; |
| this.isConstructor = isConstructor; |
| } |
| |
| final int getArity() { |
| return arity; |
| } |
| |
| /** |
| * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final |
| * @param arity new arity |
| */ |
| void setArity(final int arity) { |
| this.arity = arity; |
| } |
| |
| CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) { |
| final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args); |
| |
| //TODO the boundinvoker.type() could actually be more specific here |
| if (isConstructor()) { |
| ensureConstructor(originalInv); |
| return new CompiledFunction(boundInvoker.type(), boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args)); |
| } |
| |
| return new CompiledFunction(boundInvoker.type(), boundInvoker); |
| } |
| |
| /** |
| * Is this a ScriptFunction generated with strict semantics? |
| * @return true if strict, false otherwise |
| */ |
| public boolean isStrict() { |
| return isStrict; |
| } |
| |
| boolean isBuiltin() { |
| return isBuiltin; |
| } |
| |
| boolean isConstructor() { |
| return isConstructor; |
| } |
| |
| boolean needsCallee() { |
| // we don't know if we need a callee or not unless we are generated |
| ensureCodeGenerated(); |
| return code.needsCallee(); |
| } |
| |
| /** |
| * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument |
| * according to ECMA 10.4.3. |
| * @return true if this argument must be an object |
| */ |
| boolean needsWrappedThis() { |
| return !isStrict && !isBuiltin; |
| } |
| |
| String toSource() { |
| return "function " + (name == null ? "" : name) + "() { [native code] }"; |
| } |
| |
| String getName() { |
| return name; |
| } |
| |
| /** |
| * Get this function as a String containing its source code. If no source code |
| * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} |
| * |
| * @return string representation of this function |
| */ |
| @Override |
| public String toString() { |
| final StringBuilder sb = new StringBuilder(); |
| |
| sb.append("name='"). |
| append(name.isEmpty() ? "<anonymous>" : name). |
| append("' "). |
| append(code.size()). |
| append(" invokers="). |
| append(code); |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Pick the best invoker, i.e. the one version of this method with as narrow and specific |
| * types as possible. If the call site arguments are objects, but boxed primitives we can |
| * also try to get a primitive version of the method and do an unboxing filter, but then |
| * we need to insert a guard that checks the argument is really always a boxed primitive |
| * and not suddenly a "real" object |
| * |
| * @param callSiteType callsite type |
| * @param args arguments at callsite on first trampoline invocation |
| * @return method handle to best invoker |
| */ |
| MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { |
| return getBest(callSiteType).getInvoker(); |
| } |
| |
| MethodHandle getBestInvoker(final MethodType callSiteType) { |
| return getBestInvoker(callSiteType, null); |
| } |
| |
| MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) { |
| if (!isConstructor()) { |
| throw typeError("not.a.constructor", toSource()); |
| } |
| ensureCodeGenerated(); |
| |
| final CompiledFunction best = getBest(callSiteType); |
| ensureConstructor(best); |
| return best.getConstructor(); |
| } |
| |
| MethodHandle getBestConstructor(final MethodType callSiteType) { |
| return getBestConstructor(callSiteType, null); |
| } |
| |
| /** |
| * Subclass responsibility. If we can have lazy code generation, this is a hook to ensure that |
| * code exists before performing an operation. |
| */ |
| protected void ensureCodeGenerated() { |
| //empty |
| } |
| |
| /** |
| * Return a generic Object/Object invoker for this method. It will ensure code |
| * is generated, get the most generic of all versions of this function and adapt it |
| * to Objects. |
| * |
| * TODO this is only public because {@link JavaAdapterFactory} can't supply us with |
| * a MethodType that we can use for lookup due to boostrapping problems. Can be fixed |
| * |
| * @return generic invoker of this script function |
| */ |
| public final MethodHandle getGenericInvoker() { |
| ensureCodeGenerated(); |
| return composeGenericMethod(code.mostGeneric().getInvoker()); |
| } |
| |
| private CompiledFunction getBest(final MethodType callSiteType) { |
| ensureCodeGenerated(); |
| return code.best(callSiteType); |
| } |
| |
| /** |
| * Allocates an object using this function's allocator. |
| * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator. |
| */ |
| ScriptObject allocate() { |
| return null; |
| } |
| |
| /** |
| * This method is used to create the immutable portion of a bound function. |
| * See {@link ScriptFunction#makeBoundFunction(Object, Object[])} |
| * |
| * @param fn the original function being bound |
| * @param self this reference to bind. Can be null. |
| * @param args additional arguments to bind. Can be null. |
| */ |
| ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) { |
| ensureCodeGenerated(); |
| |
| final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args; |
| final int length = args == null ? 0 : args.length; |
| |
| CompiledFunctions boundList = new CompiledFunctions(); |
| for (final CompiledFunction inv : code) { |
| boundList.add(bind(inv, fn, self, allArgs)); |
| } |
| ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor()); |
| return boundData; |
| } |
| |
| /** |
| * Compose a constructor given a primordial constructor handle |
| * |
| * @param ctor primordial constructor handle |
| * @param needsCallee do we need to pass a callee |
| * |
| * @return the composed constructor |
| */ |
| protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) { |
| // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having |
| // "this" in the first argument position is what allows the elegant folded composition of |
| // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor |
| // always returns Object. |
| MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor; |
| |
| composedCtor = changeReturnTypeToObject(composedCtor); |
| |
| final MethodType ctorType = composedCtor.type(); |
| |
| // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually |
| // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it. |
| // (this, [callee, ]args...) => ([callee, ]args...) |
| final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); |
| |
| // Fold constructor into newFilter that replaces the return value from the constructor with the originally |
| // allocated value when the originally allocated value is a primitive. |
| // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...) |
| composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor); |
| |
| // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject... |
| if (needsCallee) { |
| // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and |
| // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...), |
| // or... |
| return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE); |
| } |
| |
| // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee |
| // (this, args...) filter (callee) => (callee, args...) |
| return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE); |
| } |
| |
| /** |
| * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed |
| * method handle. If this function's method handles don't need a callee parameter, returns the original method |
| * handle unchanged. |
| * |
| * @param mh a method handle with order of arguments {@code (callee, this, args...)} |
| * |
| * @return a method handle with order of arguments {@code (this, callee, args...)} |
| */ |
| private static MethodHandle swapCalleeAndThis(final MethodHandle mh) { |
| final MethodType type = mh.type(); |
| assert type.parameterType(0) == ScriptFunction.class : type; |
| assert type.parameterType(1) == Object.class : type; |
| final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class); |
| final int[] reorder = new int[type.parameterCount()]; |
| reorder[0] = 1; |
| assert reorder[1] == 0; |
| for (int i = 2; i < reorder.length; ++i) { |
| reorder[i] = i; |
| } |
| return MethodHandles.permuteArguments(mh, newType, reorder); |
| } |
| |
| /** |
| * Convert this argument for non-strict functions according to ES 10.4.3 |
| * |
| * @param thiz the this argument |
| * |
| * @return the converted this object |
| */ |
| private Object convertThisObject(final Object thiz) { |
| if (!(thiz instanceof ScriptObject) && needsWrappedThis()) { |
| if (JSType.nullOrUndefined(thiz)) { |
| return Context.getGlobalTrusted(); |
| } |
| |
| if (isPrimitiveThis(thiz)) { |
| return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz); |
| } |
| } |
| |
| return thiz; |
| } |
| |
| static boolean isPrimitiveThis(final Object obj) { |
| return obj instanceof String || obj instanceof ConsString || |
| obj instanceof Number || obj instanceof Boolean; |
| } |
| |
| /** |
| * Creates an invoker method handle for a bound function. |
| * |
| * @param targetFn the function being bound |
| * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or |
| * any of its specializations. |
| * @param self the "this" value being bound |
| * @param args additional arguments being bound |
| * |
| * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting |
| * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting |
| * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed |
| * to the original invoker on invocation. |
| */ |
| private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) { |
| // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound |
| // in the target and will be ignored anyway. |
| final boolean isTargetBound = targetFn.isBoundFunction(); |
| |
| final boolean needsCallee = needsCallee(originalInvoker); |
| assert needsCallee == needsCallee() : "callee contract violation 2"; |
| assert !(isTargetBound && needsCallee); // already bound functions don't need a callee |
| |
| final Object boundSelf = isTargetBound ? null : convertThisObject(self); |
| final MethodHandle boundInvoker; |
| |
| if (isVarArg(originalInvoker)) { |
| // First, bind callee and this without arguments |
| final MethodHandle noArgBoundInvoker; |
| |
| if (isTargetBound) { |
| // Don't bind either callee or this |
| noArgBoundInvoker = originalInvoker; |
| } else if (needsCallee) { |
| // Bind callee and this |
| noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf); |
| } else { |
| // Only bind this |
| noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf); |
| } |
| // Now bind arguments |
| if (args.length > 0) { |
| boundInvoker = varArgBinder(noArgBoundInvoker, args); |
| } else { |
| boundInvoker = noArgBoundInvoker; |
| } |
| } else { |
| // If target is already bound, insert additional bound arguments after "this" argument, at position 1. |
| final int argInsertPos = isTargetBound ? 1 : 0; |
| final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))]; |
| int next = 0; |
| if (!isTargetBound) { |
| if (needsCallee) { |
| boundArgs[next++] = targetFn; |
| } |
| boundArgs[next++] = boundSelf; |
| } |
| // If more bound args were specified than the function can take, we'll just drop those. |
| System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next); |
| // If target is already bound, insert additional bound arguments after "this" argument, at position 1; |
| // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions |
| // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args |
| // start at position 1. If the function is not bound, we start inserting arguments at position 0. |
| boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs); |
| } |
| |
| if (isTargetBound) { |
| return boundInvoker; |
| } |
| |
| // If the target is not already bound, add a dropArguments that'll throw away the passed this |
| return MH.dropArguments(boundInvoker, 0, Object.class); |
| } |
| |
| /** |
| * Creates a constructor method handle for a bound function using the passed constructor handle. |
| * |
| * @param originalConstructor the constructor handle to bind. It must be a composed constructor. |
| * @param fn the function being bound |
| * @param args arguments being bound |
| * |
| * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never |
| * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor |
| * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if |
| * this script function data object has no constructor handle, null is returned. |
| */ |
| private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) { |
| assert originalConstructor != null; |
| |
| // If target function is already bound, don't bother binding the callee. |
| final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor : |
| MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class); |
| |
| if (args.length == 0) { |
| return calleeBoundConstructor; |
| } |
| |
| if (isVarArg(calleeBoundConstructor)) { |
| return varArgBinder(calleeBoundConstructor, args); |
| } |
| |
| final Object[] boundArgs; |
| |
| final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1; |
| if (args.length <= maxArgCount) { |
| boundArgs = args; |
| } else { |
| boundArgs = new Object[maxArgCount]; |
| System.arraycopy(args, 0, boundArgs, 0, maxArgCount); |
| } |
| |
| return MH.insertArguments(calleeBoundConstructor, 1, boundArgs); |
| } |
| |
| /** |
| * Takes a method handle, and returns a potentially different method handle that can be used in |
| * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}. |
| * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into |
| * {@code Object} as well, except for the following ones: |
| * <ul> |
| * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> |
| * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself |
| * (callee) as an argument.</li> |
| * </ul> |
| * |
| * @param mh the original method handle |
| * |
| * @return the new handle, conforming to the rules above. |
| */ |
| protected MethodHandle composeGenericMethod(final MethodHandle mh) { |
| final MethodType type = mh.type(); |
| MethodType newType = type.generic(); |
| if (isVarArg(mh)) { |
| newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); |
| } |
| if (needsCallee(mh)) { |
| newType = newType.changeParameterType(0, ScriptFunction.class); |
| } |
| return type.equals(newType) ? mh : mh.asType(newType); |
| } |
| |
| /** |
| * Execute this script function. |
| * |
| * @param self Target object. |
| * @param arguments Call arguments. |
| * @return ScriptFunction result. |
| * |
| * @throws Throwable if there is an exception/error with the invocation or thrown from it |
| */ |
| Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable { |
| final MethodHandle mh = getGenericInvoker(); |
| |
| final Object selfObj = convertThisObject(self); |
| final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; |
| |
| if (isVarArg(mh)) { |
| if (needsCallee(mh)) { |
| return mh.invokeExact(fn, selfObj, args); |
| } |
| return mh.invokeExact(selfObj, args); |
| } |
| |
| final int paramCount = mh.type().parameterCount(); |
| if (needsCallee(mh)) { |
| switch (paramCount) { |
| case 2: |
| return mh.invokeExact(fn, selfObj); |
| case 3: |
| return mh.invokeExact(fn, selfObj, getArg(args, 0)); |
| case 4: |
| return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1)); |
| case 5: |
| return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); |
| default: |
| return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args)); |
| } |
| } |
| |
| switch (paramCount) { |
| case 1: |
| return mh.invokeExact(selfObj); |
| case 2: |
| return mh.invokeExact(selfObj, getArg(args, 0)); |
| case 3: |
| return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); |
| case 4: |
| return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); |
| default: |
| return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args)); |
| } |
| } |
| |
| private static Object getArg(final Object[] args, final int i) { |
| return i < args.length ? args[i] : UNDEFINED; |
| } |
| |
| private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) { |
| final Object[] finalArgs = new Object[argCount]; |
| |
| int nextArg = 0; |
| if (fn != null) { |
| //needs callee |
| finalArgs[nextArg++] = fn; |
| } |
| finalArgs[nextArg++] = self; |
| |
| // Don't add more args that there is argCount in the handle (including self and callee). |
| for (int i = 0; i < args.length && nextArg < argCount;) { |
| finalArgs[nextArg++] = args[i++]; |
| } |
| |
| // If we have fewer args than argCount, pad with undefined. |
| while (nextArg < argCount) { |
| finalArgs[nextArg++] = UNDEFINED; |
| } |
| |
| return finalArgs; |
| } |
| /** |
| * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the |
| * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on |
| * invocation |
| * |
| * @param mh the handle |
| * @param args the bound arguments |
| * |
| * @return the bound method handle |
| */ |
| private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) { |
| assert args != null; |
| assert args.length > 0; |
| return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args)); |
| } |
| |
| /** |
| * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already |
| * {@code Object}, the handle is returned unchanged. |
| * |
| * @param mh the handle to adapt |
| * @return the adapted handle |
| */ |
| private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) { |
| return MH.asType(mh, mh.type().changeReturnType(Object.class)); |
| } |
| |
| private void ensureConstructor(final CompiledFunction inv) { |
| if (!inv.hasConstructor()) { |
| inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker()))); |
| } |
| } |
| |
| /** |
| * Heuristic to figure out if the method handle has a callee argument. If it's type is either |
| * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has |
| * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly |
| * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore |
| * they also always receive a callee). |
| * |
| * @param mh the examined method handle |
| * |
| * @return true if the method handle expects a callee, false otherwise |
| */ |
| protected static boolean needsCallee(final MethodHandle mh) { |
| final MethodType type = mh.type(); |
| final int length = type.parameterCount(); |
| |
| if (length == 0) { |
| return false; |
| } |
| |
| if (type.parameterType(0) == boolean.class) { |
| return length > 1 && type.parameterType(1) == ScriptFunction.class; |
| } |
| |
| return type.parameterType(0) == ScriptFunction.class; |
| } |
| |
| /** |
| * Check if a javascript function methodhandle is a vararg handle |
| * |
| * @param mh method handle to check |
| * |
| * @return true if vararg |
| */ |
| protected static boolean isVarArg(final MethodHandle mh) { |
| final MethodType type = mh.type(); |
| return type.parameterType(type.parameterCount() - 1).isArray(); |
| } |
| |
| @SuppressWarnings("unused") |
| private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) { |
| if (array2 == null) { |
| // Must clone it, as we can't allow the receiving method to alter the array |
| return array1.clone(); |
| } |
| |
| final int l2 = array2.length; |
| if (l2 == 0) { |
| return array1.clone(); |
| } |
| |
| final int l1 = array1.length; |
| final Object[] concat = new Object[l1 + l2]; |
| System.arraycopy(array1, 0, concat, 0, l1); |
| System.arraycopy(array2, 0, concat, l1, l2); |
| |
| return concat; |
| } |
| |
| @SuppressWarnings("unused") |
| private static Object newFilter(final Object result, final Object allocation) { |
| return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation; |
| } |
| |
| private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { |
| return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types)); |
| } |
| } |