8141702: Add support for Symbol property keys

Reviewed-by: attila, sundar
This commit is contained in:
Hannes Wallnöfer 2015-11-11 16:28:17 +01:00
parent 15ef19ee62
commit 13dbf6a119
31 changed files with 852 additions and 236 deletions

View File

@ -165,7 +165,7 @@ public final class Main extends Shell {
try {
final Object res = context.eval(global, source, global, "<shell>");
if (res != ScriptRuntime.UNDEFINED) {
err.println(JSType.toString(res));
err.println(toString(res, global));
}
} catch (final Exception exp) {
// Is this a ECMAScript SyntaxError at last column (of the single line)?

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2015, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.function.Function;
/**
* This class provides a map based cache with weakly referenced values. Cleared references are
* purged from the underlying map when values are retrieved or created.
* It uses a {@link java.util.HashMap} to store values and needs to be externally synchronized.
*
* @param <K> the key type
* @param <V> the value type
*/
public final class WeakValueCache<K, V> {
private final HashMap<K, KeyValueReference<K, V>> map = new HashMap<>();
private final ReferenceQueue<V> refQueue = new ReferenceQueue<>();
/**
* Returns the value associated with {@code key}, or {@code null} if no such value exists.
*
* @param key the key
* @return the value or null if none exists
*/
public V get(final K key) {
removeClearedEntries();
return findValue(key);
}
/**
* Returns the value associated with {@code key}, or creates and returns a new value if
* no value exists using the {@code creator} function.
*
* @param key the key
* @param creator function to create a new value
* @return the existing value, or a new one if none existed
*/
public V getOrCreate(final K key, final Function<? super K, ? extends V> creator) {
removeClearedEntries();
V value = findValue(key);
if (value == null) {
// Define a new value if it does not exist
value = creator.apply(key);
map.put(key, new KeyValueReference<>(key, value));
}
return value;
}
private V findValue(final K key) {
final KeyValueReference<K, V> ref = map.get(key);
if (ref != null) {
return ref.get();
}
return null;
}
private void removeClearedEntries() {
// Remove cleared entries
for (;;) {
final KeyValueReference ref = (KeyValueReference) refQueue.poll();
if (ref == null) {
break;
}
map.remove(ref.key, ref);
}
}
private static class KeyValueReference<K, V> extends WeakReference<V> {
final K key;
KeyValueReference(final K key, final V value) {
super(value);
this.key = key;
}
}
}

View File

@ -738,7 +738,7 @@ public class MethodEmitter {
* @param recovery start label for catch
*/
void _try(final Label entry, final Label exit, final Label recovery) {
_try(entry, exit, recovery, (String)null, false);
_try(entry, exit, recovery, null, false);
}
void markLabelAsOptimisticCatchHandler(final Label label, final int liveLocalCount) {

View File

@ -173,9 +173,10 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
loadTuple(method, tuple);
method.dynamicSetIndex(callSiteFlags);
} else {
assert property.getKey() instanceof String; // symbol keys not yet supported in object literals
method.dup();
loadTuple(method, tuple);
method.dynamicSet(property.getKey(), codegen.getCallSiteFlags(), false);
method.dynamicSet((String) property.getKey(), codegen.getCallSiteFlags(), false);
}
}
}

View File

@ -75,6 +75,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
import jdk.nashorn.internal.runtime.Specialization;
import jdk.nashorn.internal.runtime.Symbol;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
@ -221,6 +222,10 @@ public final class Global extends Scope {
@Property(name = "Number", attributes = Attribute.NOT_ENUMERABLE)
public volatile Object number;
/** ECMA 2016 19.4.1 - Symbol constructor */
@Property(name = "Symbol", attributes = Attribute.NOT_ENUMERABLE)
public volatile Object symbol;
/**
* Getter for ECMA 15.1.4.7 Date property
*
@ -901,6 +906,7 @@ public final class Global extends Scope {
private ScriptFunction builtinUint32Array;
private ScriptFunction builtinFloat32Array;
private ScriptFunction builtinFloat64Array;
private ScriptFunction builtinSymbol;
/*
* ECMA section 13.2.3 The [[ThrowTypeError]] Function Object
@ -1106,6 +1112,8 @@ public final class Global extends Scope {
return new NativeArray(ArrayData.allocate((int[]) obj), this);
} else if (obj instanceof ArrayData) {
return new NativeArray((ArrayData) obj, this);
} else if (obj instanceof Symbol) {
return new NativeSymbol((Symbol) obj, this);
} else {
// FIXME: more special cases? Map? List?
return obj;
@ -1586,7 +1594,7 @@ public final class Global extends Scope {
/**
* Get the builtin Object prototype.
* @return the object prototype.
* @return the Object prototype.
*/
public ScriptObject getObjectPrototype() {
return ScriptFunction.getPrototype(builtinObject);
@ -1594,13 +1602,17 @@ public final class Global extends Scope {
/**
* Get the builtin Function prototype.
* @return the Function.prototype.
* @return the Function prototype.
*/
public ScriptObject getFunctionPrototype() {
return ScriptFunction.getPrototype(builtinFunction);
}
ScriptObject getArrayPrototype() {
/**
* Get the builtin Array prototype.
* @return the Array prototype
*/
public ScriptObject getArrayPrototype() {
return ScriptFunction.getPrototype(builtinArray);
}
@ -1660,6 +1672,10 @@ public final class Global extends Scope {
return ScriptFunction.getPrototype(getBuiltinJSAdapter());
}
ScriptObject getSymbolPrototype() {
return ScriptFunction.getPrototype(builtinSymbol);
}
private synchronized ScriptFunction getBuiltinArrayBuffer() {
if (this.builtinArrayBuffer == null) {
this.builtinArrayBuffer = initConstructorAndSwitchPoint("ArrayBuffer", ScriptFunction.class);
@ -2127,11 +2143,11 @@ public final class Global extends Scope {
// ES6 15.1.8 steps 6. and 7.
final jdk.nashorn.internal.runtime.Property globalProperty = ownMap.findProperty(property.getKey());
if (globalProperty != null && !globalProperty.isConfigurable() && property.isLexicalBinding()) {
throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
throw ECMAErrors.syntaxError("redeclare.variable", property.getKey().toString());
}
final jdk.nashorn.internal.runtime.Property lexicalProperty = lexicalMap.findProperty(property.getKey());
if (lexicalProperty != null && !property.isConfigurable()) {
throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
throw ECMAErrors.syntaxError("redeclare.variable", property.getKey().toString());
}
}
}
@ -2186,7 +2202,7 @@ public final class Global extends Scope {
}
@Override
protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) {
protected FindProperty findProperty(final Object key, final boolean deep, final ScriptObject start) {
if (lexicalScope != null && start != this && start.isScope()) {
final FindProperty find = lexicalScope.findProperty(key, false);
if (find != null) {
@ -2306,6 +2322,14 @@ public final class Global extends Scope {
this.builtinString = initConstructorAndSwitchPoint("String", ScriptFunction.class);
this.builtinMath = initConstructorAndSwitchPoint("Math", ScriptObject.class);
if (env._es6) {
this.builtinSymbol = initConstructorAndSwitchPoint("Symbol", ScriptFunction.class);
} else {
// We need to manually delete nasgen-generated properties we don't want
this.delete("Symbol", false);
this.builtinObject.delete("getOwnPropertySymbols", false);
}
// initialize String.prototype.length to 0
// add String.prototype.length
final ScriptObject stringPrototype = getStringPrototype();
@ -2514,6 +2538,7 @@ public final class Global extends Scope {
this.string = this.builtinString;
this.syntaxError = this.builtinSyntaxError;
this.typeError = this.builtinTypeError;
this.symbol = this.builtinSymbol;
}
private void initDebug() {

View File

@ -151,7 +151,7 @@ public final class NativeArguments extends ScriptObject {
* ECMA 10.6 for Arguments object.
*/
@Override
public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) {
final int index = ArrayIndex.getArrayIndex(key);
if (index >= 0) {
final boolean isMapped = isMapped(index);
@ -159,7 +159,7 @@ public final class NativeArguments extends ScriptObject {
if (!super.defineOwnProperty(key, propertyDesc, false)) {
if (reject) {
throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
}
return false;
}

View File

@ -328,7 +328,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
* ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw )
*/
@Override
public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) {
final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc);
// never be undefined as "length" is always defined and can't be deleted for arrays
@ -369,7 +369,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
// Step 4d
if (!succeeded) {
if (reject) {
throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
}
return false;
}

View File

@ -135,8 +135,11 @@ public final class NativeJavaImporter extends ScriptObject {
}
@Override
protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
final Object retval = createProperty(name);
protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
if (!(key instanceof String)) {
return super.invokeNoSuchProperty(key, isScope, programPoint);
}
final Object retval = createProperty((String) key);
if (isValid(programPoint)) {
throw new UnwarrantedOptimismException(retval, programPoint);
}

View File

@ -251,6 +251,23 @@ public final class NativeObject {
}
}
/**
* ECMA 2 19.1.2.8 Object.getOwnPropertySymbols ( O )
*
* @param self self reference
* @param obj object to query for property names
* @return array of property names
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static ScriptObject getOwnPropertySymbols(final Object self, final Object obj) {
if (obj instanceof ScriptObject) {
return new NativeArray(((ScriptObject)obj).getOwnSymbols(true));
} else {
// TODO: we don't support this on ScriptObjectMirror objects yet
throw notAnObject(obj);
}
}
/**
* ECMA 15.2.3.5 Object.create ( O [, Properties] )
*
@ -288,7 +305,7 @@ public final class NativeObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static ScriptObject defineProperty(final Object self, final Object obj, final Object prop, final Object attr) {
final ScriptObject sobj = Global.checkObject(obj);
sobj.defineOwnProperty(JSType.toString(prop), attr, true);
sobj.defineOwnProperty(JSType.toPropertyKey(prop), attr, true);
return sobj;
}
@ -465,6 +482,7 @@ public final class NativeObject {
case BOOLEAN:
case NUMBER:
case STRING:
case SYMBOL:
return Global.toObject(value);
case OBJECT:
return value;

View File

@ -33,6 +33,7 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
@ -106,20 +107,6 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
return getStringValue();
}
@Override
public boolean equals(final Object other) {
if (other instanceof NativeString) {
return getStringValue().equals(((NativeString) other).getStringValue());
}
return false;
}
@Override
public int hashCode() {
return getStringValue().hashCode();
}
private String getStringValue() {
return value instanceof String ? (String) value : value.toString();
}
@ -382,7 +369,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
}
@Override
public Object getOwnPropertyDescriptor(final String key) {
public Object getOwnPropertyDescriptor(final Object key) {
final int index = ArrayIndex.getArrayIndex(key);
if (index >= 0 && index < value.length()) {
final Global global = Global.instance();
@ -400,7 +387,12 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
* @return Array of keys.
*/
@Override
protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) {
@SuppressWarnings("unchecked")
protected <T> T[] getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable) {
if (type != String.class) {
return super.getOwnKeys(type, all, nonEnumerable);
}
final List<Object> keys = new ArrayList<>();
// add string index keys
@ -409,8 +401,8 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
}
// add super class properties
keys.addAll(Arrays.asList(super.getOwnKeys(all, nonEnumerable)));
return keys.toArray(new String[keys.size()]);
keys.addAll(Arrays.asList(super.getOwnKeys(type, all, nonEnumerable)));
return keys.toArray((T[]) Array.newInstance(type, keys.size()));
}
/**

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2015, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import jdk.nashorn.internal.WeakValueCache;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Symbol;
import jdk.nashorn.internal.runtime.Undefined;
/**
* ECMAScript 6 - 19.4 Symbol Objects
*/
@ScriptClass("Symbol")
public final class NativeSymbol extends ScriptObject {
private final Symbol symbol;
// 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, final Global global) {
this(symbol, global.getSymbolPrototype(), $nasgenmap$);
}
private NativeSymbol(final Symbol symbol, final ScriptObject prototype, final PropertyMap map) {
super(prototype, map);
this.symbol = symbol;
}
private static Symbol getSymbolValue(final Object self) {
if (self instanceof Symbol) {
return (Symbol) self;
} else if (self instanceof NativeSymbol) {
return ((NativeSymbol) self).symbol;
} else {
throw typeError("not.a.symbol");
}
}
// ECMA 6 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
@Override
public Object getDefaultValue(final Class<?> typeHint) {
// Just return the symbol value.
return symbol;
}
/**
* ECMA 6 19.4.3.2 Symbol.prototype.toString ( )
*
* @param self self reference
* @return localized string for this Number
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static String toString(final Object self) {
return getSymbolValue(self).toString();
}
/**
* ECMA 6 19.4.3.3 Symbol.prototype.valueOf ( )
*
* @param self self reference
* @return number value for this Number
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object valueOf(final Object self) {
return getSymbolValue(self);
}
/**
* ECMA 6 19.4.1.1 Symbol ( [ description ] )
*
* @param newObj is this function invoked with the new operator
* @param self self reference
* @param args arguments
* @return new symbol value
*/
@Constructor(arity = 1)
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
if (newObj) {
throw typeError("symbol.as.constructor");
}
final String description = args.length > 0 && args[0] != Undefined.getUndefined() ?
JSType.toString(args[0]) : "";
return new Symbol(description);
}
/**
* ES6 19.4.2.1 Symbol.for ( key )
*
* @param self self reference
* @param arg the argument
* @return the symbol value
*/
@Function(name = "for", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public synchronized static Object _for(final Object self, final Object arg) {
final String name = JSType.toString(arg);
return globalSymbolRegistry.getOrCreate(name, Symbol::new);
}
/**
* ES6 19.4.2.5 Symbol.keyFor ( sym )
*
* @param self self reference
* @param arg the argument
* @return the symbol name
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public synchronized static Object keyFor(final Object self, final Object arg) {
if (!(arg instanceof Symbol)) {
throw typeError("not.a.symbol", ScriptRuntime.safeToString(arg));
}
final String name = ((Symbol) arg).getName();
return globalSymbolRegistry.get(name) == arg ? name : Undefined.getUndefined();
}
}

View File

@ -180,7 +180,7 @@ public class AccessorProperty extends Property {
* @param objectSetter object setter
*/
protected AccessorProperty(
final String key,
final Object key,
final int flags,
final int slot,
final MethodHandle primitiveGetter,
@ -209,7 +209,7 @@ public class AccessorProperty extends Property {
* @param getter the property getter
* @param setter the property setter or null if non writable, non configurable
*/
private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
private AccessorProperty(final Object key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
super(key, flags | IS_BUILTIN | DUAL_FIELDS | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
assert !isSpill();
@ -249,7 +249,7 @@ public class AccessorProperty extends Property {
* @param structure structure for objects associated with this property
* @param slot property field number or spill slot
*/
public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot) {
public AccessorProperty(final Object key, final int flags, final Class<?> structure, final int slot) {
super(key, flags, slot);
initGetterSetter(structure);
@ -292,7 +292,7 @@ public class AccessorProperty extends Property {
* @param owner owner of property
* @param initialValue initial value to which the property can be set
*/
protected AccessorProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
protected AccessorProperty(final Object key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
this(key, flags, owner.getClass(), slot);
setInitialValue(owner, initialValue);
}
@ -307,7 +307,7 @@ public class AccessorProperty extends Property {
* @param slot field slot index
* @param initialType initial type of the property
*/
public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
public AccessorProperty(final Object key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
this(key, flags, structure, slot);
setType(hasDualFields() ? initialType : Object.class);
}
@ -603,7 +603,7 @@ public class AccessorProperty extends Property {
private void checkUndeclared() {
if ((getFlags() & NEEDS_DECLARATION) != 0) {
// a lexically defined variable that hasn't seen its declaration - throw ReferenceError
throw ECMAErrors.referenceError("not.defined", getKey());
throw ECMAErrors.referenceError("not.defined", getKey().toString());
}
}
@ -659,7 +659,7 @@ public class AccessorProperty extends Property {
}
if (isBuiltin()) {
mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey()));
mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey().toString()));
}
assert mh.type().returnType() == void.class : mh.type();

View File

@ -42,10 +42,8 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
@ -80,6 +78,7 @@ import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.WeakValueCache;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
@ -303,47 +302,7 @@ public final class Context {
}
}
private final Map<CodeSource, HostClassReference> anonymousHostClasses = new HashMap<>();
private final ReferenceQueue<Class<?>> anonymousHostClassesRefQueue = new ReferenceQueue<>();
private static class HostClassReference extends WeakReference<Class<?>> {
final CodeSource codeSource;
HostClassReference(final CodeSource codeSource, final Class<?> clazz, final ReferenceQueue<Class<?>> refQueue) {
super(clazz, refQueue);
this.codeSource = codeSource;
}
}
private synchronized Class<?> getAnonymousHostClass(final CodeSource codeSource) {
// Remove cleared entries
for(;;) {
final HostClassReference clearedRef = (HostClassReference)anonymousHostClassesRefQueue.poll();
if (clearedRef == null) {
break;
}
anonymousHostClasses.remove(clearedRef.codeSource, clearedRef);
}
// Try to find an existing host class
final Reference<Class<?>> ref = anonymousHostClasses.get(codeSource);
if (ref != null) {
final Class<?> existingHostClass = ref.get();
if (existingHostClass != null) {
return existingHostClass;
}
}
// Define a new host class if existing is not found
final Class<?> newHostClass = createNewLoader().installClass(
// NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not
// initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever
// invoked from AnonymousContextCodeInstaller, this is okay.
AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME,
AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, codeSource);
anonymousHostClasses.put(codeSource, new HostClassReference(codeSource, newHostClass, anonymousHostClassesRefQueue));
return newHostClass;
}
private final WeakValueCache<CodeSource, Class<?>> anonymousHostClasses = new WeakValueCache<>();
private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller {
private static final Unsafe UNSAFE = getUnsafe();
@ -1455,7 +1414,14 @@ public final class Context {
final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
installer = new NamedContextCodeInstaller(this, cs, loader);
} else {
installer = new AnonymousContextCodeInstaller(this, cs, getAnonymousHostClass(cs));
installer = new AnonymousContextCodeInstaller(this, cs,
anonymousHostClasses.getOrCreate(cs, (key) ->
createNewLoader().installClass(
// NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not
// initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever
// invoked from AnonymousContextCodeInstaller, this is okay.
AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME,
AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, cs)));
}
if (storedScript == null) {

View File

@ -105,7 +105,7 @@ public final class GlobalConstants implements Loggable {
* Access map for this global - associates a symbol name with an Access object, with getter
* and invalidation information
*/
private final Map<String, Access> map = new HashMap<>();
private final Map<Object, Access> map = new HashMap<>();
private final AtomicBoolean invalidatedForever = new AtomicBoolean(false);
@ -301,7 +301,7 @@ public final class GlobalConstants implements Loggable {
* that might be linked as MethodHandle.constant and force relink
* @param name name of property
*/
void delete(final String name) {
void delete(final Object name) {
if (!invalidatedForever.get()) {
synchronized (this) {
final Access acc = map.get(name);

View File

@ -40,6 +40,7 @@ import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.objects.NativeSymbol;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
@ -68,7 +69,10 @@ public enum JSType {
OBJECT("object"),
/** The function type */
FUNCTION("function");
FUNCTION("function"),
/** The symbol type */
SYMBOL("symbol");
/** The type name as returned by ECMAScript "typeof" operator*/
private final String typeName;
@ -312,6 +316,10 @@ public enum JSType {
return JSType.NUMBER;
}
if (obj instanceof Symbol) {
return JSType.SYMBOL;
}
if (obj == ScriptRuntime.UNDEFINED) {
return JSType.UNDEFINED;
}
@ -354,6 +362,10 @@ public enum JSType {
return JSType.UNDEFINED;
}
if (obj instanceof Symbol) {
return JSType.SYMBOL;
}
return JSType.OBJECT;
}
@ -470,9 +482,10 @@ public enum JSType {
public static boolean isPrimitive(final Object obj) {
return obj == null ||
obj == ScriptRuntime.UNDEFINED ||
obj instanceof Boolean ||
isString(obj) ||
obj instanceof Number ||
isString(obj);
obj instanceof Boolean ||
obj instanceof Symbol;
}
/**
@ -613,6 +626,15 @@ public enum JSType {
return toStringImpl(obj, false);
}
/**
* See ES6 #7.1.14
* @param obj key object
* @return property key
*/
public static Object toPropertyKey(final Object obj) {
return obj instanceof Symbol ? obj : toStringImpl(obj, false);
}
/**
* If obj is an instance of {@link ConsString} cast to CharSequence, else return
* result of {@link #toString(Object)}.
@ -787,7 +809,9 @@ public enum JSType {
* @return a number
*/
public static double toNumberForEq(final Object obj) {
return obj == null ? Double.NaN : toNumber(obj);
// we are not able to detect Symbol objects from codegen, so we need to
// handle them here to avoid throwing an error in toNumber conversion.
return obj == null || obj instanceof Symbol || obj instanceof NativeSymbol ? Double.NaN : toNumber(obj);
}
/**
@ -1404,6 +1428,13 @@ public enum JSType {
return obj.toString();
}
if (obj instanceof Symbol) {
if (safe) {
return obj.toString();
}
throw typeError("symbol.to.string");
}
if (safe && obj instanceof ScriptObject) {
final ScriptObject sobj = (ScriptObject)obj;
final Global gobj = Context.getGlobal();
@ -1916,6 +1947,10 @@ public enum JSType {
return Double.NaN;
}
if (obj instanceof Symbol) {
throw typeError("symbol.to.number");
}
return toNumber(toPrimitive(obj, Number.class));
}

View File

@ -207,8 +207,11 @@ public final class NativeJavaPackage extends ScriptObject {
}
@Override
protected Object invokeNoSuchProperty(final String key, final boolean isScope, final int programPoint) {
final Object retval = createProperty(key);
protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
if (!(key instanceof String)) {
return super.invokeNoSuchProperty(key, isScope, programPoint);
}
final Object retval = createProperty((String) key);
if (isValid(programPoint)) {
throw new UnwarrantedOptimismException(retval, programPoint);
}

View File

@ -100,7 +100,7 @@ public abstract class Property implements Serializable {
public static final int DUAL_FIELDS = 1 << 11;
/** Property key. */
private final String key;
private final Object key;
/** Property flags. */
private int flags;
@ -127,7 +127,7 @@ public abstract class Property implements Serializable {
* @param flags property flags
* @param slot property field number or spill slot
*/
Property(final String key, final int flags, final int slot) {
Property(final Object key, final int flags, final int slot) {
assert key != null;
this.key = key;
this.flags = flags;
@ -420,7 +420,7 @@ public abstract class Property implements Serializable {
* Get the key for this property. This key is an ordinary string. The "name".
* @return key for property
*/
public String getKey() {
public Object getKey() {
return key;
}
@ -627,7 +627,7 @@ public abstract class Property implements Serializable {
final StringBuilder sb = new StringBuilder();
final Class<?> t = getLocalType();
sb.append(indent(getKey(), 20)).
sb.append(indent(getKey().toString(), 20)).
append(" id=").
append(Debug.id(this)).
append(" (0x").

View File

@ -102,7 +102,7 @@ import java.util.Set;
* immutable hash map, addition is constant time. For LinkedHashMap it's O(N+C)
* since we have to clone the older map.
*/
public final class PropertyHashMap implements Map <String, Property> {
public final class PropertyHashMap implements Map <Object, Property> {
/** Number of initial bins. Power of 2. */
private static final int INITIAL_BINS = 32;
@ -243,7 +243,7 @@ public final class PropertyHashMap implements Map <String, Property> {
*
* @return New {@link PropertyHashMap}.
*/
public PropertyHashMap immutableRemove(final String key) {
public PropertyHashMap immutableRemove(final Object key) {
if (bins != null) {
final int binIndex = binIndex(bins, key);
final Element bin = bins[binIndex];
@ -271,7 +271,7 @@ public final class PropertyHashMap implements Map <String, Property> {
*
* @return {@link Property} matching key or {@code null} if not found.
*/
public Property find(final String key) {
public Property find(final Object key) {
final Element element = findElement(key);
return element != null ? element.getProperty() : null;
}
@ -301,7 +301,7 @@ public final class PropertyHashMap implements Map <String, Property> {
*
* @return The bin index.
*/
private static int binIndex(final Element[] bins, final String key) {
private static int binIndex(final Element[] bins, final Object key) {
return key.hashCode() & bins.length - 1;
}
@ -340,7 +340,7 @@ public final class PropertyHashMap implements Map <String, Property> {
final Element[] newBins = new Element[binSize];
for (Element element = list; element != null; element = element.getLink()) {
final Property property = element.getProperty();
final String key = property.getKey();
final Object key = property.getKey();
final int binIndex = binIndex(newBins, key);
newBins[binIndex] = new Element(newBins[binIndex], property);
@ -355,7 +355,7 @@ public final class PropertyHashMap implements Map <String, Property> {
*
* @return {@link Element} matching key or {@code null} if not found.
*/
private Element findElement(final String key) {
private Element findElement(final Object key) {
if (bins != null) {
final int binIndex = binIndex(bins, key);
return findElement(bins[binIndex], key);
@ -370,7 +370,7 @@ public final class PropertyHashMap implements Map <String, Property> {
* @param key {@link Element} key.
* @return {@link Element} matching key or {@code null} if not found.
*/
private static Element findElement(final Element elementList, final String key) {
private static Element findElement(final Element elementList, final Object key) {
final int hashCode = key.hashCode();
for (Element element = elementList; element != null; element = element.getLink()) {
if (element.match(key, hashCode)) {
@ -416,7 +416,7 @@ public final class PropertyHashMap implements Map <String, Property> {
*/
private PropertyHashMap addNoClone(final Property property) {
int newSize = size;
final String key = property.getKey();
final Object key = property.getKey();
Element newList = list;
if (bins != null) {
final int binIndex = binIndex(bins, key);
@ -437,7 +437,7 @@ public final class PropertyHashMap implements Map <String, Property> {
return new PropertyHashMap(newSize, bins, newList);
}
private PropertyHashMap replaceNoClone(final String key, final Property property) {
private PropertyHashMap replaceNoClone(final Object key, final Property property) {
if (bins != null) {
final int binIndex = binIndex(bins, key);
Element bin = bins[binIndex];
@ -457,7 +457,7 @@ public final class PropertyHashMap implements Map <String, Property> {
*
* @return New list with {@link Element} removed.
*/
private static Element removeFromList(final Element list, final String key) {
private static Element removeFromList(final Element list, final Object key) {
if (list == null) {
return null;
}
@ -480,7 +480,7 @@ public final class PropertyHashMap implements Map <String, Property> {
}
// for element x. if x get link matches,
private static Element replaceInList(final Element list, final String key, final Property property) {
private static Element replaceInList(final Element list, final Object key, final Property property) {
assert list != null;
final int hashCode = key.hashCode();
@ -519,21 +519,7 @@ public final class PropertyHashMap implements Map <String, Property> {
@Override
public boolean containsKey(final Object key) {
if (key instanceof String) {
return findElement((String)key) != null;
}
assert key instanceof String;
return false;
}
/**
* Check if the map contains a key.
*
* @param key {@link Property} key.
*
* @return {@code true} of key is in {@link PropertyHashMap}.
*/
public boolean containsKey(final String key) {
assert key instanceof String || key instanceof Symbol;
return findElement(key) != null;
}
@ -549,29 +535,13 @@ public final class PropertyHashMap implements Map <String, Property> {
@Override
public Property get(final Object key) {
if (key instanceof String) {
final Element element = findElement((String)key);
return element != null ? element.getProperty() : null;
}
assert key instanceof String;
return null;
}
/**
* Get the {@link Property} given a key that is an explicit {@link String}.
* See also {@link PropertyHashMap#get(Object)}
*
* @param key {@link Property} key.
*
* @return {@link Property}, or {@code null} if no property with that key was found.
*/
public Property get(final String key) {
assert key instanceof String || key instanceof Symbol;
final Element element = findElement(key);
return element != null ? element.getProperty() : null;
}
@Override
public Property put(final String key, final Property value) {
public Property put(final Object key, final Property value) {
throw new UnsupportedOperationException("Immutable map.");
}
@ -581,7 +551,7 @@ public final class PropertyHashMap implements Map <String, Property> {
}
@Override
public void putAll(final Map<? extends String, ? extends Property> m) {
public void putAll(final Map<? extends Object, ? extends Property> m) {
throw new UnsupportedOperationException("Immutable map.");
}
@ -591,8 +561,8 @@ public final class PropertyHashMap implements Map <String, Property> {
}
@Override
public Set<String> keySet() {
final HashSet<String> set = new HashSet<>();
public Set<Object> keySet() {
final HashSet<Object> set = new HashSet<>();
for (Element element = list; element != null; element = element.getLink()) {
set.add(element.getKey());
}
@ -605,8 +575,8 @@ public final class PropertyHashMap implements Map <String, Property> {
}
@Override
public Set<Entry<String, Property>> entrySet() {
final HashSet<Entry<String, Property>> set = new HashSet<>();
public Set<Entry<Object, Property>> entrySet() {
final HashSet<Entry<Object, Property>> set = new HashSet<>();
for (Element element = list; element != null; element = element.getLink()) {
set.add(element);
}
@ -616,7 +586,7 @@ public final class PropertyHashMap implements Map <String, Property> {
/**
* List map element.
*/
static final class Element implements Entry<String, Property> {
static final class Element implements Entry<Object, Property> {
/** Link for list construction. */
private Element link;
@ -624,7 +594,7 @@ public final class PropertyHashMap implements Map <String, Property> {
private final Property property;
/** Element key. Kept separate for performance.) */
private final String key;
private final Object key;
/** Element key hash code. */
private final int hashCode;
@ -640,7 +610,7 @@ public final class PropertyHashMap implements Map <String, Property> {
this.hashCode = this.key.hashCode();
}
boolean match(final String otherKey, final int otherHashCode) {
boolean match(final Object otherKey, final int otherHashCode) {
return this.hashCode == otherHashCode && this.key.equals(otherKey);
}
@ -655,7 +625,7 @@ public final class PropertyHashMap implements Map <String, Property> {
}
@Override
public String getKey() {
public Object getKey() {
return key;
}

View File

@ -35,7 +35,7 @@ import java.util.concurrent.atomic.LongAdder;
*/
public class PropertyListeners {
private Map<String, WeakPropertyMapSet> listeners;
private Map<Object, WeakPropertyMapSet> listeners;
// These counters are updated in debug mode
private static LongAdder listenersAdded;

View File

@ -96,7 +96,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
private transient SharedPropertyMap sharedProtoMap;
/** {@link SwitchPoint}s for gets on inherited properties. */
private transient HashMap<String, SwitchPoint> protoSwitches;
private transient HashMap<Object, SwitchPoint> protoSwitches;
/** History of maps, used to limit map duplication. */
private transient WeakHashMap<Property, Reference<PropertyMap>> history;
@ -354,7 +354,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
*
* @param key {@link Property} key to invalidate.
*/
synchronized void invalidateProtoSwitchPoint(final String key) {
synchronized void invalidateProtoSwitchPoint(final Object key) {
if (protoSwitches != null) {
final SwitchPoint sp = protoSwitches.get(key);
if (sp != null) {
@ -496,7 +496,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
public final synchronized PropertyMap deleteProperty(final Property property) {
propertyDeleted(property, true);
PropertyMap newMap = checkHistory(property);
final String key = property.getKey();
final Object key = property.getKey();
if (newMap == null && properties.containsKey(key)) {
final PropertyHashMap newProperties = properties.immutableRemove(key);
@ -577,7 +577,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
* @param propertyFlags attribute flags of the property
* @return the newly created UserAccessorProperty
*/
public final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) {
public final UserAccessorProperty newUserAccessors(final Object key, final int propertyFlags) {
return new UserAccessorProperty(key, propertyFlags, getFreeSpillSlot());
}
@ -588,7 +588,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
*
* @return {@link Property} matching key.
*/
public final Property findProperty(final String key) {
public final Property findProperty(final Object key) {
return properties.find(key);
}

View File

@ -53,6 +53,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import java.lang.reflect.Array;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
@ -310,11 +311,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
*/
protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property, final boolean extensible) {
PropertyMap newMap = propMap;
final String key = property.getKey();
final Object key = property.getKey();
final Property oldProp = newMap.findProperty(key);
if (oldProp == null) {
if (! extensible) {
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
}
if (property instanceof UserAccessorProperty) {
@ -330,7 +331,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
if (oldProp instanceof UserAccessorProperty ||
!(oldProp.isWritable() && oldProp.isEnumerable())) {
throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
}
}
}
@ -348,11 +349,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final boolean extensible = newMap.isExtensible();
for (final AccessorProperty property : properties) {
final String key = property.getKey();
final Object key = property.getKey();
if (newMap.findProperty(key) == null) {
if (! extensible) {
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
}
newMap = newMap.addPropertyBind(property, source);
}
@ -456,7 +457,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @return Returns the Property Descriptor of the named own property of this
* object, or undefined if absent.
*/
public Object getOwnPropertyDescriptor(final String key) {
public Object getOwnPropertyDescriptor(final Object key) {
final Property property = getMap().findProperty(key);
final Global global = Context.getGlobal();
@ -518,7 +519,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* Invalidate any existing global constant method handles that may exist for {@code key}.
* @param key the property name
*/
protected void invalidateGlobalConstant(final String key) {
protected void invalidateGlobalConstant(final Object key) {
final GlobalConstants globalConstants = getGlobalConstants();
if (globalConstants != null) {
globalConstants.delete(key);
@ -534,11 +535,10 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
*
* @return true if property was successfully defined
*/
public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) {
final Global global = Context.getGlobal();
final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc);
final Object current = getOwnPropertyDescriptor(key);
final String name = JSType.toString(key);
invalidateGlobalConstant(key);
@ -550,7 +550,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
// new property added to non-extensible object
if (reject) {
throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
throw typeError(global, "object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
}
return false;
}
@ -573,7 +573,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
// not configurable can not be made configurable
if (reject) {
throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
}
return false;
}
@ -582,7 +582,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
currentDesc.isEnumerable() != newDesc.isEnumerable()) {
// cannot make non-enumerable as enumerable or vice-versa
if (reject) {
throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
}
return false;
}
@ -598,7 +598,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
if (reject) {
throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
}
return false;
}
@ -636,7 +636,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
if (reject) {
throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
}
return false;
}
@ -650,7 +650,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (!currentDesc.isConfigurable()) {
// not configurable can not be made configurable
if (reject) {
throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
}
return false;
}
@ -716,7 +716,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
setArray(getArray().set(index, value, false));
}
private void checkIntegerKey(final String key) {
private void checkIntegerKey(final Object key) {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
@ -734,7 +734,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @param key property key
* @param propertyDesc property descriptor for property
*/
public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
public final void addOwnProperty(final Object key, final PropertyDescriptor propertyDesc) {
// Already checked that there is no own property with that key.
PropertyDescriptor pdesc = propertyDesc;
@ -776,7 +776,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
*
* @return FindPropertyData or null if not found.
*/
public final FindProperty findProperty(final String key, final boolean deep) {
public final FindProperty findProperty(final Object key, final boolean deep) {
return findProperty(key, deep, this);
}
@ -798,7 +798,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
*
* @return FindPropertyData or null if not found.
*/
protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) {
protected FindProperty findProperty(final Object key, final boolean deep, final ScriptObject start) {
final PropertyMap selfMap = getMap();
final Property property = selfMap.findProperty(key);
@ -820,13 +820,13 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
/**
* Low level property API. This is similar to {@link #findProperty(String, boolean)} but returns a
* Low level property API. This is similar to {@link #findProperty(Object, boolean)} but returns a
* {@code boolean} value instead of a {@link FindProperty} object.
* @param key Property key.
* @param deep Whether the search should look up proto chain.
* @return true if the property was found.
*/
boolean hasProperty(final String key, final boolean deep) {
boolean hasProperty(final Object key, final boolean deep) {
if (getMap().findProperty(key) != null) {
return true;
}
@ -841,7 +841,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return false;
}
private SwitchPoint findBuiltinSwitchPoint(final String key) {
private SwitchPoint findBuiltinSwitchPoint(final Object key) {
for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) {
final Property prop = myProto.getMap().findProperty(key);
if (prop != null) {
@ -866,7 +866,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
*
* @return New property.
*/
public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
public final Property addOwnProperty(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
}
@ -881,7 +881,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
*
* @return New property.
*/
public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
public final Property addOwnProperty(final Object key, final int propertyFlags, final Object value) {
return addSpillProperty(key, propertyFlags, value, true);
}
@ -1349,42 +1349,60 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final Set<String> keys = new HashSet<>();
final Set<String> nonEnumerable = new HashSet<>();
for (ScriptObject self = this; self != null; self = self.getProto()) {
keys.addAll(Arrays.asList(self.getOwnKeys(true, nonEnumerable)));
keys.addAll(Arrays.asList(self.getOwnKeys(String.class, true, nonEnumerable)));
}
return keys.toArray(new String[keys.size()]);
}
/**
* return an array of own property keys associated with the object.
* Return an array of own property keys associated with the object.
*
* @param all True if to include non-enumerable keys.
* @return Array of keys.
*/
public final String[] getOwnKeys(final boolean all) {
return getOwnKeys(all, null);
return getOwnKeys(String.class, all, null);
}
/**
* Return an array of own property keys associated with the object.
*
* @param all True if to include non-enumerable keys.
* @return Array of keys.
*/
public final Symbol[] getOwnSymbols(final boolean all) {
return getOwnKeys(Symbol.class, all, null);
}
/**
* return an array of own property keys associated with the object.
*
* @param <T> the type returned keys.
* @param type the type of keys to return, either {@code String.class} or {@code Symbol.class}.
* @param all True if to include non-enumerable keys.
* @param nonEnumerable set of non-enumerable properties seen already.Used
to filter out shadowed, but enumerable properties from proto children.
* @param nonEnumerable set of non-enumerable properties seen already. Used to
* filter out shadowed, but enumerable properties from proto children.
* @return Array of keys.
*/
protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) {
@SuppressWarnings("unchecked")
protected <T> T[] getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable) {
final List<Object> keys = new ArrayList<>();
final PropertyMap selfMap = this.getMap();
final ArrayData array = getArray();
for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
keys.add(JSType.toString(iter.next().longValue()));
if (type == String.class) {
for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
keys.add(JSType.toString(iter.next().longValue()));
}
}
for (final Property property : selfMap.getProperties()) {
final boolean enumerable = property.isEnumerable();
final String key = property.getKey();
final Object key = property.getKey();
if (!type.isInstance(key)) {
continue;
}
if (all) {
keys.add(key);
} else if (enumerable) {
@ -1396,12 +1414,12 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
} else {
// store this non-enumerable property for later proto walk
if (nonEnumerable != null) {
nonEnumerable.add(key);
nonEnumerable.add((T) key);
}
}
}
return keys.toArray(new String[keys.size()]);
return keys.toArray((T[]) Array.newInstance(type, keys.size()));
}
/**
@ -2397,12 +2415,12 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
/**
* Invoke fall back if a property is not found.
* @param name Name of property.
* @param key Name of property.
* @param isScope is this a scope access?
* @param programPoint program point
* @return Result from call.
*/
protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
final Object func = (find != null)? find.getObjectValue() : null;
@ -2410,9 +2428,9 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (func instanceof ScriptFunction) {
final ScriptFunction sfunc = (ScriptFunction)func;
final Object self = isScope && sfunc.isStrict()? UNDEFINED : this;
ret = ScriptRuntime.apply(sfunc, self, name);
ret = ScriptRuntime.apply(sfunc, self, key);
} else if (isScope) {
throw referenceError("not.defined", name);
throw referenceError("not.defined", key.toString());
}
if (isValid(programPoint)) {
@ -2502,7 +2520,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final Set<String> keys = new LinkedHashSet<>();
final Set<String> nonEnumerable = new HashSet<>();
for (ScriptObject self = object; self != null; self = self.getProto()) {
keys.addAll(Arrays.asList(self.getOwnKeys(false, nonEnumerable)));
keys.addAll(Arrays.asList(self.getOwnKeys(String.class, false, nonEnumerable)));
}
this.values = keys.toArray(new String[keys.size()]);
}
@ -2518,7 +2536,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final ArrayList<Object> valueList = new ArrayList<>();
final Set<String> nonEnumerable = new HashSet<>();
for (ScriptObject self = object; self != null; self = self.getProto()) {
for (final String key : self.getOwnKeys(false, nonEnumerable)) {
for (final String key : self.getOwnKeys(String.class, false, nonEnumerable)) {
valueList.add(self.get(key));
}
}
@ -2532,7 +2550,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @param flags Property flags.
* @return Added property.
*/
private Property addSpillProperty(final String key, final int flags, final Object value, final boolean hasInitialValue) {
private Property addSpillProperty(final Object key, final int flags, final Object value, final boolean hasInitialValue) {
final PropertyMap propertyMap = getMap();
final int fieldSlot = propertyMap.getFreeFieldSlot();
final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0);
@ -2726,7 +2744,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
}
private int getInt(final int index, final String key, final int programPoint) {
private int getInt(final int index, final Object key, final int programPoint) {
if (isValidArrayIndex(index)) {
for (ScriptObject object = this; ; ) {
if (object.getMap().containsArrayKeys()) {
@ -2770,7 +2788,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
}
return getInt(index, JSType.toString(primitiveKey), programPoint);
return getInt(index, JSType.toPropertyKey(primitiveKey), programPoint);
}
@Override
@ -2809,7 +2827,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return getInt(index, JSType.toString(key), programPoint);
}
private long getLong(final int index, final String key, final int programPoint) {
private long getLong(final int index, final Object key, final int programPoint) {
if (isValidArrayIndex(index)) {
for (ScriptObject object = this; ; ) {
if (object.getMap().containsArrayKeys()) {
@ -2852,7 +2870,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
}
return getLong(index, JSType.toString(primitiveKey), programPoint);
return getLong(index, JSType.toPropertyKey(primitiveKey), programPoint);
}
@Override
@ -2891,7 +2909,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return getLong(index, JSType.toString(key), programPoint);
}
private double getDouble(final int index, final String key, final int programPoint) {
private double getDouble(final int index, final Object key, final int programPoint) {
if (isValidArrayIndex(index)) {
for (ScriptObject object = this; ; ) {
if (object.getMap().containsArrayKeys()) {
@ -2934,7 +2952,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
}
return getDouble(index, JSType.toString(primitiveKey), programPoint);
return getDouble(index, JSType.toPropertyKey(primitiveKey), programPoint);
}
@Override
@ -2973,7 +2991,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return getDouble(index, JSType.toString(key), programPoint);
}
private Object get(final int index, final String key) {
private Object get(final int index, final Object key) {
if (isValidArrayIndex(index)) {
for (ScriptObject object = this; ; ) {
if (object.getMap().containsArrayKeys()) {
@ -3015,7 +3033,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return array.getObject(index);
}
return get(index, JSType.toString(primitiveKey));
return get(index, JSType.toPropertyKey(primitiveKey));
}
@Override
@ -3161,7 +3179,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @param key property key
* @param value property value
*/
public final void setObject(final FindProperty find, final int callSiteFlags, final String key, final Object value) {
public final void setObject(final FindProperty find, final int callSiteFlags, final Object key, final Object value) {
FindProperty f = find;
invalidateGlobalConstant(key);
@ -3189,10 +3207,10 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (f != null) {
if (!f.getProperty().isWritable()) {
if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) {
throw typeError("assign.constant", key); // Overwriting ES6 const should throw also in non-strict mode.
throw typeError("assign.constant", key.toString()); // Overwriting ES6 const should throw also in non-strict mode.
}
if (isStrictFlag(callSiteFlags)) {
throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
throw typeError("property.not.writable", key.toString(), ScriptRuntime.safeToString(this));
}
return;
}
@ -3201,7 +3219,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
} else if (!isExtensible()) {
if (isStrictFlag(callSiteFlags)) {
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
}
} else {
ScriptObject sobj = this;
@ -3235,7 +3253,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return;
}
final String propName = JSType.toString(primitiveKey);
final Object propName = JSType.toPropertyKey(primitiveKey);
setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
}
@ -3255,7 +3273,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return;
}
final String propName = JSType.toString(primitiveKey);
final Object propName = JSType.toPropertyKey(primitiveKey);
setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
}
@ -3275,7 +3293,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return;
}
final String propName = JSType.toString(primitiveKey);
final Object propName = JSType.toPropertyKey(primitiveKey);
setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
}
@ -3295,7 +3313,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return;
}
final String propName = JSType.toString(primitiveKey);
final Object propName = JSType.toPropertyKey(primitiveKey);
setObject(findProperty(propName, true), callSiteFlags, propName, value);
}
@ -3529,7 +3547,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
public boolean has(final Object key) {
final Object primitiveKey = JSType.toPrimitive(key);
final int index = getArrayIndex(primitiveKey);
return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), true);
return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), true);
}
@Override
@ -3567,7 +3585,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
public boolean hasOwnProperty(final Object key) {
final Object primitiveKey = JSType.toPrimitive(key, String.class);
final int index = getArrayIndex(primitiveKey);
return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), false);
return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), false);
}
@Override
@ -3657,7 +3675,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
private boolean deleteObject(final Object key, final boolean strict) {
final String propName = JSType.toString(key);
final Object propName = JSType.toPropertyKey(key);
final FindProperty find = findProperty(propName, false);
if (find == null) {
@ -3666,7 +3684,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (!find.getProperty().isConfigurable()) {
if (strict) {
throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
throw typeError("cant.delete.property", propName.toString(), ScriptRuntime.safeToString(this));
}
return false;
}
@ -3712,7 +3730,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @param setter setter function for the property
* @return the newly created UserAccessorProperty
*/
protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
protected final UserAccessorProperty newUserAccessors(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags);
//property.getSetter(Object.class, getMap());
uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));

View File

@ -836,11 +836,11 @@ public final class ScriptRuntime {
} else if (yType == JSType.BOOLEAN) {
// Can reverse order as y is primitive
return equalBooleanToAny(y, x);
} else if (isNumberOrStringAndObject(xType, yType)) {
return equalNumberOrStringToObject(x, y);
} else if (isNumberOrStringAndObject(yType, xType)) {
} else if (isWrappedPrimitiveAndObject(xType, yType)) {
return equalWrappedPrimitiveToObject(x, y);
} else if (isWrappedPrimitiveAndObject(yType, xType)) {
// Can reverse order as y is primitive
return equalNumberOrStringToObject(y, x);
return equalWrappedPrimitiveToObject(y, x);
}
return false;
@ -854,8 +854,8 @@ public final class ScriptRuntime {
return xType == JSType.NUMBER && yType == JSType.STRING;
}
private static boolean isNumberOrStringAndObject(final JSType xType, final JSType yType) {
return (xType == JSType.NUMBER || xType == JSType.STRING) && yType == JSType.OBJECT;
private static boolean isWrappedPrimitiveAndObject(final JSType xType, final JSType yType) {
return (xType == JSType.NUMBER || xType == JSType.STRING || xType == JSType.SYMBOL) && yType == JSType.OBJECT;
}
private static boolean equalNumberToString(final Object num, final Object str) {
@ -869,7 +869,7 @@ public final class ScriptRuntime {
return equals(JSType.toNumber((Boolean)bool), any);
}
private static boolean equalNumberOrStringToObject(final Object numOrStr, final Object any) {
private static boolean equalWrappedPrimitiveToObject(final Object numOrStr, final Object any) {
return equals(numOrStr, JSType.toPrimitive(any));
}

View File

@ -158,7 +158,7 @@ public class SpillProperty extends AccessorProperty {
* @param flags the property flags
* @param slot spill slot
*/
public SpillProperty(final String key, final int flags, final int slot) {
public SpillProperty(final Object key, final int flags, final int slot) {
super(key, flags, slot, primitiveGetter(slot, flags), primitiveSetter(slot, flags), objectGetter(slot), objectSetter(slot));
}
@ -174,7 +174,7 @@ public class SpillProperty extends AccessorProperty {
setType(hasDualFields() ? initialType : Object.class);
}
SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
SpillProperty(final Object key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
this(key, flags, slot);
setInitialValue(owner, initialValue);
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2015, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.internal.runtime;
import java.io.Serializable;
/**
* This class represents a unique, non-String Object property key as defined in ECMAScript 6.
*/
public final class Symbol implements Serializable {
private final String name;
private static final long serialVersionUID = -2988436597549486913L;
/**
* Symbol constructor
* @param name symbol name
*/
public Symbol(final String name) {
this.name = name;
}
@Override
public String toString() {
return "Symbol(" + name + ")";
}
/**
* Return the symbol's name
* @return the name
*/
public final String getName() {
return name;
}
}

View File

@ -120,7 +120,7 @@ public final class UserAccessorProperty extends SpillProperty {
* @param flags property flags
* @param slot spill slot
*/
UserAccessorProperty(final String key, final int flags, final int slot) {
UserAccessorProperty(final Object key, final int flags, final int slot) {
super(key, flags, slot);
}
@ -226,7 +226,7 @@ public final class UserAccessorProperty extends SpillProperty {
@Override
public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
try {
invokeObjectSetter(getAccessors((owner != null) ? owner : self), getObjectSetterInvoker(), strict ? getKey() : null, self, value);
invokeObjectSetter(getAccessors((owner != null) ? owner : self), getObjectSetterInvoker(), strict ? getKey().toString() : null, self, value);
} catch (final Error | RuntimeException t) {
throw t;
} catch (final Throwable t) {

View File

@ -198,7 +198,7 @@ public final class WithObject extends Scope {
* @return FindPropertyData or null if not found.
*/
@Override
protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) {
protected FindProperty findProperty(final Object key, final boolean deep, final ScriptObject start) {
// We call findProperty on 'expression' with 'expression' itself as start parameter.
// This way in ScriptObject.setObject we can tell the property is from a 'with' expression
// (as opposed from another non-scope object in the proto chain such as Object.prototype).
@ -210,18 +210,18 @@ public final class WithObject extends Scope {
}
@Override
protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
final FindProperty find = expression.findProperty(NO_SUCH_PROPERTY_NAME, true);
if (find != null) {
final Object func = find.getObjectValue();
if (func instanceof ScriptFunction) {
final ScriptFunction sfunc = (ScriptFunction)func;
final Object self = isScope && sfunc.isStrict()? UNDEFINED : expression;
return ScriptRuntime.apply(sfunc, self, name);
return ScriptRuntime.apply(sfunc, self, key);
}
}
return getProto().invokeNoSuchProperty(name, isScope, programPoint);
return getProto().invokeNoSuchProperty(key, isScope, programPoint);
}
@Override

View File

@ -154,6 +154,11 @@ type.error.unsupported.java.to.type=Unsupported Java.to target type {0}.
type.error.java.array.conversion.failed=Java.to conversion to array type {0} failed
type.error.constructor.requires.new=Constructor {0} requires "new".
type.error.new.on.nonpublic.javatype=new cannot be used with non-public java type {0}.
type.error.invalid.weak.key=invalid value {0} used as weak key.
type.error.symbol.to.string=Can not convert Symbol value to string.
type.error.symbol.to.number=Can not convert Symbol value to number.
type.error.not.a.symbol={0} is not a symbol.
type.error.symbol.as.constructor=Symbol is not a constructor.
range.error.dataview.constructor.offset=Wrong offset or length in DataView constructor
range.error.dataview.offset=Offset is outside the bounds of the DataView

View File

@ -36,6 +36,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
@ -47,6 +48,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.objects.NativeSymbol;
import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
@ -54,7 +56,10 @@ import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Symbol;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
import jdk.nashorn.internal.runtime.options.Options;
/**
@ -474,7 +479,7 @@ public class Shell implements PartialParser {
try {
final Object res = context.eval(global, source, global, "<shell>");
if (res != ScriptRuntime.UNDEFINED) {
err.println(JSType.toString(res));
err.println(toString(res, global));
}
} catch (final Exception e) {
err.println(e);
@ -491,4 +496,56 @@ public class Shell implements PartialParser {
return SUCCESS;
}
/**
* Converts {@code result} to a printable string. The reason we don't use {@link JSType#toString(Object)}
* or {@link ScriptRuntime#safeToString(Object)} is that we want to be able to render Symbol values
* even if they occur within an Array, and therefore have to implement our own Array to String
* conversion.
*
* @param result the result
* @param global the global object
* @return the string representation
*/
protected static String toString(final Object result, final Global global) {
if (result instanceof Symbol) {
// Normal implicit conversion of symbol to string would throw TypeError
return result.toString();
}
if (result instanceof NativeSymbol) {
return JSType.toPrimitive(result).toString();
}
if (isArrayWithDefaultToString(result, global)) {
// This should yield the same string as Array.prototype.toString but
// will not throw if the array contents include symbols.
final StringBuilder sb = new StringBuilder();
final Iterator<Object> iter = ArrayLikeIterator.arrayLikeIterator(result, true);
while (iter.hasNext()) {
final Object obj = iter.next();
if (obj != null && obj != ScriptRuntime.UNDEFINED) {
sb.append(toString(obj, global));
}
if (iter.hasNext()) {
sb.append(',');
}
}
return sb.toString();
}
return JSType.toString(result);
}
private static boolean isArrayWithDefaultToString(final Object result, final Global global) {
if (result instanceof ScriptObject) {
final ScriptObject sobj = (ScriptObject) result;
return sobj.isArray() && sobj.get("toString") == global.getArrayPrototype().get("toString");
}
return false;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2015, 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.
*/
/**
* Make sure ECMAScript 6 features are not available in ES5 mode.
*
* @test
* @run
*/
if (typeof Symbol !== 'undefined' || 'Symbol' in this) {
Assert.fail('Symbol is defined in global scope');
}
if (typeof Object.getOwnPropertySymbols !== 'undefined' || 'getOwnPropertySymbols' in Object) {
Assert.fail('getOwnPropertySymbols is defined in global Object');
}
function expectError(src, msg, error) {
try {
eval(src);
Assert.fail(msg);
} catch (e) {
if (e.name !== error) {
Assert.fail('Unexpected error: ' + e);
}
}
}
expectError('let i = 0', 'let', 'SyntaxError');
expectError('const i = 0', 'const', 'SyntaxError');
expectError('for (let i = 0; i < 10; i++) print(i)', 'for-let', 'SyntaxError');
expectError('0b0', 'numeric literal', 'SyntaxError');
expectError('0o0', 'numeric litera', 'SyntaxError');
expectError('`text`', 'template literal', 'SyntaxError');
expectError('`${ x }`', 'template literal', 'SyntaxError');
expectError('`text ${ x } text`', 'template literal', 'SyntaxError');
expectError('f`text`', 'template literal', 'SyntaxError');

View File

@ -26,7 +26,8 @@
*
* @test
* @run
* @option --language=es6 */
* @option --language=es6
*/
"use strict";

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 2015, 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-8141702: Add support for Symbol property keys
*
* @test
* @run
* @option --language=es6
*/
Assert.assertTrue(typeof Symbol === 'function');
Assert.assertTrue(typeof Symbol() === 'symbol');
Assert.assertTrue(Symbol().toString() === 'Symbol()');
Assert.assertTrue(Symbol('foo').toString() === 'Symbol(foo)');
Assert.assertTrue(Symbol(1).toString() === 'Symbol(1)');
Assert.assertTrue(Symbol(true).toString() === 'Symbol(true)');
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");
Assert.assertFalse(s1 instanceof Symbol); // not an object
var obj = {};
obj['foo'] = 'foo';
obj[s1] = s1;
obj['bar'] = 'bar';
obj[1] = 1;
obj[s2] = s2;
Assert.assertTrue(obj['foo'] === 'foo');
Assert.assertTrue(obj[s1] === s1);
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);
Assert.assertTrue(expectedNames.length == actualNames.length);
Assert.assertTrue(expectedSymbols.length == actualSymbols.length);
for (var key in expectedNames) {
Assert.assertTrue(expectedNames[key] === actualNames[key]);
}
for (var key in expectedSymbols) {
Assert.assertTrue(expectedSymbols[key] === actualSymbols[key]);
}
// Delete
Assert.assertTrue(delete obj[s1]);
Assert.assertTrue(Object.getOwnPropertySymbols(obj).length === 1);
Assert.assertTrue(Object.getOwnPropertySymbols(obj)[0] === s2);
// Object.defineProperty
Object.defineProperty(obj, s1, {value : 'hello'});
Assert.assertTrue(obj[s1] === 'hello');
actualSymbols = Object.getOwnPropertySymbols(obj);
Assert.assertTrue(Object.getOwnPropertySymbols(obj).length === 2);
Assert.assertTrue(Object.getOwnPropertySymbols(obj)[1] === s1);
// Symbol called as constructor
try {
new Symbol();
Assert.fail("Symbol invoked as constructor");
} catch (e) {
if (e.name !== "TypeError" || e.message !== "Symbol is not a constructor.") {
Assert.fail("Unexpected error: " + e);
}
}
// Implicit conversion to string or number should throw
try {
' ' + s1;
Assert.fail("Symbol converted to string");
} catch (e) {
if (e.name !== "TypeError" || e.message !== "Can not convert Symbol value to string.") {
Assert.fail("Unexpected error: " + e);
}
}
try {
4 * s1;
Assert.fail("Symbol converted to number");
} catch (e) {
if (e.name !== "TypeError" || e.message !== "Can not convert Symbol value to number.") {
Assert.fail("Unexpected error: " + e);
}
}
// Symbol.for and Symbol.keyFor
var uncached = Symbol('foo');
var cached = Symbol.for('foo');
Assert.assertTrue(uncached !== cached);
Assert.assertTrue(Symbol.keyFor(uncached) === undefined);
Assert.assertTrue(Symbol.keyFor(cached) === 'foo');
Assert.assertTrue(cached === Symbol.for('foo'));
Assert.assertTrue(cached === Symbol.for('f' + 'oo'));
// Object wrapper
var o = Object(s1);
obj = {};
obj[s1] = "s1";
Assert.assertTrue(o == s1);
Assert.assertTrue(o !== s1);
Assert.assertTrue(typeof o === 'object');
Assert.assertTrue(o instanceof Symbol);
Assert.assertTrue(obj[o] == 's1');
Assert.assertTrue(o in obj);
// various non-strict comparisons that should fail
Assert.assertFalse(0 == Symbol());
Assert.assertFalse(1 == Symbol(1));
Assert.assertFalse(null == Symbol());
Assert.assertFalse(undefined == Symbol);
Assert.assertFalse('Symbol()' == Symbol());
Assert.assertFalse('Symbol(foo)' == Symbol('foo'));