| OLD | NEW |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | 1 // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/keys.h" | 5 #include "src/keys.h" |
| 6 | 6 |
| 7 #include "src/api-arguments.h" | 7 #include "src/api-arguments.h" |
| 8 #include "src/elements.h" | 8 #include "src/elements.h" |
| 9 #include "src/factory.h" | 9 #include "src/factory.h" |
| 10 #include "src/identity-map.h" | 10 #include "src/identity-map.h" |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { | 24 static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { |
| 25 int len = array->length(); | 25 int len = array->length(); |
| 26 for (int i = 0; i < len; i++) { | 26 for (int i = 0; i < len; i++) { |
| 27 Object* e = array->get(i); | 27 Object* e = array->get(i); |
| 28 if (!(e->IsName() || e->IsNumber())) return false; | 28 if (!(e->IsName() || e->IsNumber())) return false; |
| 29 } | 29 } |
| 30 return true; | 30 return true; |
| 31 } | 31 } |
| 32 | 32 |
| 33 } // namespace | 33 } // namespace |
| 34 | |
| 35 MaybeHandle<FixedArray> KeyAccumulator::GetKeys( | 34 MaybeHandle<FixedArray> KeyAccumulator::GetKeys( |
| 36 Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter, | 35 Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter, |
| 37 GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in) { | 36 GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in) { |
| 38 USE(ContainsOnlyValidKeys); | |
| 39 Isolate* isolate = object->GetIsolate(); | 37 Isolate* isolate = object->GetIsolate(); |
| 40 KeyAccumulator accumulator(isolate, mode, filter); | 38 KeyAccumulator accumulator(isolate, mode, filter); |
| 41 accumulator.set_filter_proxy_keys(filter_proxy_keys); | 39 accumulator.set_filter_proxy_keys(filter_proxy_keys); |
| 42 accumulator.set_is_for_in(is_for_in); | 40 accumulator.set_is_for_in(is_for_in); |
| 43 MAYBE_RETURN(accumulator.CollectKeys(object, object), | 41 MAYBE_RETURN(accumulator.CollectKeys(object, object), |
| 44 MaybeHandle<FixedArray>()); | 42 MaybeHandle<FixedArray>()); |
| 45 Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion); | 43 return accumulator.GetKeys(keys_conversion); |
| 46 DCHECK(ContainsOnlyValidKeys(keys)); | |
| 47 return keys; | |
| 48 } | 44 } |
| 49 | 45 |
| 50 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) { | 46 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) { |
| 51 if (keys_.is_null()) { | 47 if (keys_.is_null()) { |
| 52 return isolate_->factory()->empty_fixed_array(); | 48 return isolate_->factory()->empty_fixed_array(); |
| 53 } | 49 } |
| 54 if (mode_ == KeyCollectionMode::kOwnOnly && | 50 if (mode_ == KeyCollectionMode::kOwnOnly && |
| 55 keys_->map() == isolate_->heap()->fixed_array_map()) { | 51 keys_->map() == isolate_->heap()->fixed_array_map()) { |
| 56 return Handle<FixedArray>::cast(keys_); | 52 return Handle<FixedArray>::cast(keys_); |
| 57 } | 53 } |
| 58 return OrderedHashSet::ConvertToKeysArray(keys(), convert); | 54 USE(ContainsOnlyValidKeys); |
| 55 Handle<FixedArray> result = |
| 56 OrderedHashSet::ConvertToKeysArray(keys(), convert); |
| 57 DCHECK(ContainsOnlyValidKeys(result)); |
| 58 return result; |
| 59 } | 59 } |
| 60 | 60 |
| 61 void KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) { | 61 void KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) { |
| 62 AddKey(handle(key, isolate_), convert); | 62 AddKey(handle(key, isolate_), convert); |
| 63 } | 63 } |
| 64 | 64 |
| 65 void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) { | 65 void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) { |
| 66 if (key->IsSymbol()) { | 66 if (key->IsSymbol()) { |
| 67 if (filter_ & SKIP_SYMBOLS) return; | 67 if (filter_ & SKIP_SYMBOLS) return; |
| 68 if (Handle<Symbol>::cast(key)->is_private()) return; | 68 if (Handle<Symbol>::cast(key)->is_private()) return; |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 DCHECK(current->IsJSObject()); | 169 DCHECK(current->IsJSObject()); |
| 170 result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current)); | 170 result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current)); |
| 171 } | 171 } |
| 172 MAYBE_RETURN(result, Nothing<bool>()); | 172 MAYBE_RETURN(result, Nothing<bool>()); |
| 173 if (!result.FromJust()) break; // |false| means "stop iterating". | 173 if (!result.FromJust()) break; // |false| means "stop iterating". |
| 174 // Iterate through proxies but ignore access checks for the ALL_CAN_READ | 174 // Iterate through proxies but ignore access checks for the ALL_CAN_READ |
| 175 // case on API objects for OWN_ONLY keys handled in CollectOwnKeys. | 175 // case on API objects for OWN_ONLY keys handled in CollectOwnKeys. |
| 176 if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) { | 176 if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) { |
| 177 return Nothing<bool>(); | 177 return Nothing<bool>(); |
| 178 } | 178 } |
| 179 if (!last_non_empty_prototype_.is_null() && |
| 180 *last_non_empty_prototype_ == *current) { |
| 181 break; |
| 182 } |
| 179 } | 183 } |
| 180 return Just(true); | 184 return Just(true); |
| 181 } | 185 } |
| 182 | 186 |
| 183 namespace { | 187 namespace { |
| 184 | 188 |
| 185 void TrySettingEmptyEnumCache(JSReceiver* object) { | 189 void TrySettingEmptyEnumCache(JSReceiver* object) { |
| 186 Map* map = object->map(); | 190 Map* map = object->map(); |
| 187 DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength()); | 191 DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength()); |
| 188 if (!map->OnlyHasSimpleProperties()) return; | 192 if (!map->OnlyHasSimpleProperties()) return; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 206 } | 210 } |
| 207 } // namespace | 211 } // namespace |
| 208 | 212 |
| 209 void FastKeyAccumulator::Prepare() { | 213 void FastKeyAccumulator::Prepare() { |
| 210 DisallowHeapAllocation no_gc; | 214 DisallowHeapAllocation no_gc; |
| 211 // Directly go for the fast path for OWN_ONLY keys. | 215 // Directly go for the fast path for OWN_ONLY keys. |
| 212 if (mode_ == KeyCollectionMode::kOwnOnly) return; | 216 if (mode_ == KeyCollectionMode::kOwnOnly) return; |
| 213 // Fully walk the prototype chain and find the last prototype with keys. | 217 // Fully walk the prototype chain and find the last prototype with keys. |
| 214 is_receiver_simple_enum_ = false; | 218 is_receiver_simple_enum_ = false; |
| 215 has_empty_prototype_ = true; | 219 has_empty_prototype_ = true; |
| 216 JSReceiver* first_non_empty_prototype; | 220 JSReceiver* last_prototype = nullptr; |
| 217 for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd(); | 221 for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd(); |
| 218 iter.Advance()) { | 222 iter.Advance()) { |
| 219 JSReceiver* current = iter.GetCurrent<JSReceiver>(); | 223 JSReceiver* current = iter.GetCurrent<JSReceiver>(); |
| 220 if (CheckAndInitalizeSimpleEnumCache(current)) continue; | 224 bool has_no_properties = CheckAndInitalizeSimpleEnumCache(current); |
| 225 if (has_no_properties) continue; |
| 226 last_prototype = current; |
| 221 has_empty_prototype_ = false; | 227 has_empty_prototype_ = false; |
| 222 first_non_empty_prototype = current; | |
| 223 // TODO(cbruni): use the first non-empty prototype. | |
| 224 USE(first_non_empty_prototype); | |
| 225 return; | |
| 226 } | 228 } |
| 227 DCHECK(has_empty_prototype_); | 229 if (has_empty_prototype_) { |
| 228 is_receiver_simple_enum_ = | 230 is_receiver_simple_enum_ = |
| 229 receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && | 231 receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && |
| 230 !JSObject::cast(*receiver_)->HasEnumerableElements(); | 232 !JSObject::cast(*receiver_)->HasEnumerableElements(); |
| 233 } else if (last_prototype != nullptr) { |
| 234 last_non_empty_prototype_ = handle(last_prototype, isolate_); |
| 235 } |
| 231 } | 236 } |
| 232 | 237 |
| 233 namespace { | 238 namespace { |
| 234 static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate, | 239 static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate, |
| 235 Handle<FixedArray> array, | 240 Handle<FixedArray> array, |
| 236 int length) { | 241 int length) { |
| 237 DCHECK_LE(length, array->length()); | 242 DCHECK_LE(length, array->length()); |
| 238 if (array->length() == length) return array; | 243 if (array->length() == length) return array; |
| 239 return isolate->factory()->CopyFixedArrayUpTo(array, length); | 244 return isolate->factory()->CopyFixedArrayUpTo(array, length); |
| 240 } | 245 } |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 355 // directly initialize the enum cache. | 360 // directly initialize the enum cache. |
| 356 return GetFastEnumPropertyKeys(isolate, object); | 361 return GetFastEnumPropertyKeys(isolate, object); |
| 357 } | 362 } |
| 358 | 363 |
| 359 bool OnlyHasSimpleProperties(Map* map) { | 364 bool OnlyHasSimpleProperties(Map* map) { |
| 360 return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; | 365 return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; |
| 361 } | 366 } |
| 362 | 367 |
| 363 } // namespace | 368 } // namespace |
| 364 | 369 |
| 365 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) { | 370 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys( |
| 371 GetKeysConversion keys_conversion) { |
| 366 Handle<FixedArray> keys; | 372 Handle<FixedArray> keys; |
| 367 if (GetKeysFast(convert).ToHandle(&keys)) { | 373 if (filter_ == ENUMERABLE_STRINGS && |
| 374 GetKeysFast(keys_conversion).ToHandle(&keys)) { |
| 368 return keys; | 375 return keys; |
| 369 } | 376 } |
| 370 return GetKeysSlow(convert); | 377 return GetKeysSlow(keys_conversion); |
| 371 } | 378 } |
| 372 | 379 |
| 373 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast( | 380 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast( |
| 374 GetKeysConversion convert) { | 381 GetKeysConversion keys_conversion) { |
| 375 bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly; | 382 bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly; |
| 376 Map* map = receiver_->map(); | 383 Map* map = receiver_->map(); |
| 377 if (!own_only || !OnlyHasSimpleProperties(map)) { | 384 if (!own_only || !OnlyHasSimpleProperties(map)) { |
| 378 return MaybeHandle<FixedArray>(); | 385 return MaybeHandle<FixedArray>(); |
| 379 } | 386 } |
| 380 | 387 |
| 381 // From this point on we are certiain to only collect own keys. | 388 // From this point on we are certiain to only collect own keys. |
| 382 DCHECK(receiver_->IsJSObject()); | 389 DCHECK(receiver_->IsJSObject()); |
| 383 Handle<JSObject> object = Handle<JSObject>::cast(receiver_); | 390 Handle<JSObject> object = Handle<JSObject>::cast(receiver_); |
| 384 | 391 |
| 385 // Do not try to use the enum-cache for dict-mode objects. | 392 // Do not try to use the enum-cache for dict-mode objects. |
| 386 if (map->is_dictionary_map()) { | 393 if (map->is_dictionary_map()) { |
| 387 return GetOwnKeysWithElements<false>(isolate_, object, convert); | 394 return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion); |
| 388 } | 395 } |
| 389 int enum_length = receiver_->map()->EnumLength(); | 396 int enum_length = receiver_->map()->EnumLength(); |
| 390 if (enum_length == kInvalidEnumCacheSentinel) { | 397 if (enum_length == kInvalidEnumCacheSentinel) { |
| 391 Handle<FixedArray> keys; | 398 Handle<FixedArray> keys; |
| 392 // Try initializing the enum cache and return own properties. | 399 // Try initializing the enum cache and return own properties. |
| 393 if (GetOwnKeysWithUninitializedEnumCache(isolate_, object) | 400 if (GetOwnKeysWithUninitializedEnumCache(isolate_, object) |
| 394 .ToHandle(&keys)) { | 401 .ToHandle(&keys)) { |
| 395 if (FLAG_trace_for_in_enumerate) { | 402 if (FLAG_trace_for_in_enumerate) { |
| 396 PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n", | 403 PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n", |
| 397 keys->length()); | 404 keys->length()); |
| 398 } | 405 } |
| 399 is_receiver_simple_enum_ = | 406 is_receiver_simple_enum_ = |
| 400 object->map()->EnumLength() != kInvalidEnumCacheSentinel; | 407 object->map()->EnumLength() != kInvalidEnumCacheSentinel; |
| 401 return keys; | 408 return keys; |
| 402 } | 409 } |
| 403 } | 410 } |
| 404 // The properties-only case failed because there were probably elements on the | 411 // The properties-only case failed because there were probably elements on the |
| 405 // receiver. | 412 // receiver. |
| 406 return GetOwnKeysWithElements<true>(isolate_, object, convert); | 413 return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion); |
| 407 } | 414 } |
| 408 | 415 |
| 409 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( | 416 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( |
| 410 GetKeysConversion convert) { | 417 GetKeysConversion keys_conversion) { |
| 411 return KeyAccumulator::GetKeys(receiver_, mode_, filter_, | 418 KeyAccumulator accumulator(isolate_, mode_, filter_); |
| 412 GetKeysConversion::kKeepNumbers, | 419 accumulator.set_filter_proxy_keys(filter_proxy_keys_); |
| 413 filter_proxy_keys_, is_for_in_); | 420 accumulator.set_is_for_in(is_for_in_); |
| 421 accumulator.set_last_non_empty_prototype(last_non_empty_prototype_); |
| 422 |
| 423 MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_), |
| 424 MaybeHandle<FixedArray>()); |
| 425 return accumulator.GetKeys(keys_conversion); |
| 414 } | 426 } |
| 415 | 427 |
| 416 namespace { | 428 namespace { |
| 417 | 429 |
| 418 enum IndexedOrNamed { kIndexed, kNamed }; | 430 enum IndexedOrNamed { kIndexed, kNamed }; |
| 419 | 431 |
| 420 // Returns |true| on success, |nothing| on exception. | 432 // Returns |true| on success, |nothing| on exception. |
| 421 template <class Callback, IndexedOrNamed type> | 433 template <class Callback, IndexedOrNamed type> |
| 422 Maybe<bool> GetKeysFromInterceptor(Handle<JSReceiver> receiver, | 434 Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver, |
| 423 Handle<JSObject> object, | 435 Handle<JSObject> object, |
| 424 KeyAccumulator* accumulator) { | 436 KeyAccumulator* accumulator) { |
| 425 Isolate* isolate = accumulator->isolate(); | 437 Isolate* isolate = accumulator->isolate(); |
| 426 if (type == kIndexed) { | 438 if (type == kIndexed) { |
| 427 if (!object->HasIndexedInterceptor()) return Just(true); | 439 if (!object->HasIndexedInterceptor()) return Just(true); |
| 428 } else { | 440 } else { |
| 429 if (!object->HasNamedInterceptor()) return Just(true); | 441 if (!object->HasNamedInterceptor()) return Just(true); |
| 430 } | 442 } |
| 431 Handle<InterceptorInfo> interceptor(type == kIndexed | 443 Handle<InterceptorInfo> interceptor(type == kIndexed |
| 432 ? object->GetIndexedInterceptor() | 444 ? object->GetIndexedInterceptor() |
| (...skipping 22 matching lines...) Expand all Loading... |
| 455 | 467 |
| 456 } // namespace | 468 } // namespace |
| 457 | 469 |
| 458 Maybe<bool> KeyAccumulator::CollectOwnElementIndices( | 470 Maybe<bool> KeyAccumulator::CollectOwnElementIndices( |
| 459 Handle<JSReceiver> receiver, Handle<JSObject> object) { | 471 Handle<JSReceiver> receiver, Handle<JSObject> object) { |
| 460 if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true); | 472 if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true); |
| 461 | 473 |
| 462 ElementsAccessor* accessor = object->GetElementsAccessor(); | 474 ElementsAccessor* accessor = object->GetElementsAccessor(); |
| 463 accessor->CollectElementIndices(object, this); | 475 accessor->CollectElementIndices(object, this); |
| 464 | 476 |
| 465 return GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, | 477 return CollectInterceptorKeys<v8::IndexedPropertyEnumeratorCallback, |
| 466 kIndexed>(receiver, object, this); | 478 kIndexed>(receiver, object, this); |
| 467 } | 479 } |
| 468 | 480 |
| 469 namespace { | 481 namespace { |
| 470 | 482 |
| 471 template <bool skip_symbols> | 483 template <bool skip_symbols> |
| 472 int CollectOwnPropertyNamesInternal(Handle<JSObject> object, | 484 int CollectOwnPropertyNamesInternal(Handle<JSObject> object, |
| 473 KeyAccumulator* keys, | 485 KeyAccumulator* keys, |
| 474 Handle<DescriptorArray> descs, | 486 Handle<DescriptorArray> descs, |
| 475 int start_index, int limit) { | 487 int start_index, int limit) { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 517 } | 529 } |
| 518 } else if (object->IsJSGlobalObject()) { | 530 } else if (object->IsJSGlobalObject()) { |
| 519 GlobalDictionary::CollectKeysTo( | 531 GlobalDictionary::CollectKeysTo( |
| 520 handle(object->global_dictionary(), isolate_), this, filter_); | 532 handle(object->global_dictionary(), isolate_), this, filter_); |
| 521 } else { | 533 } else { |
| 522 NameDictionary::CollectKeysTo( | 534 NameDictionary::CollectKeysTo( |
| 523 handle(object->property_dictionary(), isolate_), this, filter_); | 535 handle(object->property_dictionary(), isolate_), this, filter_); |
| 524 } | 536 } |
| 525 } | 537 } |
| 526 // Add the property keys from the interceptor. | 538 // Add the property keys from the interceptor. |
| 527 return GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback, | 539 return CollectInterceptorKeys<v8::GenericNamedPropertyEnumeratorCallback, |
| 528 kNamed>(receiver, object, this); | 540 kNamed>(receiver, object, this); |
| 529 } | 541 } |
| 530 | 542 |
| 531 // Returns |true| on success, |false| if prototype walking should be stopped, | 543 // Returns |true| on success, |false| if prototype walking should be stopped, |
| 532 // |nothing| if an exception was thrown. | 544 // |nothing| if an exception was thrown. |
| 533 Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver, | 545 Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver, |
| 534 Handle<JSObject> object) { | 546 Handle<JSObject> object) { |
| 535 // Check access rights if required. | 547 // Check access rights if required. |
| 536 if (object->IsAccessCheckNeeded() && | 548 if (object->IsAccessCheckNeeded() && |
| 537 !isolate_->MayAccess(handle(isolate_->context()), object)) { | 549 !isolate_->MayAccess(handle(isolate_->context()), object)) { |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 730 isolate_, keys, JSReceiver::OwnPropertyKeys(target), Nothing<bool>()); | 742 isolate_, keys, JSReceiver::OwnPropertyKeys(target), Nothing<bool>()); |
| 731 bool prev_filter_proxy_keys_ = filter_proxy_keys_; | 743 bool prev_filter_proxy_keys_ = filter_proxy_keys_; |
| 732 filter_proxy_keys_ = false; | 744 filter_proxy_keys_ = false; |
| 733 Maybe<bool> result = AddKeysFromJSProxy(proxy, keys); | 745 Maybe<bool> result = AddKeysFromJSProxy(proxy, keys); |
| 734 filter_proxy_keys_ = prev_filter_proxy_keys_; | 746 filter_proxy_keys_ = prev_filter_proxy_keys_; |
| 735 return result; | 747 return result; |
| 736 } | 748 } |
| 737 | 749 |
| 738 } // namespace internal | 750 } // namespace internal |
| 739 } // namespace v8 | 751 } // namespace v8 |
| OLD | NEW |