8147008: Nashorn primitive linker should handle ES6 symbols
authorhannesw
Wed, 13 Jan 2016 19:34:13 +0100
changeset 34975 d839258df47a
parent 34974 94a13629c390
child 34976 62f7766a44dc
8147008: Nashorn primitive linker should handle ES6 symbols Reviewed-by: attila, sundar
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeBoolean.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeSymbol.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java
nashorn/test/script/basic/es6/symbols.js
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Tue Jan 12 16:30:10 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Wed Jan 13 19:34:13 2016 +0100
@@ -1133,6 +1133,8 @@
             return NativeNumber.lookupPrimitive(request, self);
         } else if (self instanceof Boolean) {
             return NativeBoolean.lookupPrimitive(request, self);
+        } else if (self instanceof Symbol) {
+            return NativeSymbol.lookupPrimitive(request, self);
         }
         throw new IllegalArgumentException("Unsupported primitive: " + self);
     }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeBoolean.java	Tue Jan 12 16:30:10 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeBoolean.java	Wed Jan 13 19:34:13 2016 +0100
@@ -168,9 +168,9 @@
     }
 
     /**
-     * Wrap a native string in a NativeString object.
+     * Wrap a native boolean in a NativeBoolean object.
      *
-     * @param receiver Native string.
+     * @param receiver Native boolean.
      * @return Wrapped object.
      */
     @SuppressWarnings("unused")
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeSymbol.java	Tue Jan 12 16:30:10 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeSymbol.java	Wed Jan 13 19:34:13 2016 +0100
@@ -25,8 +25,14 @@
 
 package jdk.nashorn.internal.objects;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import jdk.dynalink.linker.GuardedInvocation;
+import jdk.dynalink.linker.LinkRequest;
 import jdk.nashorn.internal.WeakValueCache;
 import jdk.nashorn.internal.objects.annotations.Attribute;
 import jdk.nashorn.internal.objects.annotations.Constructor;
@@ -39,6 +45,7 @@
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Symbol;
 import jdk.nashorn.internal.runtime.Undefined;
+import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
 
 /**
  * ECMAScript 6 - 19.4 Symbol Objects
@@ -48,12 +55,21 @@
 
     private final Symbol symbol;
 
+    /** Method handle to create an object wrapper for a primitive symbol. */
+    static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeSymbol.class, Object.class));
+    /** Method handle to retrieve the Symbol prototype object. */
+    private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
+
     // initialized by nasgen
     private static PropertyMap $nasgenmap$;
 
     /** See ES6 19.4.2.1 */
     private static WeakValueCache<String, Symbol> globalSymbolRegistry = new WeakValueCache<>();
 
+    NativeSymbol(final Symbol symbol) {
+        this(symbol, Global.instance());
+    }
+
     NativeSymbol(final Symbol symbol, final Global global) {
         this(symbol, global.getSymbolPrototype(), $nasgenmap$);
     }
@@ -73,6 +89,17 @@
         }
     }
 
+    /**
+     * Lookup the appropriate method for an invoke dynamic call.
+     *
+     * @param request  The link request
+     * @param receiver The receiver for the call
+     * @return Link to be invoked at call site.
+     */
+    public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) {
+        return PrimitiveLookup.lookupPrimitive(request, Symbol.class, new NativeSymbol((Symbol)receiver), WRAPFILTER, PROTOFILTER);
+    }
+
     // ECMA 6 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
     @Override
     public Object getDefaultValue(final Class<?> typeHint) {
@@ -149,4 +176,19 @@
         final String name = ((Symbol) arg).getName();
         return globalSymbolRegistry.get(name) == arg ? name : Undefined.getUndefined();
     }
+
+    @SuppressWarnings("unused")
+    private static NativeSymbol wrapFilter(final Object receiver) {
+        return new NativeSymbol((Symbol)receiver);
+    }
+
+    @SuppressWarnings("unused")
+    private static Object protoFilter(final Object object) {
+        return Global.instance().getSymbolPrototype();
+    }
+
+    private static MethodHandle findOwnMH(final String name, final MethodType type) {
+        return MH.findStatic(MethodHandles.lookup(), NativeSymbol.class, name, type);
+    }
+
 }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java	Tue Jan 12 16:30:10 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java	Wed Jan 13 19:34:13 2016 +0100
@@ -41,6 +41,7 @@
 import jdk.nashorn.internal.runtime.ConsString;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.Symbol;
 
 /**
  * Internal linker for String, Boolean, and Number objects, only ever used by Nashorn engine and not exposed to other
@@ -58,7 +59,8 @@
 
     private static boolean canLinkTypeStatic(final Class<?> type) {
         return type == String.class || type == Boolean.class || type == ConsString.class || type == Integer.class
-                || type == Double.class || type == Float.class || type == Short.class || type == Byte.class;
+                || type == Double.class || type == Float.class || type == Short.class || type == Byte.class
+                || type == Symbol.class;
     }
 
     @Override
@@ -168,7 +170,7 @@
 
     @SuppressWarnings("unused")
     private static boolean isJavaScriptPrimitive(final Object o) {
-        return JSType.isString(o) || o instanceof Boolean || JSType.isNumber(o) || o == null;
+        return JSType.isString(o) || o instanceof Boolean || JSType.isNumber(o) || o == null || o instanceof Symbol;
     }
 
     private static final MethodHandle GUARD_PRIMITIVE = findOwnMH("isJavaScriptPrimitive", boolean.class, Object.class);
--- a/nashorn/test/script/basic/es6/symbols.js	Tue Jan 12 16:30:10 2016 +0100
+++ b/nashorn/test/script/basic/es6/symbols.js	Wed Jan 13 19:34:13 2016 +0100
@@ -40,11 +40,11 @@
 Assert.assertTrue(Symbol(null).toString() === 'Symbol(null)');
 Assert.assertTrue(Symbol(undefined).toString() === 'Symbol()');
 
-var s1 = Symbol();
-var s2 = Symbol("s2");
+const s1 = Symbol();
+const s2 = Symbol("s2");
 Assert.assertFalse(s1 instanceof Symbol); // not an object
 
-var obj = {};
+let obj = {};
 obj['foo'] = 'foo';
 obj[s1] = s1;
 obj['bar'] = 'bar';
@@ -57,17 +57,17 @@
 Assert.assertTrue(obj[1] === 1);
 Assert.assertTrue(obj[s2] === s2);
 
-var expectedNames = ['1', 'foo', 'bar'];
-var expectedSymbols = [s1, s2];
-var actualNames = Object.getOwnPropertyNames(obj);
-var actualSymbols = Object.getOwnPropertySymbols(obj);
+const expectedNames = ['1', 'foo', 'bar'];
+const expectedSymbols = [s1, s2];
+const actualNames = Object.getOwnPropertyNames(obj);
+let actualSymbols = Object.getOwnPropertySymbols(obj);
 Assert.assertTrue(expectedNames.length == actualNames.length);
 Assert.assertTrue(expectedSymbols.length == actualSymbols.length);
 
-for (var key in expectedNames) {
+for (let key in expectedNames) {
     Assert.assertTrue(expectedNames[key] === actualNames[key]);
 }
-for (var key in expectedSymbols) {
+for (let key in expectedSymbols) {
     Assert.assertTrue(expectedSymbols[key] === actualSymbols[key]);
 }
 
@@ -114,8 +114,8 @@
 
 // Symbol.for and Symbol.keyFor
 
-var uncached = Symbol('foo');
-var cached = Symbol.for('foo');
+const uncached = Symbol('foo');
+const cached = Symbol.for('foo');
 
 Assert.assertTrue(uncached !== cached);
 Assert.assertTrue(Symbol.keyFor(uncached) === undefined);
@@ -123,9 +123,15 @@
 Assert.assertTrue(cached === Symbol.for('foo'));
 Assert.assertTrue(cached === Symbol.for('f' + 'oo'));
 
+// JDK-8147008: Make sure symbols are handled by primitive linker
+Symbol.prototype.foo = 123;
+Symbol.prototype[s2] = s2;
+Assert.assertEquals(s1.foo, 123);
+Assert.assertEquals(s2[s2], s2);
+
 // Object wrapper
 
-var o = Object(s1);
+const o = Object(s1);
 obj = {};
 obj[s1] = "s1";
 Assert.assertTrue(o == s1);
@@ -134,6 +140,8 @@
 Assert.assertTrue(o instanceof Symbol);
 Assert.assertTrue(obj[o] == 's1');
 Assert.assertTrue(o in obj);
+Assert.assertEquals(o.foo, 123);
+Assert.assertEquals(o[s2], s2);
 
 // various non-strict comparisons that should fail