[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:
parent
07fadde87c
commit
63efe9e416
18
include/v8.h
18
include/v8.h
@ -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
|
||||
|
35
src/api.cc
35
src/api.cc
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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++) {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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('{');
|
||||
|
23
src/keys.cc
23
src/keys.cc
@ -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>());
|
||||
|
22
src/keys.h
22
src/keys.h
@ -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;
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user