mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-16 02:43:20 +00:00
8141702: Add support for Symbol property keys
Reviewed-by: attila, sundar
This commit is contained in:
parent
15ef19ee62
commit
13dbf6a119
@ -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)?
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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").
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
58
nashorn/test/script/basic/es6.js
Normal file
58
nashorn/test/script/basic/es6.js
Normal 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');
|
||||
@ -26,7 +26,8 @@
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
* @option --language=es6 */
|
||||
* @option --language=es6
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
146
nashorn/test/script/basic/es6/symbols.js
Normal file
146
nashorn/test/script/basic/es6/symbols.js
Normal 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'));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user