From: rossberg@chromium.org Date: Thu, 9 Jan 2014 15:57:30 +0000 (+0000) Subject: ES6: Add Object.getOwnPropertySymbols X-Git-Tag: upstream/4.7.83~11215 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=014a86ef8c75c0f530f913ad15d40fe879b9c4d0;p=platform%2Fupstream%2Fv8.git 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 . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18520 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/macros.py b/src/macros.py index 7bad23b..0ba5427 100644 --- a/src/macros.py +++ b/src/macros.py @@ -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; diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js index 4277136..212bb0b 100644 --- a/src/mirror-debugger.js +++ b/src/mirror-debugger.js @@ -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. diff --git a/src/objects.cc b/src/objects.cc index c484eab..dcbe211 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -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::NumberOfElementsFilterAttributes( for (int i = 0; i < capacity; i++) { Object* k = HashTable::KeyAt(i); if (HashTable::IsKey(k) && - ((filter & SYMBOLIC) == 0 || !k->IsSymbol())) { + !FilterKey(k, filter)) { PropertyDetails details = DetailsAt(i); if (details.IsDeleted()) continue; PropertyAttributes attr = details.attributes(); diff --git a/src/property-details.h b/src/property-details.h index 753eeee..71ac8bb 100644 --- a/src/property-details.h +++ b/src/property-details.h @@ -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. diff --git a/src/runtime.cc b/src/runtime.cc index ba9da22..7a2d46c 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -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(filter_value); // Skip the global proxy as it has no properties and always delegates to the // real global object. diff --git a/src/symbol.js b/src/symbol.js index 050e7d9..be308d9 100644 --- a/src/symbol.js +++ b/src/symbol.js @@ -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(); diff --git a/src/v8natives.js b/src/v8natives.js index b2b2ec5..e987baf 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -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); - - // 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); + // 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); + } } } // 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. )); } diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 3e3afee..00465e3 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -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)"); diff --git a/test/mjsunit/harmony/symbols.js b/test/mjsunit/harmony/symbols.js index 3fcd06d..ce02a05 100644 --- a/test/mjsunit/harmony/symbols.js +++ b/test/mjsunit/harmony/symbols.js @@ -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()