8055762: Nashorn misses linker for netscape.javascript.JSObject instances

Thu, 21 Aug 2014 20:06:48 +0530

author
sundar
date
Thu, 21 Aug 2014 20:06:48 +0530
changeset 966
620bf937f377
parent 965
83429983b9ef
child 967
934689dc9f10

8055762: Nashorn misses linker for netscape.javascript.JSObject instances
Reviewed-by: lagergren, jlaskey

make/build.xml file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/Bootstrap.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java file | annotate | diff | comparison | revisions
test/script/basic/JDK-8055762.js file | annotate | diff | comparison | revisions
test/script/basic/JDK-8055762.js.EXPECTED file | annotate | diff | comparison | revisions
     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=&quot;${toString:nashorn.ext.path}&quot;"/>
    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

mercurial