[api] Add more parameters to Object::GetPropertyNames

Expose more or less the full functionality of the KeyAccumulator in the API:
- use the PropertyFilter introduced for GetOwnPropertyNames
- use KeyCollectionLimit for OWN_ONLY or INLCUDE_PROTOS
- use IndexFilter to eithe SKIP_INDICES or INCLUDE_INDICES

Rewire Object::GetOwnPropertyNames to use GetPropertyNames.

BUG=chromium:148757

Review-Url: https://codereview.chromium.org/2002203002
Cr-Commit-Position: refs/heads/master@{#36595}
This commit is contained in:
cbruni 2016-05-30 08:52:56 -07:00 committed by Commit bot
parent 07fadde87c
commit 63efe9e416
16 changed files with 237 additions and 83 deletions

View File

@ -2631,6 +2631,21 @@ enum PropertyFilter {
SKIP_SYMBOLS = 16
};
/**
* Keys/Properties filter enums:
*
* KeyCollectionMode limits the range of collected properties. kOwnOnly limits
* the collected properties to the given Object only. kIncludesPrototypes will
* include all keys of the objects's prototype chain as well.
*/
enum class KeyCollectionMode { kOwnOnly, kIncludePrototypes };
/**
* kIncludesIndices allows for integer indices to be collected, while
* kSkipIndices will exclude integer indicies from being collected.
*/
enum class IndexFilter { kIncludeIndices, kSkipIndices };
/**
* Integrity level for objects.
*/
@ -2780,6 +2795,9 @@ class V8_EXPORT Object : public Value {
V8_DEPRECATE_SOON("Use maybe version", Local<Array> GetPropertyNames());
V8_WARN_UNUSED_RESULT MaybeLocal<Array> GetPropertyNames(
Local<Context> context);
V8_WARN_UNUSED_RESULT MaybeLocal<Array> GetPropertyNames(
Local<Context> context, KeyCollectionMode mode,
PropertyFilter property_filter, IndexFilter index_filter);
/**
* This function has the same functionality as GetPropertyNames but

View File

@ -3867,15 +3867,27 @@ Local<Object> v8::Object::FindInstanceInPrototypeChain(
return Utils::ToLocal(i::handle(iter.GetCurrent<i::JSObject>(), isolate));
}
MaybeLocal<Array> v8::Object::GetPropertyNames(Local<Context> context) {
return GetPropertyNames(
context, v8::KeyCollectionMode::kIncludePrototypes,
static_cast<v8::PropertyFilter>(ONLY_ENUMERABLE | SKIP_SYMBOLS),
v8::IndexFilter::kIncludeIndices);
}
MaybeLocal<Array> v8::Object::GetPropertyNames(Local<Context> context,
KeyCollectionMode mode,
PropertyFilter property_filter,
IndexFilter index_filter) {
PREPARE_FOR_EXECUTION(context, Object, GetPropertyNames, Array);
auto self = Utils::OpenHandle(this);
i::Handle<i::FixedArray> value;
has_pending_exception = !i::KeyAccumulator::GetKeys(self, i::INCLUDE_PROTOS,
i::ENUMERABLE_STRINGS)
.ToHandle(&value);
i::KeyAccumulator accumulator(
isolate, static_cast<i::KeyCollectionMode>(mode),
static_cast<i::PropertyFilter>(property_filter));
accumulator.set_skip_indices(index_filter == IndexFilter::kSkipIndices);
has_pending_exception = accumulator.CollectKeys(self, self).IsNothing();
RETURN_ON_FAILED_EXECUTION(Array);
value = accumulator.GetKeys(i::GetKeysConversion::kKeepNumbers);
DCHECK(self->map()->EnumLength() == i::kInvalidEnumCacheSentinel ||
self->map()->EnumLength() == 0 ||
self->map()->instance_descriptors()->GetEnumCache() != *value);
@ -3901,19 +3913,8 @@ Local<Array> v8::Object::GetOwnPropertyNames() {
MaybeLocal<Array> v8::Object::GetOwnPropertyNames(Local<Context> context,
PropertyFilter filter) {
PREPARE_FOR_EXECUTION(context, Object, GetOwnPropertyNames, Array);
auto self = Utils::OpenHandle(this);
i::Handle<i::FixedArray> value;
has_pending_exception =
!i::KeyAccumulator::GetKeys(self, i::OWN_ONLY,
static_cast<i::PropertyFilter>(filter))
.ToHandle(&value);
RETURN_ON_FAILED_EXECUTION(Array);
DCHECK(self->map()->EnumLength() == i::kInvalidEnumCacheSentinel ||
self->map()->EnumLength() == 0 ||
self->map()->instance_descriptors()->GetEnumCache() != *value);
auto result = isolate->factory()->NewJSArrayWithElements(value);
RETURN_ESCAPED(Utils::ToLocal(result));
return GetPropertyNames(context, KeyCollectionMode::kOwnOnly, filter,
v8::IndexFilter::kIncludeIndices);
}
MaybeLocal<String> v8::Object::ObjectProtoToString(Local<Context> context) {

View File

@ -1641,8 +1641,9 @@ BUILTIN(ObjectAssign) {
// 4b ii. Let keys be ? from.[[OwnPropertyKeys]]().
Handle<FixedArray> keys;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, keys,
KeyAccumulator::GetKeys(from, OWN_ONLY, ALL_PROPERTIES, KEEP_NUMBERS));
isolate, keys, KeyAccumulator::GetKeys(
from, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES,
GetKeysConversion::kKeepNumbers));
// 4c. Repeat for each element nextKey of keys in List order,
for (int j = 0; j < keys->length(); ++j) {
Handle<Object> next_key(keys->get(j), isolate);
@ -1915,7 +1916,8 @@ Object* GetOwnPropertyKeys(Isolate* isolate,
Handle<FixedArray> keys;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, keys,
KeyAccumulator::GetKeys(receiver, OWN_ONLY, filter, CONVERT_TO_STRING));
KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, filter,
GetKeysConversion::kConvertToString));
return *isolate->factory()->NewJSArrayWithElements(keys);
}
@ -2011,8 +2013,9 @@ BUILTIN(ObjectKeys) {
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, keys,
KeyAccumulator::GetKeys(receiver, OWN_ONLY, ENUMERABLE_STRINGS,
CONVERT_TO_STRING));
KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS,
GetKeysConversion::kConvertToString));
}
return *isolate->factory()->NewJSArrayWithElements(keys, FAST_ELEMENTS);
}
@ -2054,8 +2057,9 @@ BUILTIN(ObjectGetOwnPropertyDescriptors) {
Handle<FixedArray> keys;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, keys, KeyAccumulator::GetKeys(receiver, OWN_ONLY, ALL_PROPERTIES,
CONVERT_TO_STRING));
isolate, keys, KeyAccumulator::GetKeys(
receiver, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES,
GetKeysConversion::kConvertToString));
Handle<JSObject> descriptors =
isolate->factory()->NewJSObject(isolate->object_function());
@ -2755,8 +2759,9 @@ BUILTIN(ReflectOwnKeys) {
Handle<FixedArray> keys;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, keys,
KeyAccumulator::GetKeys(Handle<JSReceiver>::cast(target), OWN_ONLY,
ALL_PROPERTIES, CONVERT_TO_STRING));
KeyAccumulator::GetKeys(Handle<JSReceiver>::cast(target),
KeyCollectionMode::kOwnOnly, ALL_PROPERTIES,
GetKeysConversion::kConvertToString));
return *isolate->factory()->NewJSArrayWithElements(keys);
}

View File

@ -494,7 +494,7 @@ MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
if (function_context->closure() == *function &&
!function_context->IsNativeContext()) {
CopyContextExtensionToScopeObject(function_context, local_scope,
INCLUDE_PROTOS);
KeyCollectionMode::kIncludePrototypes);
}
return local_scope;
@ -520,7 +520,8 @@ Handle<JSObject> ScopeIterator::MaterializeClosure() {
// Finally copy any properties from the function context extension. This will
// be variables introduced by eval.
CopyContextExtensionToScopeObject(context, closure_scope, OWN_ONLY);
CopyContextExtensionToScopeObject(context, closure_scope,
KeyCollectionMode::kOwnOnly);
return closure_scope;
}
@ -571,7 +572,8 @@ Handle<JSObject> ScopeIterator::MaterializeInnerScope() {
if (!context.is_null()) {
// Fill all context locals.
CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope);
CopyContextExtensionToScopeObject(context, inner_scope, OWN_ONLY);
CopyContextExtensionToScopeObject(context, inner_scope,
KeyCollectionMode::kOwnOnly);
}
return inner_scope;
}
@ -764,11 +766,11 @@ void ScopeIterator::CopyContextLocalsToScopeObject(
void ScopeIterator::CopyContextExtensionToScopeObject(
Handle<Context> context, Handle<JSObject> scope_object,
KeyCollectionType type) {
KeyCollectionMode mode) {
if (context->extension_object() == nullptr) return;
Handle<JSObject> extension(context->extension_object());
Handle<FixedArray> keys =
KeyAccumulator::GetKeys(extension, type, ENUMERABLE_STRINGS)
KeyAccumulator::GetKeys(extension, mode, ENUMERABLE_STRINGS)
.ToHandleChecked();
for (int i = 0; i < keys->length(); i++) {

View File

@ -153,7 +153,7 @@ class ScopeIterator {
Handle<JSObject> scope_object);
void CopyContextExtensionToScopeObject(Handle<Context> context,
Handle<JSObject> scope_object,
KeyCollectionType type);
KeyCollectionMode mode);
// Get the chain of nested scopes within this scope for the source statement
// position. The scopes will be added to the list from the outermost scope to

View File

@ -886,7 +886,8 @@ class ElementsAccessorBase : public ElementsAccessor {
Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
PropertyFilter filter) {
int count = 0;
KeyAccumulator accumulator(isolate, OWN_ONLY, ALL_PROPERTIES);
KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
ALL_PROPERTIES);
Subclass::CollectElementIndicesImpl(
object, handle(object->elements(), isolate), &accumulator);
Handle<FixedArray> keys = accumulator.GetKeys();
@ -950,7 +951,7 @@ class ElementsAccessorBase : public ElementsAccessor {
uint32_t length = Subclass::GetMaxIndex(*object, *backing_store);
for (uint32_t i = 0; i < length; i++) {
if (Subclass::HasElementImpl(object, i, backing_store, filter)) {
if (convert == CONVERT_TO_STRING) {
if (convert == GetKeysConversion::kConvertToString) {
Handle<String> index_string = isolate->factory()->Uint32ToString(i);
list->set(insertion_index, *index_string);
} else {
@ -996,7 +997,7 @@ class ElementsAccessorBase : public ElementsAccessor {
uint32_t array_length = 0;
// Indices from dictionary elements should only be converted after
// sorting.
if (convert == CONVERT_TO_STRING) {
if (convert == GetKeysConversion::kConvertToString) {
for (uint32_t i = 0; i < nof_indices; i++) {
Handle<Object> index_string = isolate->factory()->Uint32ToString(
combined_keys->get(i)->Number());
@ -2409,8 +2410,8 @@ class SloppyArgumentsElementsAccessor
Handle<FixedArray> indices = isolate->factory()->NewFixedArray(
GetCapacityImpl(*object, *backing_store));
DirectCollectElementIndicesImpl(isolate, object, backing_store,
KEEP_NUMBERS, ENUMERABLE_STRINGS, indices,
&nof_indices);
GetKeysConversion::kKeepNumbers,
ENUMERABLE_STRINGS, indices, &nof_indices);
SortIndices(indices, nof_indices);
for (uint32_t i = 0; i < nof_indices; i++) {
keys->AddKey(indices->get(i));
@ -2427,7 +2428,7 @@ class SloppyArgumentsElementsAccessor
for (uint32_t i = 0; i < length; ++i) {
if (parameter_map->get(i + 2)->IsTheHole()) continue;
if (convert == CONVERT_TO_STRING) {
if (convert == GetKeysConversion::kConvertToString) {
Handle<String> index_string = isolate->factory()->Uint32ToString(i);
list->set(insertion_index, *index_string);
} else {

View File

@ -533,7 +533,8 @@ JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow(
if (contents.is_null()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, contents,
KeyAccumulator::GetKeys(object, OWN_ONLY, ENUMERABLE_STRINGS),
KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS),
EXCEPTION);
}
builder_.AppendCharacter('{');

View File

@ -33,11 +33,11 @@ static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
} // namespace
MaybeHandle<FixedArray> KeyAccumulator::GetKeys(
Handle<JSReceiver> object, KeyCollectionType type, PropertyFilter filter,
Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in) {
USE(ContainsOnlyValidKeys);
Isolate* isolate = object->GetIsolate();
KeyAccumulator accumulator(isolate, type, filter);
KeyAccumulator accumulator(isolate, mode, filter);
accumulator.set_filter_proxy_keys(filter_proxy_keys);
accumulator.set_is_for_in(is_for_in);
MAYBE_RETURN(accumulator.CollectKeys(object, object),
@ -51,7 +51,7 @@ Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
if (keys_.is_null()) {
return isolate_->factory()->empty_fixed_array();
}
if (type_ == OWN_ONLY &&
if (mode_ == KeyCollectionMode::kOwnOnly &&
keys_->map() == isolate_->heap()->fixed_array_map()) {
return Handle<FixedArray>::cast(keys_);
}
@ -134,7 +134,7 @@ Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy,
isolate_, keys, FilterProxyKeys(isolate_, proxy, keys, filter_),
Nothing<bool>());
}
if (type_ == OWN_ONLY && !is_for_in_) {
if (mode_ == KeyCollectionMode::kOwnOnly && !is_for_in_) {
// If we collect only the keys from a JSProxy do not sort or deduplicate it.
keys_ = keys;
return Just(true);
@ -148,13 +148,13 @@ Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver,
// Proxies have no hidden prototype and we should not trigger the
// [[GetPrototypeOf]] trap on the last iteration when using
// AdvanceFollowingProxies.
if (type_ == OWN_ONLY && object->IsJSProxy()) {
if (mode_ == KeyCollectionMode::kOwnOnly && object->IsJSProxy()) {
MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)),
Nothing<bool>());
return Just(true);
}
PrototypeIterator::WhereToEnd end = type_ == OWN_ONLY
PrototypeIterator::WhereToEnd end = mode_ == KeyCollectionMode::kOwnOnly
? PrototypeIterator::END_AT_NON_HIDDEN
: PrototypeIterator::END_AT_NULL;
for (PrototypeIterator iter(isolate_, object,
@ -209,7 +209,7 @@ bool CheckAndInitalizeSimpleEnumCache(JSReceiver* object) {
void FastKeyAccumulator::Prepare() {
DisallowHeapAllocation no_gc;
// Directly go for the fast path for OWN_ONLY keys.
if (type_ == OWN_ONLY) return;
if (mode_ == KeyCollectionMode::kOwnOnly) return;
// Fully walk the prototype chain and find the last prototype with keys.
is_receiver_simple_enum_ = false;
has_empty_prototype_ = true;
@ -372,7 +372,7 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) {
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
GetKeysConversion convert) {
bool own_only = has_empty_prototype_ || type_ == OWN_ONLY;
bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly;
Map* map = receiver_->map();
if (!own_only || !OnlyHasSimpleProperties(map)) {
return MaybeHandle<FixedArray>();
@ -408,7 +408,8 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
GetKeysConversion convert) {
return KeyAccumulator::GetKeys(receiver_, type_, filter_, KEEP_NUMBERS,
return KeyAccumulator::GetKeys(receiver_, mode_, filter_,
GetKeysConversion::kKeepNumbers,
filter_proxy_keys_, is_for_in_);
}
@ -536,11 +537,11 @@ Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
!isolate_->MayAccess(handle(isolate_->context()), object)) {
// The cross-origin spec says that [[Enumerate]] shall return an empty
// iterator when it doesn't have access...
if (type_ == INCLUDE_PROTOS) {
if (mode_ == KeyCollectionMode::kIncludePrototypes) {
return Just(false);
}
// ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
DCHECK_EQ(OWN_ONLY, type_);
DCHECK(KeyCollectionMode::kOwnOnly == mode_);
filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ);
}
MAYBE_RETURN(CollectOwnElementIndices(receiver, object), Nothing<bool>());

View File

@ -31,16 +31,17 @@ enum AddKeyConversion { DO_NOT_CONVERT, CONVERT_TO_ARRAY_INDEX };
// are more compact and allow for reasonably fast includes check.
class KeyAccumulator final BASE_EMBEDDED {
public:
KeyAccumulator(Isolate* isolate, KeyCollectionType type,
KeyAccumulator(Isolate* isolate, KeyCollectionMode mode,
PropertyFilter filter)
: isolate_(isolate), type_(type), filter_(filter) {}
: isolate_(isolate), mode_(mode), filter_(filter) {}
~KeyAccumulator();
static MaybeHandle<FixedArray> GetKeys(
Handle<JSReceiver> object, KeyCollectionType type, PropertyFilter filter,
GetKeysConversion keys_conversion = KEEP_NUMBERS,
Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
GetKeysConversion keys_conversion = GetKeysConversion::kKeepNumbers,
bool filter_proxy_keys = true, bool is_for_in = false);
Handle<FixedArray> GetKeys(GetKeysConversion convert = KEEP_NUMBERS);
Handle<FixedArray> GetKeys(
GetKeysConversion convert = GetKeysConversion::kKeepNumbers);
Maybe<bool> CollectKeys(Handle<JSReceiver> receiver,
Handle<JSReceiver> object);
Maybe<bool> CollectOwnElementIndices(Handle<JSReceiver> receiver,
@ -81,7 +82,7 @@ class KeyAccumulator final BASE_EMBEDDED {
// keys_ is either an Handle<OrderedHashSet> or in the case of own JSProxy
// keys a Handle<FixedArray>.
Handle<FixedArray> keys_;
KeyCollectionType type_;
KeyCollectionMode mode_;
PropertyFilter filter_;
bool filter_proxy_keys_ = true;
bool is_for_in_ = false;
@ -96,8 +97,8 @@ class KeyAccumulator final BASE_EMBEDDED {
class FastKeyAccumulator {
public:
FastKeyAccumulator(Isolate* isolate, Handle<JSReceiver> receiver,
KeyCollectionType type, PropertyFilter filter)
: isolate_(isolate), receiver_(receiver), type_(type), filter_(filter) {
KeyCollectionMode mode, PropertyFilter filter)
: isolate_(isolate), receiver_(receiver), mode_(mode), filter_(filter) {
Prepare();
}
@ -106,7 +107,8 @@ class FastKeyAccumulator {
void set_filter_proxy_keys(bool filter) { filter_proxy_keys_ = filter; }
void set_is_for_in(bool value) { is_for_in_ = value; }
MaybeHandle<FixedArray> GetKeys(GetKeysConversion convert = KEEP_NUMBERS);
MaybeHandle<FixedArray> GetKeys(
GetKeysConversion convert = GetKeysConversion::kKeepNumbers);
private:
void Prepare();
@ -115,7 +117,7 @@ class FastKeyAccumulator {
Isolate* isolate_;
Handle<JSReceiver> receiver_;
KeyCollectionType type_;
KeyCollectionMode mode_;
PropertyFilter filter_;
bool filter_proxy_keys_ = true;
bool is_for_in_ = false;

View File

@ -1114,8 +1114,9 @@ MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate,
// static
MUST_USE_RESULT MaybeHandle<FixedArray> JSReceiver::OwnPropertyKeys(
Handle<JSReceiver> object) {
return KeyAccumulator::GetKeys(object, OWN_ONLY, ALL_PROPERTIES,
CONVERT_TO_STRING);
return KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
ALL_PROPERTIES,
GetKeysConversion::kConvertToString);
}
#define FIELD_ADDR(p, offset) \

View File

@ -6178,7 +6178,8 @@ MaybeHandle<Object> JSReceiver::DefineProperties(Isolate* isolate,
// 5. ReturnIfAbrupt(keys).
Handle<FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, keys, KeyAccumulator::GetKeys(props, OWN_ONLY, ALL_PROPERTIES),
isolate, keys, KeyAccumulator::GetKeys(props, KeyCollectionMode::kOwnOnly,
ALL_PROPERTIES),
Object);
// 6. Let descriptors be an empty List.
int capacity = keys->length();
@ -7856,7 +7857,7 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
// an array.
PropertyFilter filter = static_cast<PropertyFilter>(
ONLY_WRITABLE | ONLY_ENUMERABLE | ONLY_CONFIGURABLE);
KeyAccumulator accumulator(isolate, OWN_ONLY, filter);
KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly, filter);
accumulator.CollectOwnPropertyNames(copy, copy);
Handle<FixedArray> names = accumulator.GetKeys();
for (int i = 0; i < names->length(); i++) {
@ -8252,10 +8253,11 @@ MaybeHandle<FixedArray> GetOwnValuesOrEntries(Isolate* isolate,
PropertyFilter key_filter =
static_cast<PropertyFilter>(filter & ~ONLY_ENUMERABLE);
KeyAccumulator accumulator(isolate, OWN_ONLY, key_filter);
KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly, key_filter);
MAYBE_RETURN(accumulator.CollectKeys(object, object),
MaybeHandle<FixedArray>());
Handle<FixedArray> keys = accumulator.GetKeys(CONVERT_TO_STRING);
Handle<FixedArray> keys =
accumulator.GetKeys(GetKeysConversion::kConvertToString);
values_or_entries = isolate->factory()->NewFixedArray(keys->length());
int length = 0;
@ -17594,7 +17596,7 @@ Handle<FixedArray> OrderedHashSet::ConvertToKeysArray(
for (int i = 0; i < length; i++) {
int index = kHashTableStartIndex + nof_buckets + (i * kEntrySize);
Object* key = table->get(index);
if (convert == CONVERT_TO_STRING && key->IsNumber()) {
if (convert == GetKeysConversion::kConvertToString && key->IsNumber()) {
key = *isolate->factory()->NumberToString(handle(key, isolate));
}
result->set(i, key);

View File

@ -1799,10 +1799,13 @@ enum AccessorComponent {
ACCESSOR_SETTER
};
enum class GetKeysConversion { kKeepNumbers, kConvertToString };
enum GetKeysConversion { KEEP_NUMBERS, CONVERT_TO_STRING };
enum KeyCollectionType { OWN_ONLY, INCLUDE_PROTOS };
enum class KeyCollectionMode {
kOwnOnly = static_cast<int>(v8::KeyCollectionMode::kOwnOnly),
kIncludePrototypes =
static_cast<int>(v8::KeyCollectionMode::kIncludePrototypes)
};
// JSReceiver includes types on which properties can be defined, i.e.,
// JSObject and JSProxy.

View File

@ -199,7 +199,8 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
return *isolate->factory()->NewNumberFromUint(Min(actual_length, length));
}
KeyAccumulator accumulator(isolate, OWN_ONLY, ALL_PROPERTIES);
KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
ALL_PROPERTIES);
// No need to separate prototype levels since we only get element keys.
for (PrototypeIterator iter(isolate, array,
PrototypeIterator::START_AT_RECEIVER);
@ -215,7 +216,8 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
accumulator.CollectOwnElementIndices(array, current);
}
// Erase any keys >= length.
Handle<FixedArray> keys = accumulator.GetKeys(KEEP_NUMBERS);
Handle<FixedArray> keys =
accumulator.GetKeys(GetKeysConversion::kKeepNumbers);
int j = 0;
for (int i = 0; i < keys->length(); i++) {
if (NumberToUint32(keys->get(i)) >= length) continue;

View File

@ -22,15 +22,17 @@ namespace {
// deletions during a for-in.
MaybeHandle<HeapObject> Enumerate(Handle<JSReceiver> receiver) {
Isolate* const isolate = receiver->GetIsolate();
FastKeyAccumulator accumulator(isolate, receiver, INCLUDE_PROTOS,
FastKeyAccumulator accumulator(isolate, receiver,
KeyCollectionMode::kIncludePrototypes,
ENUMERABLE_STRINGS);
accumulator.set_filter_proxy_keys(false);
accumulator.set_is_for_in(true);
// Test if we have an enum cache for {receiver}.
if (!accumulator.is_receiver_simple_enum()) {
Handle<FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION(isolate, keys, accumulator.GetKeys(KEEP_NUMBERS),
HeapObject);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, keys, accumulator.GetKeys(GetKeysConversion::kKeepNumbers),
HeapObject);
// Test again, since cache may have been built by GetKeys() calls above.
if (!accumulator.is_receiver_simple_enum()) return keys;
}

View File

@ -563,7 +563,8 @@ RUNTIME_FUNCTION(Runtime_GetOwnPropertyKeys) {
Handle<FixedArray> keys;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, keys,
KeyAccumulator::GetKeys(object, OWN_ONLY, filter, CONVERT_TO_STRING));
KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, filter,
GetKeysConversion::kConvertToString));
return *isolate->factory()->NewJSArrayWithElements(keys);
}

View File

@ -15072,18 +15072,39 @@ THREADED_TEST(DateAccess) {
CHECK_EQ(1224744689038.0, date.As<v8::Date>()->ValueOf());
}
void CheckIsSymbolAt(v8::Isolate* isolate, v8::Local<v8::Array> properties,
unsigned index, const char* name) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Value> value =
properties->Get(context, v8::Integer::New(isolate, index))
.ToLocalChecked();
CHECK(value->IsSymbol());
v8::String::Utf8Value symbol_name(Local<Symbol>::Cast(value)->Name());
CHECK_EQ(0, strcmp(name, *symbol_name));
}
void CheckStringArray(v8::Isolate* isolate, v8::Local<v8::Array> properties,
unsigned length, const char* names[]) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
CHECK_EQ(length, properties->Length());
for (unsigned i = 0; i < length; i++) {
v8::Local<v8::Value> value =
properties->Get(context, v8::Integer::New(isolate, i)).ToLocalChecked();
if (names[i] == nullptr) {
DCHECK(value->IsSymbol());
} else {
v8::String::Utf8Value elm(value);
CHECK_EQ(0, strcmp(names[i], *elm));
}
}
}
void CheckProperties(v8::Isolate* isolate, v8::Local<v8::Value> val,
unsigned elmc, const char* elmv[]) {
unsigned length, const char* names[]) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> obj = val.As<v8::Object>();
v8::Local<v8::Array> props = obj->GetPropertyNames(context).ToLocalChecked();
CHECK_EQ(elmc, props->Length());
for (unsigned i = 0; i < elmc; i++) {
v8::String::Utf8Value elm(
props->Get(context, v8::Integer::New(isolate, i)).ToLocalChecked());
CHECK_EQ(0, strcmp(elmv[i], *elm));
}
CheckStringArray(isolate, props, length, names);
}
@ -15194,6 +15215,97 @@ THREADED_TEST(PropertyEnumeration2) {
}
}
THREADED_TEST(PropertyNames) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Value> result = CompileRun(
"var result = {0: 0, 1: 1, a: 2, b: 3};"
"result[Symbol('symbol')] = true;"
"result.__proto__ = {2: 4, 3: 5, c: 6, d: 7};"
"result;");
v8::Local<v8::Object> object = result.As<v8::Object>();
v8::PropertyFilter default_filter =
static_cast<v8::PropertyFilter>(v8::ONLY_ENUMERABLE | v8::SKIP_SYMBOLS);
v8::PropertyFilter include_symbols_filter = v8::ONLY_ENUMERABLE;
v8::Local<v8::Array> properties =
object->GetPropertyNames(context.local()).ToLocalChecked();
const char* expected_properties1[] = {"0", "1", "a", "b", "2", "3", "c", "d"};
CheckStringArray(isolate, properties, 8, expected_properties1);
properties =
object
->GetPropertyNames(context.local(),
v8::KeyCollectionMode::kIncludePrototypes,
default_filter, v8::IndexFilter::kIncludeIndices)
.ToLocalChecked();
CheckStringArray(isolate, properties, 8, expected_properties1);
properties = object
->GetPropertyNames(context.local(),
v8::KeyCollectionMode::kIncludePrototypes,
include_symbols_filter,
v8::IndexFilter::kIncludeIndices)
.ToLocalChecked();
const char* expected_properties1_1[] = {"0", "1", "a", "b", nullptr,
"2", "3", "c", "d"};
CheckStringArray(isolate, properties, 9, expected_properties1_1);
CheckIsSymbolAt(isolate, properties, 4, "symbol");
properties =
object
->GetPropertyNames(context.local(),
v8::KeyCollectionMode::kIncludePrototypes,
default_filter, v8::IndexFilter::kSkipIndices)
.ToLocalChecked();
const char* expected_properties2[] = {"a", "b", "c", "d"};
CheckStringArray(isolate, properties, 4, expected_properties2);
properties = object
->GetPropertyNames(context.local(),
v8::KeyCollectionMode::kIncludePrototypes,
include_symbols_filter,
v8::IndexFilter::kSkipIndices)
.ToLocalChecked();
const char* expected_properties2_1[] = {"a", "b", nullptr, "c", "d"};
CheckStringArray(isolate, properties, 5, expected_properties2_1);
CheckIsSymbolAt(isolate, properties, 2, "symbol");
properties =
object
->GetPropertyNames(context.local(), v8::KeyCollectionMode::kOwnOnly,
default_filter, v8::IndexFilter::kIncludeIndices)
.ToLocalChecked();
const char* expected_properties3[] = {"0", "1", "a", "b"};
CheckStringArray(isolate, properties, 4, expected_properties3);
properties = object
->GetPropertyNames(
context.local(), v8::KeyCollectionMode::kOwnOnly,
include_symbols_filter, v8::IndexFilter::kIncludeIndices)
.ToLocalChecked();
const char* expected_properties3_1[] = {"0", "1", "a", "b", nullptr};
CheckStringArray(isolate, properties, 5, expected_properties3_1);
CheckIsSymbolAt(isolate, properties, 4, "symbol");
properties =
object
->GetPropertyNames(context.local(), v8::KeyCollectionMode::kOwnOnly,
default_filter, v8::IndexFilter::kSkipIndices)
.ToLocalChecked();
const char* expected_properties4[] = {"a", "b"};
CheckStringArray(isolate, properties, 2, expected_properties4);
properties = object
->GetPropertyNames(
context.local(), v8::KeyCollectionMode::kOwnOnly,
include_symbols_filter, v8::IndexFilter::kSkipIndices)
.ToLocalChecked();
const char* expected_properties4_1[] = {"a", "b", nullptr};
CheckStringArray(isolate, properties, 3, expected_properties4_1);
CheckIsSymbolAt(isolate, properties, 2, "symbol");
}
THREADED_TEST(AccessChecksReenabledCorrectly) {
LocalContext context;