aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/objects/NativeFunction.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/objects/NativeFunction.java')
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFunction.java101
1 files changed, 65 insertions, 36 deletions
diff --git a/src/jdk/nashorn/internal/objects/NativeFunction.java b/src/jdk/nashorn/internal/objects/NativeFunction.java
index 013683e0..bd2f2dd9 100644
--- a/src/jdk/nashorn/internal/objects/NativeFunction.java
+++ b/src/jdk/nashorn/internal/objects/NativeFunction.java
@@ -25,12 +25,15 @@
package jdk.nashorn.internal.objects;
+import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.util.List;
-
+import jdk.internal.dynalink.support.Lookup;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
@@ -41,10 +44,11 @@ import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ParserException;
import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
-import jdk.nashorn.internal.runtime.Source;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
/**
* ECMA 15.3 Function Objects
@@ -56,6 +60,9 @@ import jdk.nashorn.internal.runtime.Source;
@ScriptClass("Function")
public final class NativeFunction {
+ /** apply arg converter handle */
+ public static final MethodHandle TO_APPLY_ARGS = Lookup.findOwnStatic(MethodHandles.lookup(), "toApplyArgs", Object[].class, Object.class);
+
// initialized by nasgen
@SuppressWarnings("unused")
private static PropertyMap $nasgenmap$;
@@ -89,50 +96,75 @@ public final class NativeFunction {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object apply(final Object self, final Object thiz, final Object array) {
- if (!(self instanceof ScriptFunction) && !(self instanceof JSObject)) {
- throw typeError("not.a.function", ScriptRuntime.safeToString(self));
- }
+ checkCallable(self);
- Object[] args = null;
+ final Object[] args = toApplyArgs(array);
- if (array instanceof ScriptObject) {
+ if (self instanceof ScriptFunction) {
+ return ScriptRuntime.apply((ScriptFunction)self, thiz, args);
+ } else if (self instanceof JSObject) {
+ return ((JSObject)self).call(thiz, args);
+ }
+ throw new AssertionError("Should not reach here");
+ }
+
+ /**
+ * Given an array-like object, converts it into a Java object array suitable for invocation of ScriptRuntime.apply
+ * or for direct invocation of the applied function.
+ * @param array the array-like object. Can be null in which case a zero-length array is created.
+ * @return the Java array
+ */
+ public static Object[] toApplyArgs(final Object array) {
+ if (array instanceof NativeArguments) {
+ return ((NativeArguments)array).getArray().asObjectArray();
+ } else if (array instanceof ScriptObject) {
// look for array-like object
final ScriptObject sobj = (ScriptObject)array;
- final Object len = sobj.getLength();
- final int n = (int)JSType.toUint32(len);
+ final int n = lengthToInt(sobj.getLength());
- args = new Object[n];
+ final Object[] args = new Object[n];
for (int i = 0; i < args.length; i++) {
args[i] = sobj.get(i);
}
+ return args;
} else if (array instanceof Object[]) {
- args = (Object[])array;
+ return (Object[])array;
} else if (array instanceof List) {
final List<?> list = (List<?>)array;
- list.toArray(args = new Object[list.size()]);
+ return list.toArray(new Object[list.size()]);
} else if (array == null || array == UNDEFINED) {
- args = ScriptRuntime.EMPTY_ARRAY;
+ return ScriptRuntime.EMPTY_ARRAY;
} else if (array instanceof JSObject) {
// look for array-like JSObject object
final JSObject jsObj = (JSObject)array;
- final Object len = jsObj.hasMember("length")? jsObj.getMember("length") : Integer.valueOf(0);
- final int n = (int)JSType.toUint32(len);
+ final Object len = jsObj.hasMember("length")? jsObj.getMember("length") : Integer.valueOf(0);
+ final int n = lengthToInt(len);
- args = new Object[n];
+ final Object[] args = new Object[n];
for (int i = 0; i < args.length; i++) {
args[i] = jsObj.hasSlot(i)? jsObj.getSlot(i) : UNDEFINED;
}
+ return args;
} else {
throw typeError("function.apply.expects.array");
}
+ }
- if (self instanceof ScriptFunction) {
- return ScriptRuntime.apply((ScriptFunction)self, thiz, args);
- } else if (self instanceof JSObject) {
- return ((JSObject)self).call(thiz, args);
+ private static int lengthToInt(final Object len) {
+ final long ln = JSType.toUint32(len);
+ // NOTE: ECMASCript 5.1 section 15.3.4.3 says length should be treated as Uint32, but we wouldn't be able to
+ // allocate a Java array of more than MAX_VALUE elements anyway, so at this point we have to throw an error.
+ // People applying a function to more than 2^31 arguments will unfortunately be out of luck.
+ if (ln > Integer.MAX_VALUE) {
+ throw rangeError("range.error.inappropriate.array.length", JSType.toString(len));
}
+ return (int)ln;
+ }
- throw new AssertionError("should not reach here");
+ private static void checkCallable(final Object self) {
+ if (!(self instanceof ScriptFunction || (self instanceof JSObject && ((JSObject)self).isFunction()))) {
+ throw typeError("not.a.function", ScriptRuntime.safeToString(self));
+ }
}
/**
@@ -144,11 +176,9 @@ public final class NativeFunction {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object call(final Object self, final Object... args) {
- if (!(self instanceof ScriptFunction) && !(self instanceof JSObject)) {
- throw typeError("not.a.function", ScriptRuntime.safeToString(self));
- }
+ checkCallable(self);
- Object thiz = (args.length == 0) ? UNDEFINED : args[0];
+ final Object thiz = (args.length == 0) ? UNDEFINED : args[0];
Object[] arguments;
if (args.length > 1) {
@@ -175,11 +205,7 @@ public final class NativeFunction {
* @return function with bound arguments
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
- public static ScriptFunction bind(final Object self, final Object... args) {
- if (!(self instanceof ScriptFunction)) {
- throw typeError("not.a.function", ScriptRuntime.safeToString(self));
- }
-
+ public static Object bind(final Object self, final Object... args) {
final Object thiz = (args.length == 0) ? UNDEFINED : args[0];
Object[] arguments;
@@ -190,7 +216,7 @@ public final class NativeFunction {
arguments = ScriptRuntime.EMPTY_ARRAY;
}
- return ((ScriptFunctionImpl)self).makeBoundFunction(thiz, arguments);
+ return Bootstrap.bindCallable(self, thiz, arguments);
}
/**
@@ -236,7 +262,7 @@ public final class NativeFunction {
funcBody = JSType.toString(args[args.length - 1]);
final String paramList = paramListBuf.toString();
- if (! paramList.isEmpty()) {
+ if (!paramList.isEmpty()) {
checkFunctionParameters(paramList);
sb.append(paramList);
}
@@ -258,8 +284,7 @@ public final class NativeFunction {
}
private static void checkFunctionParameters(final String params) {
- final Source src = sourceFor("<function>", params);
- final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
+ final Parser parser = getParser(params);
try {
parser.parseFormalParameterList();
} catch (final ParserException pe) {
@@ -268,12 +293,16 @@ public final class NativeFunction {
}
private static void checkFunctionBody(final String funcBody) {
- final Source src = sourceFor("<function>", funcBody);
- final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
+ final Parser parser = getParser(funcBody);
try {
parser.parseFunctionBody();
} catch (final ParserException pe) {
pe.throwAsEcmaException();
}
}
+
+ private static Parser getParser(final String sourceText) {
+ final ScriptEnvironment env = Global.getEnv();
+ return new Parser(env, sourceFor("<function>", sourceText), new Context.ThrowErrorManager(), env._strict, null);
+ }
}