8055796: JSObject and browser JSObject linkers should provide fallback to call underlying Java methods directly
Reviewed-by: attila, hannesw
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Aug 21 20:06:48 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Fri Aug 22 15:47:28 2014 +0530
@@ -88,15 +88,14 @@
static {
final DynamicLinkerFactory factory = new DynamicLinkerFactory();
final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker();
- final JSObjectLinker jsObjectLinker = new JSObjectLinker(nashornBeansLinker);
factory.setPrioritizedLinkers(
new NashornLinker(),
new NashornPrimitiveLinker(),
new NashornStaticClassLinker(),
new BoundDynamicMethodLinker(),
new JavaSuperAdapterLinker(),
- jsObjectLinker,
- new BrowserJSObjectLinker(),
+ new JSObjectLinker(nashornBeansLinker),
+ new BrowserJSObjectLinker(nashornBeansLinker),
new ReflectionCheckLinker());
factory.setFallbackLinkers(nashornBeansLinker, new NashornBottomLinker());
factory.setSyncOnRelink(true);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Thu Aug 21 20:06:48 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Fri Aug 22 15:47:28 2014 +0530
@@ -48,6 +48,11 @@
// not final because this is lazily initialized
// when we hit a subclass for the first time.
private static volatile Class<?> jsObjectClass;
+ private final NashornBeansLinker nashornBeansLinker;
+
+ BrowserJSObjectLinker(final NashornBeansLinker nashornBeansLinker) {
+ this.nashornBeansLinker = nashornBeansLinker;
+ }
@Override
public boolean canLinkType(final Class<?> type) {
@@ -91,7 +96,7 @@
final GuardedInvocation inv;
if (jsObjectClass.isInstance(self)) {
- inv = lookup(desc);
+ inv = lookup(desc, request, linkerServices);
} else {
throw new AssertionError(); // Should never reach here.
}
@@ -99,7 +104,7 @@
return Bootstrap.asTypeSafeReturn(inv, linkerServices, desc);
}
- private static GuardedInvocation lookup(final CallSiteDescriptor desc) {
+ private GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request, final LinkerServices linkerServices) throws Exception {
final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
final int c = desc.getNameTokenCount();
@@ -107,7 +112,14 @@
case "getProp":
case "getElem":
case "getMethod":
- return c > 2 ? findGetMethod(desc) : findGetIndexMethod();
+ if (c > 2) {
+ return findGetMethod(desc);
+ } else {
+ // For indexed get, we want GuardedInvocation from beans linker and pass it.
+ // BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access.
+ final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
+ return findGetIndexMethod(beanInv);
+ }
case "setProp":
case "setElem":
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
@@ -122,8 +134,9 @@
return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
}
- private static GuardedInvocation findGetIndexMethod() {
- return new GuardedInvocation(JSOBJECTLINKER_GET, IS_JSOBJECT_GUARD);
+ private static GuardedInvocation findGetIndexMethod(final GuardedInvocation inv) {
+ final MethodHandle getter = MH.insertArguments(JSOBJECTLINKER_GET, 0, inv.getInvocation());
+ return inv.replaceMethods(getter, inv.getGuard());
}
private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) {
@@ -141,7 +154,7 @@
}
@SuppressWarnings("unused")
- private static Object get(final Object jsobj, final Object key) throws Throwable {
+ private static Object get(final MethodHandle fallback, final Object jsobj, final Object key) throws Throwable {
if (key instanceof Integer) {
return JSOBJECT_GETSLOT.invokeExact(jsobj, (int)key);
} else if (key instanceof Number) {
@@ -150,7 +163,12 @@
return JSOBJECT_GETSLOT.invokeExact(jsobj, index);
}
} else if (key instanceof String) {
- return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
+ final String name = (String)key;
+ if (name.indexOf('(') != -1) {
+ return fallback.invokeExact(jsobj, key);
+ } else {
+ return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
+ }
}
return null;
}
@@ -174,7 +192,7 @@
private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
// method handles of the current class
private static final MethodHandle IS_JSOBJECT_GUARD = findOwnMH_S("isJSObject", boolean.class, Object.class);
- private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH_S("get", Object.class, Object.class, Object.class);
+ private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH_S("get", Object.class, MethodHandle.class, Object.class, Object.class);
private static final MethodHandle JSOBJECTLINKER_PUT = findOwnMH_S("put", Void.TYPE, Object.class, Object.class, Object.class);
private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Thu Aug 21 20:06:48 2014 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Fri Aug 22 15:47:28 2014 +0530
@@ -81,7 +81,7 @@
final GuardedInvocation inv;
if (self instanceof JSObject) {
- inv = lookup(desc);
+ inv = lookup(desc, request, linkerServices);
} else if (self instanceof Map || self instanceof Bindings) {
// guard to make sure the Map or Bindings does not turn into JSObject later!
final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
@@ -110,7 +110,7 @@
}
- private static GuardedInvocation lookup(final CallSiteDescriptor desc) {
+ private GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request, final LinkerServices linkerServices) throws Exception {
final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
final int c = desc.getNameTokenCount();
@@ -118,7 +118,14 @@
case "getProp":
case "getElem":
case "getMethod":
- return c > 2 ? findGetMethod(desc) : findGetIndexMethod();
+ if (c > 2) {
+ return findGetMethod(desc);
+ } else {
+ // For indexed get, we want get GuardedInvocation beans linker and pass it.
+ // JSObjectLinker.get uses this fallback getter for explicit signature method access.
+ final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
+ return findGetIndexMethod(beanInv);
+ }
case "setProp":
case "setElem":
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
@@ -137,8 +144,9 @@
return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
}
- private static GuardedInvocation findGetIndexMethod() {
- return new GuardedInvocation(JSOBJECTLINKER_GET, IS_JSOBJECT_GUARD);
+ private static GuardedInvocation findGetIndexMethod(final GuardedInvocation inv) {
+ final MethodHandle getter = MH.insertArguments(JSOBJECTLINKER_GET, 0, inv.getInvocation());
+ return inv.replaceMethods(getter, inv.getGuard());
}
private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) {
@@ -170,7 +178,8 @@
}
@SuppressWarnings("unused")
- private static Object get(final Object jsobj, final Object key) {
+ private static Object get(final MethodHandle fallback, final Object jsobj, final Object key)
+ throws Throwable {
if (key instanceof Integer) {
return ((JSObject)jsobj).getSlot((Integer)key);
} else if (key instanceof Number) {
@@ -179,7 +188,13 @@
return ((JSObject)jsobj).getSlot(index);
}
} else if (key instanceof String) {
- return ((JSObject)jsobj).getMember((String)key);
+ final String name = (String)key;
+ // get with method name and signature. delegate it to beans linker!
+ if (name.indexOf('(') != -1) {
+ return fallback.invokeExact(jsobj, key);
+ } else {
+ return ((JSObject)jsobj).getMember(name);
+ }
}
return null;
}
@@ -238,7 +253,7 @@
// method handles of the current class
private static final MethodHandle IS_JSOBJECT_GUARD = findOwnMH_S("isJSObject", boolean.class, Object.class);
- private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH_S("get", Object.class, Object.class, Object.class);
+ private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH_S("get", Object.class, MethodHandle.class, Object.class, Object.class);
private static final MethodHandle JSOBJECTLINKER_PUT = findOwnMH_S("put", Void.TYPE, Object.class, Object.class, Object.class);
// method handles of JSObject class
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8055796.js Fri Aug 22 15:47:28 2014 +0530
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8055796: JSObject and browser JSObject linkers should provide fallback to call underlying Java methods directly
+ *
+ * @test
+ * @run
+ */
+
+var m = new javax.script.ScriptEngineManager();
+var e = m.getEngineByName("nashorn");
+var jsobj = e.eval("({ foo: 33, valueOf: function() 42 })");
+
+print("foo =", jsobj['getMember(java.lang.String)']("foo"));
+print("eval =", jsobj['eval(String)']("this + 44"));
+print("valueOf function? =", (jsobj.valueOf)['isFunction()']());
+
+var JSObject = Java.type("netscape.javascript.JSObject");
+var bjsobj = new (Java.extend(JSObject))() {
+ getMember: function(name) {
+ if (name == "func") {
+ return function(arg) {
+ print("func called with " + arg);
+ }
+ }
+ return name.toUpperCase();
+ },
+
+ getSlot: function(index) {
+ return index*index;
+ },
+
+ setMember: function(name, value) {
+ print(name + " set to " + value);
+ },
+
+ setSlot: function(index, value) {
+ print("[" + index + "] set to " + value);
+ }
+};
+
+print("getMember('foo') =", bjsobj['getMember(String)']('foo'));
+print("getSlot(6) =", bjsobj['getSlot(int)'](6));
+bjsobj['setMember(String, Object)']('bar', 'hello');
+bjsobj['setSlot(int, Object)'](10, 42);
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8055796.js.EXPECTED Fri Aug 22 15:47:28 2014 +0530
@@ -0,0 +1,7 @@
+foo = 33
+eval = 86
+valueOf function? = true
+getMember('foo') = FOO
+getSlot(6) = 36
+bar set to hello
+[10] set to 42