ES6: Add Object.getOwnPropertySymbols
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.getownpropertysymbols This allows you to get the symbols used as property keys for an object. var object = {}; var sym = Symbol(); object[sym] = 42; assert(Object.getOwnPropertySymbols(object)[0] === sym); This is only available with --harmony-symbols BUG=v8:3049 R=rossberg@chromium.org, rossberg LOG=Y Review URL: https://codereview.chromium.org/108083005 Patch from Erik Arvidsson <arv@chromium.org>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18520 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
839297487f
commit
014a86ef8c
@ -159,6 +159,7 @@ macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ?
|
||||
|
||||
# Private names.
|
||||
macro NEW_PRIVATE(name) = (%CreatePrivateSymbol(name));
|
||||
macro IS_PRIVATE(sym) = (%SymbolIsPrivate(sym));
|
||||
macro HAS_PRIVATE(obj, sym) = (sym in obj);
|
||||
macro GET_PRIVATE(obj, sym) = (obj[sym]);
|
||||
macro SET_PRIVATE(obj, sym, val) = (obj[sym] = val);
|
||||
@ -260,3 +261,9 @@ const COMPILATION_TYPE_JSON = 2;
|
||||
|
||||
# Matches Messages::kNoLineNumberInfo from v8.h
|
||||
const kNoLineNumberInfo = 0;
|
||||
|
||||
# Matches PropertyAttributes from property-details.h
|
||||
const PROPERTY_ATTRIBUTES_NONE = 0;
|
||||
const PROPERTY_ATTRIBUTES_STRING = 8;
|
||||
const PROPERTY_ATTRIBUTES_SYMBOLIC = 16;
|
||||
const PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL = 32;
|
||||
|
@ -637,8 +637,9 @@ ObjectMirror.prototype.propertyNames = function(kind, limit) {
|
||||
|
||||
// Find all the named properties.
|
||||
if (kind & PropertyKind.Named) {
|
||||
// Get the local property names.
|
||||
propertyNames = %GetLocalPropertyNames(this.value_, true);
|
||||
// Get all the local property names.
|
||||
propertyNames =
|
||||
%GetLocalPropertyNames(this.value_, PROPERTY_ATTRIBUTES_NONE);
|
||||
total += propertyNames.length;
|
||||
|
||||
// Get names for named interceptor properties if any.
|
||||
|
@ -5924,6 +5924,24 @@ bool JSReceiver::IsSimpleEnum() {
|
||||
}
|
||||
|
||||
|
||||
static bool FilterKey(Object* key, PropertyAttributes filter) {
|
||||
if ((filter & SYMBOLIC) && key->IsSymbol()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((filter & PRIVATE_SYMBOL) &&
|
||||
key->IsSymbol() && Symbol::cast(key)->is_private()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((filter & STRING) && !key->IsSymbol()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int Map::NumberOfDescribedProperties(DescriptorFlag which,
|
||||
PropertyAttributes filter) {
|
||||
int result = 0;
|
||||
@ -5933,7 +5951,7 @@ int Map::NumberOfDescribedProperties(DescriptorFlag which,
|
||||
: NumberOfOwnDescriptors();
|
||||
for (int i = 0; i < limit; i++) {
|
||||
if ((descs->GetDetails(i).attributes() & filter) == 0 &&
|
||||
((filter & SYMBOLIC) == 0 || !descs->GetKey(i)->IsSymbol())) {
|
||||
!FilterKey(descs->GetKey(i), filter)) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
@ -13540,7 +13558,7 @@ void JSObject::GetLocalPropertyNames(
|
||||
DescriptorArray* descs = map()->instance_descriptors();
|
||||
for (int i = 0; i < real_size; i++) {
|
||||
if ((descs->GetDetails(i).attributes() & filter) == 0 &&
|
||||
((filter & SYMBOLIC) == 0 || !descs->GetKey(i)->IsSymbol())) {
|
||||
!FilterKey(descs->GetKey(i), filter)) {
|
||||
storage->set(index++, descs->GetKey(i));
|
||||
}
|
||||
}
|
||||
@ -15642,7 +15660,7 @@ int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Object* k = HashTable<Shape, Key>::KeyAt(i);
|
||||
if (HashTable<Shape, Key>::IsKey(k) &&
|
||||
((filter & SYMBOLIC) == 0 || !k->IsSymbol())) {
|
||||
!FilterKey(k, filter)) {
|
||||
PropertyDetails details = DetailsAt(i);
|
||||
if (details.IsDeleted()) continue;
|
||||
PropertyAttributes attr = details.attributes();
|
||||
|
@ -42,9 +42,12 @@ enum PropertyAttributes {
|
||||
SEALED = DONT_DELETE,
|
||||
FROZEN = SEALED | READ_ONLY,
|
||||
|
||||
SYMBOLIC = 8, // Used to filter symbol names
|
||||
DONT_SHOW = DONT_ENUM | SYMBOLIC,
|
||||
ABSENT = 16 // Used in runtime to indicate a property is absent.
|
||||
STRING = 8, // Used to filter symbols and string names
|
||||
SYMBOLIC = 16,
|
||||
PRIVATE_SYMBOL = 32,
|
||||
|
||||
DONT_SHOW = DONT_ENUM | SYMBOLIC | PRIVATE_SYMBOL,
|
||||
ABSENT = 64 // Used in runtime to indicate a property is absent.
|
||||
// ABSENT can never be stored in or returned from a descriptor's attributes
|
||||
// bitfield. It is only used as a return value meaning the attributes of
|
||||
// a non-existent property.
|
||||
|
@ -5736,6 +5736,7 @@ static int LocalPrototypeChainLength(JSObject* obj) {
|
||||
|
||||
// Return the names of the local named properties.
|
||||
// args[0]: object
|
||||
// args[1]: PropertyAttributes as int
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 2);
|
||||
@ -5743,8 +5744,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(include_symbols, 1);
|
||||
PropertyAttributes filter = include_symbols ? NONE : SYMBOLIC;
|
||||
CONVERT_SMI_ARG_CHECKED(filter_value, 1);
|
||||
PropertyAttributes filter = static_cast<PropertyAttributes>(filter_value);
|
||||
|
||||
// Skip the global proxy as it has no properties and always delegates to the
|
||||
// real global object.
|
||||
|
@ -68,6 +68,20 @@ function SymbolValueOf() {
|
||||
return %_ValueOf(this);
|
||||
}
|
||||
|
||||
|
||||
// ES6 19.1.2.8
|
||||
function ObjectGetOwnPropertySymbols(obj) {
|
||||
if (!IS_SPEC_OBJECT(obj)) {
|
||||
throw MakeTypeError("called_on_non_object",
|
||||
["Object.getOwnPropertySymbols"]);
|
||||
}
|
||||
|
||||
// TODO(arv): Proxies use a shared trap for String and Symbol keys.
|
||||
|
||||
return ObjectGetOwnPropertyKeys(obj, true);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
function SetUpSymbol() {
|
||||
@ -85,3 +99,14 @@ function SetUpSymbol() {
|
||||
}
|
||||
|
||||
SetUpSymbol();
|
||||
|
||||
|
||||
function ExtendObject() {
|
||||
%CheckIsBootstrapping();
|
||||
|
||||
InstallFunctions($Object, DONT_ENUM, $Array(
|
||||
"getOwnPropertySymbols", ObjectGetOwnPropertySymbols
|
||||
));
|
||||
}
|
||||
|
||||
ExtendObject();
|
||||
|
@ -1052,46 +1052,41 @@ function ToNameArray(obj, trap, includeSymbols) {
|
||||
}
|
||||
|
||||
|
||||
// ES5 section 15.2.3.4.
|
||||
function ObjectGetOwnPropertyNames(obj) {
|
||||
if (!IS_SPEC_OBJECT(obj)) {
|
||||
throw MakeTypeError("called_on_non_object", ["Object.getOwnPropertyNames"]);
|
||||
}
|
||||
// Special handling for proxies.
|
||||
if (%IsJSProxy(obj)) {
|
||||
var handler = %GetHandler(obj);
|
||||
var names = CallTrap0(handler, "getOwnPropertyNames", UNDEFINED);
|
||||
return ToNameArray(names, "getOwnPropertyNames", false);
|
||||
}
|
||||
|
||||
function ObjectGetOwnPropertyKeys(obj, symbolsOnly) {
|
||||
var nameArrays = new InternalArray();
|
||||
var filter = symbolsOnly ?
|
||||
PROPERTY_ATTRIBUTES_STRING | PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL :
|
||||
PROPERTY_ATTRIBUTES_SYMBOLIC;
|
||||
|
||||
// Find all the indexed properties.
|
||||
|
||||
// Get the local element names.
|
||||
var localElementNames = %GetLocalElementNames(obj);
|
||||
for (var i = 0; i < localElementNames.length; ++i) {
|
||||
localElementNames[i] = %_NumberToString(localElementNames[i]);
|
||||
}
|
||||
nameArrays.push(localElementNames);
|
||||
// Only get the local element names if we want to include string keys.
|
||||
if (!symbolsOnly) {
|
||||
var localElementNames = %GetLocalElementNames(obj);
|
||||
for (var i = 0; i < localElementNames.length; ++i) {
|
||||
localElementNames[i] = %_NumberToString(localElementNames[i]);
|
||||
}
|
||||
nameArrays.push(localElementNames);
|
||||
|
||||
// Get names for indexed interceptor properties.
|
||||
var interceptorInfo = %GetInterceptorInfo(obj);
|
||||
if ((interceptorInfo & 1) != 0) {
|
||||
var indexedInterceptorNames = %GetIndexedInterceptorElementNames(obj);
|
||||
if (!IS_UNDEFINED(indexedInterceptorNames)) {
|
||||
nameArrays.push(indexedInterceptorNames);
|
||||
// Get names for indexed interceptor properties.
|
||||
var interceptorInfo = %GetInterceptorInfo(obj);
|
||||
if ((interceptorInfo & 1) != 0) {
|
||||
var indexedInterceptorNames = %GetIndexedInterceptorElementNames(obj);
|
||||
if (!IS_UNDEFINED(indexedInterceptorNames)) {
|
||||
nameArrays.push(indexedInterceptorNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find all the named properties.
|
||||
|
||||
// Get the local property names.
|
||||
nameArrays.push(%GetLocalPropertyNames(obj, false));
|
||||
nameArrays.push(%GetLocalPropertyNames(obj, filter));
|
||||
|
||||
// Get names for named interceptor properties if any.
|
||||
if ((interceptorInfo & 2) != 0) {
|
||||
var namedInterceptorNames = %GetNamedInterceptorPropertyNames(obj);
|
||||
var namedInterceptorNames =
|
||||
%GetNamedInterceptorPropertyNames(obj);
|
||||
if (!IS_UNDEFINED(namedInterceptorNames)) {
|
||||
nameArrays.push(namedInterceptorNames);
|
||||
}
|
||||
@ -1104,18 +1099,18 @@ function ObjectGetOwnPropertyNames(obj) {
|
||||
// Property names are expected to be unique strings,
|
||||
// but interceptors can interfere with that assumption.
|
||||
if (interceptorInfo != 0) {
|
||||
var propertySet = { __proto__: null };
|
||||
var seenKeys = { __proto__: null };
|
||||
var j = 0;
|
||||
for (var i = 0; i < propertyNames.length; ++i) {
|
||||
if (IS_SYMBOL(propertyNames[i])) continue;
|
||||
var name = ToString(propertyNames[i]);
|
||||
// We need to check for the exact property value since for intrinsic
|
||||
// properties like toString if(propertySet["toString"]) will always
|
||||
// succeed.
|
||||
if (propertySet[name] === true) {
|
||||
continue;
|
||||
var name = propertyNames[i];
|
||||
if (symbolsOnly) {
|
||||
if (!IS_SYMBOL(name) || IS_PRIVATE(name)) continue;
|
||||
} else {
|
||||
if (IS_SYMBOL(name)) continue;
|
||||
name = ToString(name);
|
||||
}
|
||||
propertySet[name] = true;
|
||||
if (seenKeys[name]) continue;
|
||||
seenKeys[name] = true;
|
||||
propertyNames[j++] = name;
|
||||
}
|
||||
propertyNames.length = j;
|
||||
@ -1125,6 +1120,22 @@ function ObjectGetOwnPropertyNames(obj) {
|
||||
}
|
||||
|
||||
|
||||
// ES5 section 15.2.3.4.
|
||||
function ObjectGetOwnPropertyNames(obj) {
|
||||
if (!IS_SPEC_OBJECT(obj)) {
|
||||
throw MakeTypeError("called_on_non_object", ["Object.getOwnPropertyNames"]);
|
||||
}
|
||||
// Special handling for proxies.
|
||||
if (%IsJSProxy(obj)) {
|
||||
var handler = %GetHandler(obj);
|
||||
var names = CallTrap0(handler, "getOwnPropertyNames", UNDEFINED);
|
||||
return ToNameArray(names, "getOwnPropertyNames", false);
|
||||
}
|
||||
|
||||
return ObjectGetOwnPropertyKeys(obj, false);
|
||||
}
|
||||
|
||||
|
||||
// ES5 section 15.2.3.5.
|
||||
function ObjectCreate(proto, properties) {
|
||||
if (!IS_SPEC_OBJECT(proto) && proto !== null) {
|
||||
@ -1434,12 +1445,15 @@ function SetUpObject() {
|
||||
"getPrototypeOf", ObjectGetPrototypeOf,
|
||||
"getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
|
||||
"getOwnPropertyNames", ObjectGetOwnPropertyNames,
|
||||
// getOwnPropertySymbols is added in symbol.js.
|
||||
"is", ObjectIs,
|
||||
"isExtensible", ObjectIsExtensible,
|
||||
"isFrozen", ObjectIsFrozen,
|
||||
"isSealed", ObjectIsSealed,
|
||||
"preventExtensions", ObjectPreventExtension,
|
||||
"seal", ObjectSeal
|
||||
// deliverChangeRecords, getNotifier, observe and unobserve are added
|
||||
// in object-observe.js.
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -10195,7 +10195,8 @@ THREADED_TEST(Regress91517) {
|
||||
// Call the runtime version of GetLocalPropertyNames() on the natively
|
||||
// created object through JavaScript.
|
||||
context->Global()->Set(v8_str("obj"), o4);
|
||||
CompileRun("var names = %GetLocalPropertyNames(obj, true);");
|
||||
// PROPERTY_ATTRIBUTES_NONE = 0
|
||||
CompileRun("var names = %GetLocalPropertyNames(obj, 0);");
|
||||
|
||||
ExpectInt32("names.length", 1006);
|
||||
ExpectTrue("names.indexOf(\"baz\") >= 0");
|
||||
@ -10249,7 +10250,8 @@ THREADED_TEST(Regress269562) {
|
||||
// the natively created object through JavaScript.
|
||||
context->Global()->Set(v8_str("obj"), o2);
|
||||
context->Global()->Set(v8_str("sym"), sym);
|
||||
CompileRun("var names = %GetLocalPropertyNames(obj, true);");
|
||||
// PROPERTY_ATTRIBUTES_NONE = 0
|
||||
CompileRun("var names = %GetLocalPropertyNames(obj, 0);");
|
||||
|
||||
ExpectInt32("names.length", 7);
|
||||
ExpectTrue("names.indexOf(\"foo\") >= 0");
|
||||
@ -21228,7 +21230,8 @@ TEST(AccessCheckThrows) {
|
||||
CheckCorrectThrow("%HasElement(other, 1)");
|
||||
CheckCorrectThrow("%IsPropertyEnumerable(other, 'x')");
|
||||
CheckCorrectThrow("%GetPropertyNames(other)");
|
||||
CheckCorrectThrow("%GetLocalPropertyNames(other, true)");
|
||||
// PROPERTY_ATTRIBUTES_NONE = 0
|
||||
CheckCorrectThrow("%GetLocalPropertyNames(other, 0)");
|
||||
CheckCorrectThrow("%DefineOrRedefineAccessorProperty("
|
||||
"other, 'x', null, null, 1)");
|
||||
|
||||
|
@ -273,7 +273,7 @@ function TestKeyGet(obj) {
|
||||
}
|
||||
|
||||
|
||||
function TestKeyHas() {
|
||||
function TestKeyHas(obj) {
|
||||
for (var i in symbols) {
|
||||
assertTrue(symbols[i] in obj)
|
||||
assertTrue(Object.hasOwnProperty.call(obj, symbols[i]))
|
||||
@ -298,6 +298,15 @@ function TestKeyNames(obj) {
|
||||
}
|
||||
|
||||
|
||||
function TestGetOwnPropertySymbols(obj) {
|
||||
var syms = Object.getOwnPropertySymbols(obj)
|
||||
assertEquals(syms.length, symbols.length)
|
||||
for (var i in syms) {
|
||||
assertEquals("symbol", typeof syms[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function TestKeyDescriptor(obj) {
|
||||
for (var i in symbols) {
|
||||
var desc = Object.getOwnPropertyDescriptor(obj, symbols[i]);
|
||||
@ -331,6 +340,7 @@ for (var i in objs) {
|
||||
TestKeyHas(obj)
|
||||
TestKeyEnum(obj)
|
||||
TestKeyNames(obj)
|
||||
TestGetOwnPropertySymbols(obj)
|
||||
TestKeyDescriptor(obj)
|
||||
TestKeyDelete(obj)
|
||||
}
|
||||
@ -350,3 +360,44 @@ function TestCachedKeyAfterScavenge() {
|
||||
}
|
||||
}
|
||||
TestCachedKeyAfterScavenge();
|
||||
|
||||
|
||||
function TestGetOwnPropertySymbolsWithProto() {
|
||||
// We need to be have fast properties to have insertion order for property
|
||||
// keys. The current limit is currently 30 properties.
|
||||
var syms = symbols.slice(0, 30);
|
||||
var proto = {}
|
||||
var object = Object.create(proto)
|
||||
for (var i = 0; i < syms.length; i++) {
|
||||
// Even on object, odd on proto.
|
||||
if (i % 2) {
|
||||
proto[syms[i]] = i
|
||||
} else {
|
||||
object[syms[i]] = i
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(%HasFastProperties(object));
|
||||
|
||||
var objectOwnSymbols = Object.getOwnPropertySymbols(object)
|
||||
assertEquals(objectOwnSymbols.length, syms.length / 2)
|
||||
|
||||
for (var i = 0; i < objectOwnSymbols.length; i++) {
|
||||
assertEquals(objectOwnSymbols[i], syms[i * 2])
|
||||
}
|
||||
}
|
||||
TestGetOwnPropertySymbolsWithProto()
|
||||
|
||||
|
||||
function TestGetOwnPropertySymbolsWithPrivateSymbols() {
|
||||
var privateSymbol = %CreatePrivateSymbol("private")
|
||||
var publicSymbol = Symbol()
|
||||
var publicSymbol2 = Symbol()
|
||||
var obj = {}
|
||||
obj[publicSymbol] = 1
|
||||
obj[privateSymbol] = 2
|
||||
obj[publicSymbol2] = 3
|
||||
var syms = Object.getOwnPropertySymbols(obj)
|
||||
assertEquals(syms, [publicSymbol, publicSymbol2])
|
||||
}
|
||||
TestGetOwnPropertySymbolsWithPrivateSymbols()
|
||||
|
Loading…
Reference in New Issue
Block a user