--- a/nashorn/src/jdk/nashorn/api/scripting/JSObject.java Tue Oct 22 17:47:56 2013 +0530
+++ b/nashorn/src/jdk/nashorn/api/scripting/JSObject.java Tue Oct 22 22:12:24 2013 +0530
@@ -234,4 +234,13 @@
public boolean isArray() {
return false;
}
+
+ /**
+ * Returns this object's numeric value.
+ *
+ * @return this object's numeric value.
+ */
+ public double toNumber() {
+ return Double.NaN;
+ }
}
--- a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Tue Oct 22 17:47:56 2013 +0530
+++ b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Tue Oct 22 22:12:24 2013 +0530
@@ -43,6 +43,7 @@
import javax.script.Bindings;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.GlobalObject;
+import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -705,4 +706,13 @@
}
}
}
+
+ @Override
+ public double toNumber() {
+ return inGlobal(new Callable<Double>() {
+ @Override public Double call() {
+ return JSType.toNumber(sobj);
+ }
+ });
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java Tue Oct 22 17:47:56 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java Tue Oct 22 22:12:24 2013 +0530
@@ -72,8 +72,7 @@
}
// convert to boolean
- codegen.load(unaryNode);
- method.convert(Type.BOOLEAN);
+ codegen.load(unaryNode, Type.BOOLEAN);
if (state) {
method.ifne(label);
} else {
@@ -146,8 +145,7 @@
break;
}
- codegen.load(binaryNode);
- method.convert(Type.BOOLEAN);
+ codegen.load(binaryNode, Type.BOOLEAN);
if (state) {
method.ifne(label);
} else {
@@ -169,8 +167,7 @@
}
}
- codegen.load(node);
- method.convert(Type.BOOLEAN);
+ codegen.load(node, Type.BOOLEAN);
if (state) {
method.ifne(label);
} else {
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Oct 22 17:47:56 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Oct 22 22:12:24 2013 +0530
@@ -402,7 +402,7 @@
return loadBinaryOperands(node.lhs(), node.rhs(), node.getType(), false);
}
- private MethodEmitter load(final Expression node, final Type type) {
+ MethodEmitter load(final Expression node, final Type type) {
return load(node, type, false);
}
@@ -432,7 +432,7 @@
@Override
public boolean enterAccessNode(final AccessNode accessNode) {
if (!baseAlreadyOnStack) {
- load(accessNode.getBase()).convert(Type.OBJECT);
+ load(accessNode.getBase(), Type.OBJECT);
}
assert method.peekType().isObject();
method.dynamicGet(type, accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
@@ -442,7 +442,7 @@
@Override
public boolean enterIndexNode(final IndexNode indexNode) {
if (!baseAlreadyOnStack) {
- load(indexNode.getBase()).convert(Type.OBJECT);
+ load(indexNode.getBase(), Type.OBJECT);
load(indexNode.getIndex());
}
method.dynamicGetIndex(type, getCallSiteFlags(), indexNode.isFunction());
@@ -632,11 +632,13 @@
final Type[] params = signature == null ? null : Type.getMethodArguments(signature);
for (final Expression arg : args) {
assert arg != null;
- load(arg);
if (n >= argCount) {
+ load(arg);
method.pop(); // we had to load the arg for its side effects
} else if (params != null) {
- method.convert(params[n]);
+ load(arg, params[n]);
+ } else {
+ load(arg);
}
n++;
}
@@ -1277,7 +1279,7 @@
for (int i = 0; i < args.size(); i++) {
method.dup();
method.load(i);
- load(args.get(i)).convert(Type.OBJECT); //has to be upcast to object or we fail
+ load(args.get(i), Type.OBJECT); //has to be upcast to object or we fail
method.arraystore();
}
@@ -1719,7 +1721,7 @@
}
for (final Expression arg : args) {
- load(arg).convert(Type.OBJECT);
+ load(arg, Type.OBJECT);
}
method.invokestatic(
@@ -2105,7 +2107,7 @@
if (exceptionCondition != null) {
next = new Label("next");
- load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
+ load(exceptionCondition, Type.BOOLEAN).ifeq(next);
} else {
next = null;
}
@@ -2352,7 +2354,7 @@
final List<Expression> args = callNode.getArgs();
// Load function reference.
- load(callNode.getFunction()).convert(Type.OBJECT); // must detect type error
+ load(callNode.getFunction(), Type.OBJECT); // must detect type error
method.dynamicNew(1 + loadArgs(args), getCallSiteFlags());
method.store(unaryNode.getSymbol());
@@ -2383,7 +2385,7 @@
@Override
public boolean enterSUB(final UnaryNode unaryNode) {
assert unaryNode.getType().isNumeric();
- load(unaryNode.rhs()).convert(unaryNode.getType()).neg().store(unaryNode.getSymbol());
+ load(unaryNode.rhs(), unaryNode.getType()).neg().store(unaryNode.getSymbol());
return false;
}
@@ -2424,7 +2426,7 @@
final Label skip = new Label("skip");
- load(lhs).convert(Type.OBJECT).dup().convert(Type.BOOLEAN);
+ load(lhs, Type.OBJECT).dup().convert(Type.BOOLEAN);
if (binaryNode.tokenType() == TokenType.AND) {
method.ifeq(skip);
@@ -2433,7 +2435,7 @@
}
method.pop();
- load(rhs).convert(Type.OBJECT);
+ load(rhs, Type.OBJECT);
method.label(skip);
method.store(binaryNode.getSymbol());
--- a/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Tue Oct 22 17:47:56 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Tue Oct 22 22:12:24 2013 +0530
@@ -32,7 +32,6 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.LiteralNode;
@@ -143,7 +142,7 @@
method.dup();
method.getField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor());
method.load(property.getSlot());
- codegen.load(values.get(i)).convert(OBJECT);
+ codegen.load(values.get(i), OBJECT);
method.arraystore();
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java Tue Oct 22 17:47:56 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java Tue Oct 22 22:12:24 2013 +0530
@@ -246,12 +246,11 @@
* @return the primitive form of the object
*/
public static Object toPrimitive(final Object obj, final Class<?> hint) {
- if (!(obj instanceof ScriptObject)) {
- return obj;
- }
+ return obj instanceof ScriptObject ? toPrimitive((ScriptObject)obj, hint) : obj;
+ }
- final ScriptObject sobj = (ScriptObject)obj;
- final Object result = sobj.getDefaultValue(hint);
+ private static Object toPrimitive(final ScriptObject sobj, final Class<?> hint) {
+ final Object result = sobj.getDefaultValue(hint);
if (!isPrimitive(result)) {
throw typeError("bad.default.value", result.toString());
@@ -475,6 +474,19 @@
return toNumberGeneric(obj);
}
+
+ /**
+ * JavaScript compliant conversion of Object to number
+ * See ECMA 9.3 ToNumber
+ *
+ * @param obj an object
+ *
+ * @return a number
+ */
+ public static double toNumber(final ScriptObject obj) {
+ return toNumber(toPrimitive(obj, Number.class));
+ }
+
/**
* Digit representation for a character
*
@@ -1028,7 +1040,7 @@
}
if (obj instanceof ScriptObject) {
- return toNumber(toPrimitive(obj, Number.class));
+ return toNumber((ScriptObject)obj);
}
return Double.NaN;
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptLoader.java Tue Oct 22 17:47:56 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptLoader.java Tue Oct 22 22:12:24 2013 +0530
@@ -52,24 +52,10 @@
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
checkPackageAccess(name);
- try {
- return super.loadClass(name, resolve);
- } catch (final ClassNotFoundException | SecurityException e) {
- // We'll get ClassNotFoundException for Nashorn 'struct' classes.
- // Also, we'll get SecurityException for jdk.nashorn.internal.*
- // classes. So, load these using to context's 'shared' loader.
- // All these classes start with "jdk.nashorn.internal." prefix.
- try {
- if (name.startsWith(NASHORN_PKG_PREFIX)) {
- return context.getSharedLoader().loadClass(name);
- }
- } catch (final ClassNotFoundException ignored) {
- //ignored
- }
-
- // throw the original exception from here
- throw e;
+ if (name.startsWith(NASHORN_PKG_PREFIX)) {
+ return context.getSharedLoader().loadClass(name);
}
+ return super.loadClass(name, resolve);
}
// package-private and private stuff below this point
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Tue Oct 22 17:47:56 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Tue Oct 22 22:12:24 2013 +0530
@@ -25,25 +25,28 @@
package jdk.nashorn.internal.runtime.linker;
-import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.HashMap;
+import java.util.Map;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
+import jdk.nashorn.api.scripting.JSObject;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
+import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.api.scripting.JSObject;
/**
* A Dynalink linker to handle web browser built-in JS (DOM etc.) objects as well
* as ScriptObjects from other Nashorn contexts.
*/
-final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
+final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory {
@Override
public boolean canLinkType(final Class<?> type) {
return canLinkTypeStatic(type);
@@ -75,6 +78,22 @@
return Bootstrap.asType(inv, linkerServices, desc);
}
+ @Override
+ public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
+ final boolean sourceIsAlwaysJSObject = JSObject.class.isAssignableFrom(sourceType);
+ if(!sourceIsAlwaysJSObject && !sourceType.isAssignableFrom(JSObject.class)) {
+ return null;
+ }
+
+ final MethodHandle converter = CONVERTERS.get(targetType);
+ if(converter == null) {
+ return null;
+ }
+
+ return new GuardedInvocation(converter, sourceIsAlwaysJSObject ? null : IS_JSOBJECT_GUARD).asType(MethodType.methodType(targetType, sourceType));
+ }
+
+
private static GuardedInvocation lookup(final CallSiteDescriptor desc) {
final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
final int c = desc.getNameTokenCount();
@@ -87,9 +106,9 @@
case "setElem":
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
case "call":
- return findCallMethod(desc, operator);
+ return findCallMethod(desc);
case "callMethod":
- return findCallMethodMethod(desc, operator);
+ return findCallMethodMethod(desc);
case "new":
return findNewMethod(desc);
default:
@@ -115,14 +134,14 @@
return new GuardedInvocation(JSOBJECTLINKER_PUT, null, IS_JSOBJECT_GUARD);
}
- private static GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final String operator) {
+ private static GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc) {
final String methodName = desc.getNameToken(2);
MethodHandle func = MH.insertArguments(JSOBJECT_CALLMEMBER, 1, methodName);
func = MH.asCollector(func, Object[].class, desc.getMethodType().parameterCount() - 1);
return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD);
}
- private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final String operator) {
+ private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc) {
final MethodHandle func = MH.asCollector(JSOBJECT_CALL, Object[].class, desc.getMethodType().parameterCount() - 2);
return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD);
}
@@ -163,6 +182,25 @@
}
}
+ @SuppressWarnings("unused")
+ private static int toInt32(final JSObject obj) {
+ return JSType.toInt32(toNumber(obj));
+ }
+
+ @SuppressWarnings("unused")
+ private static long toInt64(final JSObject obj) {
+ return JSType.toInt64(toNumber(obj));
+ }
+
+ private static double toNumber(final JSObject obj) {
+ return obj == null ? 0 : obj.toNumber();
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean toBoolean(final JSObject obj) {
+ return obj != null;
+ }
+
private static int getIndex(final Number n) {
final double value = n.doubleValue();
return JSType.isRepresentableAsInt(value) ? (int)value : -1;
@@ -182,23 +220,28 @@
private static final MethodHandle JSOBJECT_CALL = findJSObjectMH("call", Object.class, Object.class, Object[].class);
private static final MethodHandle JSOBJECT_NEW = findJSObjectMH("newObject", Object.class, Object[].class);
+ private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>();
+ static {
+ CONVERTERS.put(boolean.class, findOwnMH("toBoolean", boolean.class, JSObject.class));
+ CONVERTERS.put(int.class, findOwnMH("toInt32", int.class, JSObject.class));
+ CONVERTERS.put(long.class, findOwnMH("toInt64", long.class, JSObject.class));
+ CONVERTERS.put(double.class, findOwnMH("toNumber", double.class, JSObject.class));
+ }
+
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
- final Class<?> own = JSObjectLinker.class;
- final MethodType mt = MH.type(rtype, types);
- try {
- return MH.findStatic(MethodHandles.lookup(), own, name, mt);
- } catch (final MethodHandleFactory.LookupException e) {
- return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
- }
+ return findMH(name, JSObjectLinker.class, rtype, types);
}
private static MethodHandle findJSObjectMH(final String name, final Class<?> rtype, final Class<?>... types) {
- final Class<?> own = JSObject.class;
+ return findMH(name, JSObject.class, rtype, types);
+ }
+
+ private static MethodHandle findMH(final String name, final Class<?> target, final Class<?> rtype, final Class<?>... types) {
final MethodType mt = MH.type(rtype, types);
try {
- return MH.findVirtual(MethodHandles.publicLookup(), own, name, mt);
+ return MH.findStatic(MethodHandles.lookup(), target, name, mt);
} catch (final MethodHandleFactory.LookupException e) {
- return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
+ return MH.findVirtual(MethodHandles.lookup(), target, name, mt);
}
}
}