8147008: Nashorn primitive linker should handle ES6 symbols

Reviewed-by: attila, sundar
This commit is contained in:
Hannes Wallnöfer 2016-01-13 19:34:13 +01:00
parent dd9c62130f
commit bfa98d042c
5 changed files with 70 additions and 16 deletions

View File

@ -1133,6 +1133,8 @@ public final class Global extends Scope {
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);
}

View File

@ -168,9 +168,9 @@ public final class NativeBoolean extends ScriptObject {
}
/**
* 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")

View File

@ -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.ScriptObject;
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 @@ public final class NativeSymbol extends ScriptObject {
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 @@ public final class NativeSymbol extends ScriptObject {
}
}
/**
* 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 @@ public final class NativeSymbol extends ScriptObject {
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);
}
}

View File

@ -41,6 +41,7 @@ import jdk.nashorn.internal.objects.Global;
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 @@ final class NashornPrimitiveLinker implements TypeBasedGuardingDynamicLinker, Gu
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 @@ final class NashornPrimitiveLinker implements TypeBasedGuardingDynamicLinker, Gu
@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);

View File

@ -40,11 +40,11 @@ Assert.assertTrue(Symbol([1, 2, 3]).toString() === 'Symbol(1,2,3)');
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['bar'] === 'bar');
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 @@ try {
// 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(Symbol.keyFor(cached) === 'foo');
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(typeof o === 'object');
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