Thu, 21 Aug 2014 20:06:48 +0530
8055762: Nashorn misses linker for netscape.javascript.JSObject instances
Reviewed-by: lagergren, jlaskey
1.1 --- a/make/build.xml Thu Aug 21 14:03:24 2014 +0530 1.2 +++ b/make/build.xml Thu Aug 21 20:06:48 2014 +0530 1.3 @@ -34,6 +34,7 @@ 1.4 <loadproperties srcFile="make/project.properties"/> 1.5 <path id="nashorn.ext.path"> 1.6 <pathelement location="${dist.dir}"/> 1.7 + <pathelement location="${java.ext.dirs}"/> 1.8 </path> 1.9 <property name="ext.class.path" value="-Djava.ext.dirs="${toString:nashorn.ext.path}""/> 1.10 <condition property="svn.executable" value="/usr/local/bin/svn" else="svn">
2.1 --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Aug 21 14:03:24 2014 +0530 2.2 +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Aug 21 20:06:48 2014 +0530 2.3 @@ -96,6 +96,7 @@ 2.4 new BoundDynamicMethodLinker(), 2.5 new JavaSuperAdapterLinker(), 2.6 jsObjectLinker, 2.7 + new BrowserJSObjectLinker(), 2.8 new ReflectionCheckLinker()); 2.9 factory.setFallbackLinkers(nashornBeansLinker, new NashornBottomLinker()); 2.10 factory.setSyncOnRelink(true);
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Thu Aug 21 20:06:48 2014 +0530 3.3 @@ -0,0 +1,199 @@ 3.4 +/* 3.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. Oracle designates this 3.11 + * particular file as subject to the "Classpath" exception as provided 3.12 + * by Oracle in the LICENSE file that accompanied this code. 3.13 + * 3.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.17 + * version 2 for more details (a copy is included in the LICENSE file that 3.18 + * accompanied this code). 3.19 + * 3.20 + * You should have received a copy of the GNU General Public License version 3.21 + * 2 along with this work; if not, write to the Free Software Foundation, 3.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.23 + * 3.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.25 + * or visit www.oracle.com if you need additional information or have any 3.26 + * questions. 3.27 + */ 3.28 + 3.29 +package jdk.nashorn.internal.runtime.linker; 3.30 + 3.31 +import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.*; 3.32 + 3.33 +import java.lang.invoke.MethodHandle; 3.34 +import java.lang.invoke.MethodHandles; 3.35 +import jdk.internal.dynalink.CallSiteDescriptor; 3.36 +import jdk.internal.dynalink.linker.GuardedInvocation; 3.37 +import jdk.internal.dynalink.linker.LinkRequest; 3.38 +import jdk.internal.dynalink.linker.LinkerServices; 3.39 +import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; 3.40 +import jdk.internal.dynalink.support.CallSiteDescriptorFactory; 3.41 +import jdk.nashorn.internal.lookup.MethodHandleFactory; 3.42 +import jdk.nashorn.internal.lookup.MethodHandleFunctionality; 3.43 +import jdk.nashorn.internal.runtime.JSType; 3.44 + 3.45 +/** 3.46 + * A Dynalink linker to handle web browser built-in JS (DOM etc.) objects. 3.47 + */ 3.48 +final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { 3.49 + private static final ClassLoader myLoader = BrowserJSObjectLinker.class.getClassLoader(); 3.50 + private static final String JSOBJECT_CLASS = "netscape.javascript.JSObject"; 3.51 + // not final because this is lazily initialized 3.52 + // when we hit a subclass for the first time. 3.53 + private static volatile Class<?> jsObjectClass; 3.54 + 3.55 + @Override 3.56 + public boolean canLinkType(final Class<?> type) { 3.57 + return canLinkTypeStatic(type); 3.58 + } 3.59 + 3.60 + static boolean canLinkTypeStatic(final Class<?> type) { 3.61 + if (jsObjectClass != null && jsObjectClass.isAssignableFrom(type)) { 3.62 + return true; 3.63 + } 3.64 + 3.65 + // check if this class is a subclass of JSObject 3.66 + Class<?> clazz = type; 3.67 + while (clazz != null) { 3.68 + if (clazz.getClassLoader() == myLoader && 3.69 + clazz.getName().equals(JSOBJECT_CLASS)) { 3.70 + jsObjectClass = clazz; 3.71 + return true; 3.72 + } 3.73 + clazz = clazz.getSuperclass(); 3.74 + } 3.75 + 3.76 + return false; 3.77 + } 3.78 + 3.79 + private static void checkJSObjectClass() { 3.80 + assert jsObjectClass != null : JSOBJECT_CLASS + " not found!"; 3.81 + } 3.82 + 3.83 + @Override 3.84 + public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) throws Exception { 3.85 + final LinkRequest requestWithoutContext = request.withoutRuntimeContext(); // Nashorn has no runtime context 3.86 + final Object self = requestWithoutContext.getReceiver(); 3.87 + final CallSiteDescriptor desc = requestWithoutContext.getCallSiteDescriptor(); 3.88 + checkJSObjectClass(); 3.89 + 3.90 + if (desc.getNameTokenCount() < 2 || !"dyn".equals(desc.getNameToken(CallSiteDescriptor.SCHEME))) { 3.91 + // We only support standard "dyn:*[:*]" operations 3.92 + return null; 3.93 + } 3.94 + 3.95 + final GuardedInvocation inv; 3.96 + if (jsObjectClass.isInstance(self)) { 3.97 + inv = lookup(desc); 3.98 + } else { 3.99 + throw new AssertionError(); // Should never reach here. 3.100 + } 3.101 + 3.102 + return Bootstrap.asTypeSafeReturn(inv, linkerServices, desc); 3.103 + } 3.104 + 3.105 + private static GuardedInvocation lookup(final CallSiteDescriptor desc) { 3.106 + final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); 3.107 + final int c = desc.getNameTokenCount(); 3.108 + 3.109 + switch (operator) { 3.110 + case "getProp": 3.111 + case "getElem": 3.112 + case "getMethod": 3.113 + return c > 2 ? findGetMethod(desc) : findGetIndexMethod(); 3.114 + case "setProp": 3.115 + case "setElem": 3.116 + return c > 2 ? findSetMethod(desc) : findSetIndexMethod(); 3.117 + default: 3.118 + return null; 3.119 + } 3.120 + } 3.121 + 3.122 + private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) { 3.123 + final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 3.124 + final MethodHandle getter = MH.insertArguments(JSOBJECT_GETMEMBER, 1, name); 3.125 + return new GuardedInvocation(getter, IS_JSOBJECT_GUARD); 3.126 + } 3.127 + 3.128 + private static GuardedInvocation findGetIndexMethod() { 3.129 + return new GuardedInvocation(JSOBJECTLINKER_GET, IS_JSOBJECT_GUARD); 3.130 + } 3.131 + 3.132 + private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) { 3.133 + final MethodHandle getter = MH.insertArguments(JSOBJECT_SETMEMBER, 1, desc.getNameToken(2)); 3.134 + return new GuardedInvocation(getter, IS_JSOBJECT_GUARD); 3.135 + } 3.136 + 3.137 + private static GuardedInvocation findSetIndexMethod() { 3.138 + return new GuardedInvocation(JSOBJECTLINKER_PUT, IS_JSOBJECT_GUARD); 3.139 + } 3.140 + 3.141 + @SuppressWarnings("unused") 3.142 + private static boolean isJSObject(final Object self) { 3.143 + return jsObjectClass.isInstance(self); 3.144 + } 3.145 + 3.146 + @SuppressWarnings("unused") 3.147 + private static Object get(final Object jsobj, final Object key) throws Throwable { 3.148 + if (key instanceof Integer) { 3.149 + return JSOBJECT_GETSLOT.invokeExact(jsobj, (int)key); 3.150 + } else if (key instanceof Number) { 3.151 + final int index = getIndex((Number)key); 3.152 + if (index > -1) { 3.153 + return JSOBJECT_GETSLOT.invokeExact(jsobj, index); 3.154 + } 3.155 + } else if (key instanceof String) { 3.156 + return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key); 3.157 + } 3.158 + return null; 3.159 + } 3.160 + 3.161 + @SuppressWarnings("unused") 3.162 + private static void put(final Object jsobj, final Object key, final Object value) throws Throwable { 3.163 + if (key instanceof Integer) { 3.164 + JSOBJECT_SETSLOT.invokeExact(jsobj, (int)key, value); 3.165 + } else if (key instanceof Number) { 3.166 + JSOBJECT_SETSLOT.invokeExact(jsobj, getIndex((Number)key), value); 3.167 + } else if (key instanceof String) { 3.168 + JSOBJECT_SETMEMBER.invokeExact(jsobj, (String)key, value); 3.169 + } 3.170 + } 3.171 + 3.172 + private static int getIndex(final Number n) { 3.173 + final double value = n.doubleValue(); 3.174 + return JSType.isRepresentableAsInt(value) ? (int)value : -1; 3.175 + } 3.176 + 3.177 + private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality(); 3.178 + // method handles of the current class 3.179 + private static final MethodHandle IS_JSOBJECT_GUARD = findOwnMH_S("isJSObject", boolean.class, Object.class); 3.180 + private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH_S("get", Object.class, Object.class, Object.class); 3.181 + private static final MethodHandle JSOBJECTLINKER_PUT = findOwnMH_S("put", Void.TYPE, Object.class, Object.class, Object.class); 3.182 + 3.183 + private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 3.184 + return MH.findStatic(MethodHandles.lookup(), BrowserJSObjectLinker.class, name, MH.type(rtype, types)); 3.185 + } 3.186 + 3.187 + // method handles of netscape.javascript.JSObject class 3.188 + // These are in separate class as we lazily initialize these 3.189 + // method handles when we hit a subclass of JSObject first time. 3.190 + static class JSObjectHandles { 3.191 + // method handles of JSObject class 3.192 + static final MethodHandle JSOBJECT_GETMEMBER = findJSObjectMH_V("getMember", Object.class, String.class).asType(MH.type(Object.class, Object.class, String.class)); 3.193 + static final MethodHandle JSOBJECT_GETSLOT = findJSObjectMH_V("getSlot", Object.class, int.class).asType(MH.type(Object.class, Object.class, int.class)); 3.194 + static final MethodHandle JSOBJECT_SETMEMBER = findJSObjectMH_V("setMember", Void.TYPE, String.class, Object.class).asType(MH.type(Void.TYPE, Object.class, String.class, Object.class)); 3.195 + static final MethodHandle JSOBJECT_SETSLOT = findJSObjectMH_V("setSlot", Void.TYPE, int.class, Object.class).asType(MH.type(Void.TYPE, Object.class, int.class, Object.class)); 3.196 + 3.197 + private static MethodHandle findJSObjectMH_V(final String name, final Class<?> rtype, final Class<?>... types) { 3.198 + checkJSObjectClass(); 3.199 + return MH.findVirtual(MethodHandles.publicLookup(), jsObjectClass, name, MH.type(rtype, types)); 3.200 + } 3.201 + } 3.202 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/script/basic/JDK-8055762.js Thu Aug 21 20:06:48 2014 +0530 4.3 @@ -0,0 +1,62 @@ 4.4 +/* 4.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + */ 4.26 + 4.27 +/** 4.28 + * JDK-8055762: Nashorn misses linker for netscape.javascript.JSObject instances 4.29 + * @test 4.30 + * @run 4.31 + */ 4.32 + 4.33 +// basic checks for special linkage for netscape.javascript.JSObject 4.34 +// instances. For this test, we just subclass that class rather than 4.35 +// involve actual browser script engine or javafx webkit objects. 4.36 + 4.37 +var JSObject = Java.type("netscape.javascript.JSObject"); 4.38 +var obj = new (Java.extend(JSObject))() { 4.39 + getMember: function(name) { 4.40 + if (name == "func") { 4.41 + return function(arg) { 4.42 + print("func called with " + arg); 4.43 + } 4.44 + } 4.45 + return name.toUpperCase(); 4.46 + }, 4.47 + 4.48 + getSlot: function(index) { 4.49 + return index^2; 4.50 + }, 4.51 + 4.52 + setMember: function(name, value) { 4.53 + print(name + " set to " + value); 4.54 + }, 4.55 + 4.56 + setSlot: function(index, value) { 4.57 + print("[" + index + "] set to " + value); 4.58 + } 4.59 +}; 4.60 + 4.61 +print(obj["foo"]); 4.62 +print(obj[2]); 4.63 +obj.bar = 23; 4.64 +obj[3] = 23; 4.65 +obj.func("hello");
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/script/basic/JDK-8055762.js.EXPECTED Thu Aug 21 20:06:48 2014 +0530 5.3 @@ -0,0 +1,5 @@ 5.4 +FOO 5.5 +0 5.6 +bar set to 23 5.7 +[3] set to 23 5.8 +func called with hello