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:
rossberg@chromium.org 2014-01-09 15:57:30 +00:00
parent 839297487f
commit 014a86ef8c
9 changed files with 172 additions and 49 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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();

View File

@ -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.

View File

@ -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.

View File

@ -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();

View File

@ -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.
));
}

View File

@ -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)");

View File

@ -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()