8055796: JSObject and browser JSObject linkers should provide fallback to call underlying Java methods directly
authorsundar
Fri, 22 Aug 2014 15:47:28 +0530
changeset 26237 b6509e37ce64
parent 26236 78b5ece438c0
child 26238 829648e12198
8055796: JSObject and browser JSObject linkers should provide fallback to call underlying Java methods directly Reviewed-by: attila, hannesw
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
nashorn/test/script/basic/JDK-8055796.js
nashorn/test/script/basic/JDK-8055796.js.EXPECTED
--- 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