Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(355)

Unified Diff: src/keys.cc

Issue 1938413002: [keys] Moving property/keys related methods to KeyAccumulator in keys.cc (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: nits Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/keys.h ('k') | src/messages.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/keys.cc
diff --git a/src/keys.cc b/src/keys.cc
index f8b606ca4bf1cd9b8ddd07ad1d0c75d9532ddb7b..2de68e6cdd862a7f4bba5dc0881095dfac73dfc4 100644
--- a/src/keys.cc
+++ b/src/keys.cc
@@ -4,8 +4,10 @@
#include "src/keys.h"
+#include "src/api-arguments.h"
#include "src/elements.h"
#include "src/factory.h"
+#include "src/identity-map.h"
#include "src/isolate-inl.h"
#include "src/objects-inl.h"
#include "src/property-descriptor.h"
@@ -312,6 +314,46 @@ void KeyAccumulator::NextPrototype() {
level_symbol_length_ = 0;
}
+Maybe<bool> KeyAccumulator::GetKeys_Internal(Handle<JSReceiver> receiver,
+ Handle<JSReceiver> object,
+ KeyCollectionType type) {
+ // 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()) {
+ MAYBE_RETURN(
+ JSProxyOwnPropertyKeys(receiver, Handle<JSProxy>::cast(object)),
+ Nothing<bool>());
+ return Just(true);
+ }
+
+ PrototypeIterator::WhereToEnd end = type == OWN_ONLY
+ ? PrototypeIterator::END_AT_NON_HIDDEN
+ : PrototypeIterator::END_AT_NULL;
+ for (PrototypeIterator iter(isolate_, object,
+ PrototypeIterator::START_AT_RECEIVER, end);
+ !iter.IsAtEnd();) {
+ Handle<JSReceiver> current =
+ PrototypeIterator::GetCurrent<JSReceiver>(iter);
+ Maybe<bool> result = Just(false); // Dummy initialization.
+ if (current->IsJSProxy()) {
+ result = JSProxyOwnPropertyKeys(receiver, Handle<JSProxy>::cast(current));
+ } else {
+ DCHECK(current->IsJSObject());
+ result =
+ GetKeysFromJSObject(receiver, Handle<JSObject>::cast(current), type);
+ }
+ MAYBE_RETURN(result, Nothing<bool>());
+ if (!result.FromJust()) break; // |false| means "stop iterating".
+ // Iterate through proxies but ignore access checks for the ALL_CAN_READ
+ // case on API objects for OWN_ONLY keys handled in GetKeysFromJSObject.
+ if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
+ return Nothing<bool>();
+ }
+ }
+ return Just(true);
+}
+
namespace {
void TrySettingEmptyEnumCache(JSReceiver* object) {
@@ -363,6 +405,89 @@ void FastKeyAccumulator::Prepare() {
}
namespace {
+static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
+ Handle<FixedArray> array,
+ int length) {
+ DCHECK_LE(length, array->length());
+ if (array->length() == length) return array;
+ return isolate->factory()->CopyFixedArrayUpTo(array, length);
+}
+
+Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
+ Handle<JSObject> object) {
+ Handle<Map> map(object->map());
+ bool cache_enum_length = map->OnlyHasSimpleProperties();
+
+ Handle<DescriptorArray> descs =
+ Handle<DescriptorArray>(map->instance_descriptors(), isolate);
+ int own_property_count = map->EnumLength();
+ // If the enum length of the given map is set to kInvalidEnumCache, this
+ // means that the map itself has never used the present enum cache. The
+ // first step to using the cache is to set the enum length of the map by
+ // counting the number of own descriptors that are ENUMERABLE_STRINGS.
+ if (own_property_count == kInvalidEnumCacheSentinel) {
+ own_property_count =
+ map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
+ } else {
+ DCHECK(
+ own_property_count ==
+ map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS));
+ }
+
+ if (descs->HasEnumCache()) {
+ Handle<FixedArray> keys(descs->GetEnumCache(), isolate);
+ // In case the number of properties required in the enum are actually
+ // present, we can reuse the enum cache. Otherwise, this means that the
+ // enum cache was generated for a previous (smaller) version of the
+ // Descriptor Array. In that case we regenerate the enum cache.
+ if (own_property_count <= keys->length()) {
+ isolate->counters()->enum_cache_hits()->Increment();
+ if (cache_enum_length) map->SetEnumLength(own_property_count);
+ return ReduceFixedArrayTo(isolate, keys, own_property_count);
+ }
+ }
+
+ if (descs->IsEmpty()) {
+ isolate->counters()->enum_cache_hits()->Increment();
+ if (cache_enum_length) map->SetEnumLength(0);
+ return isolate->factory()->empty_fixed_array();
+ }
+
+ isolate->counters()->enum_cache_misses()->Increment();
+
+ Handle<FixedArray> storage =
+ isolate->factory()->NewFixedArray(own_property_count);
+ Handle<FixedArray> indices =
+ isolate->factory()->NewFixedArray(own_property_count);
+
+ int size = map->NumberOfOwnDescriptors();
+ int index = 0;
+
+ for (int i = 0; i < size; i++) {
+ PropertyDetails details = descs->GetDetails(i);
+ if (details.IsDontEnum()) continue;
+ Object* key = descs->GetKey(i);
+ if (key->IsSymbol()) continue;
+ storage->set(index, key);
+ if (!indices.is_null()) {
+ if (details.type() != DATA) {
+ indices = Handle<FixedArray>();
+ } else {
+ FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
+ int load_by_field_index = field_index.GetLoadByFieldIndex();
+ indices->set(index, Smi::FromInt(load_by_field_index));
+ }
+ }
+ index++;
+ }
+ DCHECK(index == storage->length());
+
+ DescriptorArray::SetEnumCache(descs, isolate, storage, indices);
+ if (cache_enum_length) {
+ map->SetEnumLength(own_property_count);
+ }
+ return storage;
+}
template <bool fast_properties>
Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
@@ -371,10 +496,10 @@ Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
Handle<FixedArray> keys;
ElementsAccessor* accessor = object->GetElementsAccessor();
if (fast_properties) {
- keys = JSObject::GetFastEnumPropertyKeys(isolate, object);
+ keys = GetFastEnumPropertyKeys(isolate, object);
} else {
// TODO(cbruni): preallocate big enough array to also hold elements.
- keys = JSObject::GetEnumPropertyKeys(object);
+ keys = KeyAccumulator::GetEnumPropertyKeys(isolate, object);
}
Handle<FixedArray> result =
accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
@@ -402,7 +527,7 @@ MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache(
}
// We have no elements but possibly enumerable property keys, hence we can
// directly initialize the enum cache.
- return JSObject::GetFastEnumPropertyKeys(isolate, object);
+ return GetFastEnumPropertyKeys(isolate, object);
}
bool OnlyHasSimpleProperties(Map* map) {
@@ -461,5 +586,297 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
filter_proxy_keys_);
}
+enum IndexedOrNamed { kIndexed, kNamed };
+
+// Returns |true| on success, |nothing| on exception.
+template <class Callback, IndexedOrNamed type>
+static Maybe<bool> GetKeysFromInterceptor(Handle<JSReceiver> receiver,
+ Handle<JSObject> object,
+ KeyAccumulator* accumulator) {
+ Isolate* isolate = accumulator->isolate();
+ if (type == kIndexed) {
+ if (!object->HasIndexedInterceptor()) return Just(true);
+ } else {
+ if (!object->HasNamedInterceptor()) return Just(true);
+ }
+ Handle<InterceptorInfo> interceptor(type == kIndexed
+ ? object->GetIndexedInterceptor()
+ : object->GetNamedInterceptor(),
+ isolate);
+ if ((accumulator->filter() & ONLY_ALL_CAN_READ) &&
+ !interceptor->all_can_read()) {
+ return Just(true);
+ }
+ PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
+ *object, Object::DONT_THROW);
+ Handle<JSObject> result;
+ if (!interceptor->enumerator()->IsUndefined()) {
+ Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator());
+ const char* log_tag = type == kIndexed ? "interceptor-indexed-enum"
+ : "interceptor-named-enum";
+ LOG(isolate, ApiObjectAccess(log_tag, *object));
+ result = args.Call(enum_fun);
+ }
+ RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+ if (result.is_null()) return Just(true);
+ DCHECK(result->IsJSArray() || result->HasSloppyArgumentsElements());
+ // The accumulator takes care of string/symbol filtering.
+ if (type == kIndexed) {
+ accumulator->AddElementKeysFromInterceptor(result);
+ } else {
+ accumulator->AddKeys(result, DO_NOT_CONVERT);
+ }
+ return Just(true);
+}
+
+void KeyAccumulator::CollectOwnElementKeys(Handle<JSObject> object) {
+ if (filter_ & SKIP_STRINGS) return;
+ ElementsAccessor* accessor = object->GetElementsAccessor();
+ accessor->CollectElementIndices(object, this, kMaxUInt32, filter_, 0);
+}
+
+void KeyAccumulator::CollectOwnPropertyNames(Handle<JSObject> object) {
+ if (object->HasFastProperties()) {
+ int real_size = object->map()->NumberOfOwnDescriptors();
+ Handle<DescriptorArray> descs(object->map()->instance_descriptors(),
+ isolate_);
+ for (int i = 0; i < real_size; i++) {
+ PropertyDetails details = descs->GetDetails(i);
+ if ((details.attributes() & filter_) != 0) continue;
+ if (filter_ & ONLY_ALL_CAN_READ) {
+ if (details.kind() != kAccessor) continue;
+ Object* accessors = descs->GetValue(i);
+ if (!accessors->IsAccessorInfo()) continue;
+ if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
+ }
+ Name* key = descs->GetKey(i);
+ if (key->FilterKey(filter_)) continue;
+ this->AddKey(key, DO_NOT_CONVERT);
+ }
+ } else if (object->IsJSGlobalObject()) {
+ GlobalDictionary::CollectKeysTo(
+ handle(object->global_dictionary(), isolate_), this, filter_);
+ } else {
+ NameDictionary::CollectKeysTo(
+ handle(object->property_dictionary(), isolate_), this, filter_);
+ }
+}
+
+// Returns |true| on success, |false| if prototype walking should be stopped,
+// |nothing| if an exception was thrown.
+Maybe<bool> KeyAccumulator::GetKeysFromJSObject(Handle<JSReceiver> receiver,
+ Handle<JSObject> object,
+ KeyCollectionType type) {
+ this->NextPrototype();
+ // Check access rights if required.
+ if (object->IsAccessCheckNeeded() &&
+ !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) {
+ return Just(false);
+ }
+ // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
+ DCHECK_EQ(OWN_ONLY, type);
+ filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ);
+ }
+
+ this->CollectOwnElementKeys(object);
+
+ // Add the element keys from the interceptor.
+ Maybe<bool> success =
+ GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, kIndexed>(
+ receiver, object, this);
+ MAYBE_RETURN(success, Nothing<bool>());
+
+ if (filter_ == ENUMERABLE_STRINGS) {
+ Handle<FixedArray> enum_keys =
+ KeyAccumulator::GetEnumPropertyKeys(isolate_, object);
+ this->AddKeys(enum_keys, DO_NOT_CONVERT);
+ } else {
+ this->CollectOwnPropertyNames(object);
+ }
+
+ // Add the property keys from the interceptor.
+ success = GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback,
+ kNamed>(receiver, object, this);
+ MAYBE_RETURN(success, Nothing<bool>());
+ return Just(true);
+}
+
+// static
+Handle<FixedArray> KeyAccumulator::GetEnumPropertyKeys(
+ Isolate* isolate, Handle<JSObject> object) {
+ if (object->HasFastProperties()) {
+ return GetFastEnumPropertyKeys(isolate, object);
+ } else if (object->IsJSGlobalObject()) {
+ Handle<GlobalDictionary> dictionary(object->global_dictionary(), isolate);
+ int length = dictionary->NumberOfEnumElements();
+ if (length == 0) {
+ return isolate->factory()->empty_fixed_array();
+ }
+ Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
+ dictionary->CopyEnumKeysTo(*storage);
+ return storage;
+ } else {
+ Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
+ int length = dictionary->NumberOfEnumElements();
+ if (length == 0) {
+ return isolate->factory()->empty_fixed_array();
+ }
+ Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
+ dictionary->CopyEnumKeysTo(*storage);
+ return storage;
+ }
+}
+
+// ES6 9.5.12
+// Returns |true| on success, |nothing| in case of exception.
+Maybe<bool> KeyAccumulator::JSProxyOwnPropertyKeys(Handle<JSReceiver> receiver,
+ Handle<JSProxy> proxy) {
+ STACK_CHECK(isolate_, Nothing<bool>());
+ // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+ Handle<Object> handler(proxy->handler(), isolate_);
+ // 2. If handler is null, throw a TypeError exception.
+ // 3. Assert: Type(handler) is Object.
+ if (proxy->IsRevoked()) {
+ isolate_->Throw(*isolate_->factory()->NewTypeError(
+ MessageTemplate::kProxyRevoked, isolate_->factory()->ownKeys_string()));
+ return Nothing<bool>();
+ }
+ // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
+ Handle<JSReceiver> target(proxy->target(), isolate_);
+ // 5. Let trap be ? GetMethod(handler, "ownKeys").
+ Handle<Object> trap;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
+ isolate_->factory()->ownKeys_string()),
+ Nothing<bool>());
+ // 6. If trap is undefined, then
+ if (trap->IsUndefined()) {
+ // 6a. Return target.[[OwnPropertyKeys]]().
+ return this->GetKeys_Internal(receiver, target, OWN_ONLY);
+ }
+ // 7. Let trapResultArray be Call(trap, handler, «target»).
+ Handle<Object> trap_result_array;
+ Handle<Object> args[] = {target};
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, trap_result_array,
+ Execution::Call(isolate_, trap, handler, arraysize(args), args),
+ Nothing<bool>());
+ // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray,
+ // «String, Symbol»).
+ Handle<FixedArray> trap_result;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, trap_result,
+ Object::CreateListFromArrayLike(isolate_, trap_result_array,
+ ElementTypes::kStringAndSymbol),
+ Nothing<bool>());
+ // 9. Let extensibleTarget be ? IsExtensible(target).
+ Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target);
+ MAYBE_RETURN(maybe_extensible, Nothing<bool>());
+ bool extensible_target = maybe_extensible.FromJust();
+ // 10. Let targetKeys be ? target.[[OwnPropertyKeys]]().
+ Handle<FixedArray> target_keys;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, target_keys,
+ JSReceiver::OwnPropertyKeys(target),
+ Nothing<bool>());
+ // 11. (Assert)
+ // 12. Let targetConfigurableKeys be an empty List.
+ // To save memory, we're re-using target_keys and will modify it in-place.
+ Handle<FixedArray> target_configurable_keys = target_keys;
+ // 13. Let targetNonconfigurableKeys be an empty List.
+ Handle<FixedArray> target_nonconfigurable_keys =
+ isolate_->factory()->NewFixedArray(target_keys->length());
+ int nonconfigurable_keys_length = 0;
+ // 14. Repeat, for each element key of targetKeys:
+ for (int i = 0; i < target_keys->length(); ++i) {
+ // 14a. Let desc be ? target.[[GetOwnProperty]](key).
+ PropertyDescriptor desc;
+ Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor(
+ isolate_, target, handle(target_keys->get(i), isolate_), &desc);
+ MAYBE_RETURN(found, Nothing<bool>());
+ // 14b. If desc is not undefined and desc.[[Configurable]] is false, then
+ if (found.FromJust() && !desc.configurable()) {
+ // 14b i. Append key as an element of targetNonconfigurableKeys.
+ target_nonconfigurable_keys->set(nonconfigurable_keys_length,
+ target_keys->get(i));
+ nonconfigurable_keys_length++;
+ // The key was moved, null it out in the original list.
+ target_keys->set(i, Smi::FromInt(0));
+ } else {
+ // 14c. Else,
+ // 14c i. Append key as an element of targetConfigurableKeys.
+ // (No-op, just keep it in |target_keys|.)
+ }
+ }
+ this->NextPrototype(); // Prepare for accumulating keys.
+ // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty,
+ // then:
+ if (extensible_target && nonconfigurable_keys_length == 0) {
+ // 15a. Return trapResult.
+ return this->AddKeysFromProxy(proxy, trap_result);
+ }
+ // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult.
+ Zone set_zone(isolate_->allocator());
+ const int kPresent = 1;
+ const int kGone = 0;
+ IdentityMap<int> unchecked_result_keys(isolate_->heap(), &set_zone);
+ int unchecked_result_keys_size = 0;
+ for (int i = 0; i < trap_result->length(); ++i) {
+ DCHECK(trap_result->get(i)->IsUniqueName());
+ Object* key = trap_result->get(i);
+ int* entry = unchecked_result_keys.Get(key);
+ if (*entry != kPresent) {
+ *entry = kPresent;
+ unchecked_result_keys_size++;
+ }
+ }
+ // 17. Repeat, for each key that is an element of targetNonconfigurableKeys:
+ for (int i = 0; i < nonconfigurable_keys_length; ++i) {
+ Object* key = target_nonconfigurable_keys->get(i);
+ // 17a. If key is not an element of uncheckedResultKeys, throw a
+ // TypeError exception.
+ int* found = unchecked_result_keys.Find(key);
+ if (found == nullptr || *found == kGone) {
+ isolate_->Throw(*isolate_->factory()->NewTypeError(
+ MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate_)));
+ return Nothing<bool>();
+ }
+ // 17b. Remove key from uncheckedResultKeys.
+ *found = kGone;
+ unchecked_result_keys_size--;
+ }
+ // 18. If extensibleTarget is true, return trapResult.
+ if (extensible_target) {
+ return this->AddKeysFromProxy(proxy, trap_result);
+ }
+ // 19. Repeat, for each key that is an element of targetConfigurableKeys:
+ for (int i = 0; i < target_configurable_keys->length(); ++i) {
+ Object* key = target_configurable_keys->get(i);
+ if (key->IsSmi()) continue; // Zapped entry, was nonconfigurable.
+ // 19a. If key is not an element of uncheckedResultKeys, throw a
+ // TypeError exception.
+ int* found = unchecked_result_keys.Find(key);
+ if (found == nullptr || *found == kGone) {
+ isolate_->Throw(*isolate_->factory()->NewTypeError(
+ MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate_)));
+ return Nothing<bool>();
+ }
+ // 19b. Remove key from uncheckedResultKeys.
+ *found = kGone;
+ unchecked_result_keys_size--;
+ }
+ // 20. If uncheckedResultKeys is not empty, throw a TypeError exception.
+ if (unchecked_result_keys_size != 0) {
+ DCHECK_GT(unchecked_result_keys_size, 0);
+ isolate_->Throw(*isolate_->factory()->NewTypeError(
+ MessageTemplate::kProxyOwnKeysNonExtensible));
+ return Nothing<bool>();
+ }
+ // 21. Return trapResult.
+ return this->AddKeysFromProxy(proxy, trap_result);
+}
+
} // namespace internal
} // namespace v8
« no previous file with comments | « src/keys.h ('k') | src/messages.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698