| 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 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 if (map->IsJSProxyMap()) return; | 220 if (map->IsJSProxyMap()) return; |
| 221 if (map->NumberOfOwnDescriptors() > 0) { | 221 if (map->NumberOfOwnDescriptors() > 0) { |
| 222 int number_of_enumerable_own_properties = | 222 int number_of_enumerable_own_properties = |
| 223 map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS); | 223 map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS); |
| 224 if (number_of_enumerable_own_properties > 0) return; | 224 if (number_of_enumerable_own_properties > 0) return; |
| 225 } | 225 } |
| 226 DCHECK(object->IsJSObject()); | 226 DCHECK(object->IsJSObject()); |
| 227 map->SetEnumLength(0); | 227 map->SetEnumLength(0); |
| 228 } | 228 } |
| 229 | 229 |
| 230 bool CheckAndInitalizeSimpleEnumCache(JSReceiver* object) { | 230 bool CheckAndInitalizeEmptyEnumCache(JSReceiver* object) { |
| 231 if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) { | 231 if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) { |
| 232 TrySettingEmptyEnumCache(object); | 232 TrySettingEmptyEnumCache(object); |
| 233 } | 233 } |
| 234 if (object->map()->EnumLength() != 0) return false; | 234 if (object->map()->EnumLength() != 0) return false; |
| 235 DCHECK(object->IsJSObject()); | 235 DCHECK(object->IsJSObject()); |
| 236 return !JSObject::cast(object)->HasEnumerableElements(); | 236 return !JSObject::cast(object)->HasEnumerableElements(); |
| 237 } | 237 } |
| 238 } // namespace | 238 } // namespace |
| 239 | 239 |
| 240 void FastKeyAccumulator::Prepare() { | 240 void FastKeyAccumulator::Prepare() { |
| 241 DisallowHeapAllocation no_gc; | 241 DisallowHeapAllocation no_gc; |
| 242 // Directly go for the fast path for OWN_ONLY keys. | 242 // Directly go for the fast path for OWN_ONLY keys. |
| 243 if (mode_ == KeyCollectionMode::kOwnOnly) return; | 243 if (mode_ == KeyCollectionMode::kOwnOnly) return; |
| 244 // Fully walk the prototype chain and find the last prototype with keys. | 244 // Fully walk the prototype chain and find the last prototype with keys. |
| 245 is_receiver_simple_enum_ = false; | 245 is_receiver_simple_enum_ = false; |
| 246 has_empty_prototype_ = true; | 246 has_empty_prototype_ = true; |
| 247 JSReceiver* last_prototype = nullptr; | 247 JSReceiver* last_prototype = nullptr; |
| 248 for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd(); | 248 for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd(); |
| 249 iter.Advance()) { | 249 iter.Advance()) { |
| 250 JSReceiver* current = iter.GetCurrent<JSReceiver>(); | 250 JSReceiver* current = iter.GetCurrent<JSReceiver>(); |
| 251 bool has_no_properties = CheckAndInitalizeSimpleEnumCache(current); | 251 bool has_no_properties = CheckAndInitalizeEmptyEnumCache(current); |
| 252 if (has_no_properties) continue; | 252 if (has_no_properties) continue; |
| 253 last_prototype = current; | 253 last_prototype = current; |
| 254 has_empty_prototype_ = false; | 254 has_empty_prototype_ = false; |
| 255 } | 255 } |
| 256 if (has_empty_prototype_) { | 256 if (has_empty_prototype_) { |
| 257 is_receiver_simple_enum_ = | 257 is_receiver_simple_enum_ = |
| 258 receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && | 258 receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && |
| 259 !JSObject::cast(*receiver_)->HasEnumerableElements(); | 259 !JSObject::cast(*receiver_)->HasEnumerableElements(); |
| 260 } else if (last_prototype != nullptr) { | 260 } else if (last_prototype != nullptr) { |
| 261 last_non_empty_prototype_ = handle(last_prototype, isolate_); | 261 last_non_empty_prototype_ = handle(last_prototype, isolate_); |
| 262 } | 262 } |
| 263 } | 263 } |
| 264 | 264 |
| 265 namespace { | 265 namespace { |
| 266 static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate, | 266 static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate, |
| 267 Handle<FixedArray> array, | 267 Handle<FixedArray> array, |
| 268 int length) { | 268 int length) { |
| 269 DCHECK_LE(length, array->length()); | 269 DCHECK_LE(length, array->length()); |
| 270 if (array->length() == length) return array; | 270 if (array->length() == length) return array; |
| 271 return isolate->factory()->CopyFixedArrayUpTo(array, length); | 271 return isolate->factory()->CopyFixedArrayUpTo(array, length); |
| 272 } | 272 } |
| 273 | 273 |
| 274 // Initializes and directly returns the enume cache. Users of this function |
| 275 // have to make sure to never directly leak the enum cache. |
| 274 Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate, | 276 Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate, |
| 275 Handle<JSObject> object) { | 277 Handle<JSObject> object) { |
| 276 Handle<Map> map(object->map()); | 278 Handle<Map> map(object->map()); |
| 277 bool cache_enum_length = map->OnlyHasSimpleProperties(); | 279 bool cache_enum_length = map->OnlyHasSimpleProperties(); |
| 278 | 280 |
| 279 Handle<DescriptorArray> descs = | 281 Handle<DescriptorArray> descs = |
| 280 Handle<DescriptorArray>(map->instance_descriptors(), isolate); | 282 Handle<DescriptorArray>(map->instance_descriptors(), isolate); |
| 281 int own_property_count = map->EnumLength(); | 283 int own_property_count = map->EnumLength(); |
| 282 // If the enum length of the given map is set to kInvalidEnumCache, this | 284 // If the enum length of the given map is set to kInvalidEnumCache, this |
| 283 // means that the map itself has never used the present enum cache. The | 285 // means that the map itself has never used the present enum cache. The |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 363 MaybeHandle<FixedArray> result = | 365 MaybeHandle<FixedArray> result = |
| 364 accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE); | 366 accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE); |
| 365 | 367 |
| 366 if (FLAG_trace_for_in_enumerate) { | 368 if (FLAG_trace_for_in_enumerate) { |
| 367 PrintF("| strings=%d symbols=0 elements=%u || prototypes>=1 ||\n", | 369 PrintF("| strings=%d symbols=0 elements=%u || prototypes>=1 ||\n", |
| 368 keys->length(), result.ToHandleChecked()->length() - keys->length()); | 370 keys->length(), result.ToHandleChecked()->length() - keys->length()); |
| 369 } | 371 } |
| 370 return result; | 372 return result; |
| 371 } | 373 } |
| 372 | 374 |
| 373 MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache( | |
| 374 Isolate* isolate, Handle<JSObject> object) { | |
| 375 // Uninitalized enum cache | |
| 376 Map* map = object->map(); | |
| 377 if (object->elements() != isolate->heap()->empty_fixed_array() || | |
| 378 object->elements() != isolate->heap()->empty_slow_element_dictionary()) { | |
| 379 // Assume that there are elements. | |
| 380 return MaybeHandle<FixedArray>(); | |
| 381 } | |
| 382 int number_of_own_descriptors = map->NumberOfOwnDescriptors(); | |
| 383 if (number_of_own_descriptors == 0) { | |
| 384 map->SetEnumLength(0); | |
| 385 return isolate->factory()->empty_fixed_array(); | |
| 386 } | |
| 387 // We have no elements but possibly enumerable property keys, hence we can | |
| 388 // directly initialize the enum cache. | |
| 389 return GetFastEnumPropertyKeys(isolate, object); | |
| 390 } | |
| 391 | |
| 392 bool OnlyHasSimpleProperties(Map* map) { | 375 bool OnlyHasSimpleProperties(Map* map) { |
| 393 return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; | 376 return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; |
| 394 } | 377 } |
| 395 | 378 |
| 396 } // namespace | 379 } // namespace |
| 397 | 380 |
| 398 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys( | 381 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys( |
| 399 GetKeysConversion keys_conversion) { | 382 GetKeysConversion keys_conversion) { |
| 400 if (filter_ == ENUMERABLE_STRINGS) { | 383 if (filter_ == ENUMERABLE_STRINGS) { |
| 401 Handle<FixedArray> keys; | 384 Handle<FixedArray> keys; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 421 Handle<JSObject> object = Handle<JSObject>::cast(receiver_); | 404 Handle<JSObject> object = Handle<JSObject>::cast(receiver_); |
| 422 | 405 |
| 423 // Do not try to use the enum-cache for dict-mode objects. | 406 // Do not try to use the enum-cache for dict-mode objects. |
| 424 if (map->is_dictionary_map()) { | 407 if (map->is_dictionary_map()) { |
| 425 return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion); | 408 return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion); |
| 426 } | 409 } |
| 427 int enum_length = receiver_->map()->EnumLength(); | 410 int enum_length = receiver_->map()->EnumLength(); |
| 428 if (enum_length == kInvalidEnumCacheSentinel) { | 411 if (enum_length == kInvalidEnumCacheSentinel) { |
| 429 Handle<FixedArray> keys; | 412 Handle<FixedArray> keys; |
| 430 // Try initializing the enum cache and return own properties. | 413 // Try initializing the enum cache and return own properties. |
| 431 if (GetOwnKeysWithUninitializedEnumCache(isolate_, object) | 414 if (GetOwnKeysWithUninitializedEnumCache().ToHandle(&keys)) { |
| 432 .ToHandle(&keys)) { | |
| 433 if (FLAG_trace_for_in_enumerate) { | 415 if (FLAG_trace_for_in_enumerate) { |
| 434 PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n", | 416 PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n", |
| 435 keys->length()); | 417 keys->length()); |
| 436 } | 418 } |
| 437 is_receiver_simple_enum_ = | 419 is_receiver_simple_enum_ = |
| 438 object->map()->EnumLength() != kInvalidEnumCacheSentinel; | 420 object->map()->EnumLength() != kInvalidEnumCacheSentinel; |
| 439 return keys; | 421 return keys; |
| 440 } | 422 } |
| 441 } | 423 } |
| 442 // The properties-only case failed because there were probably elements on the | 424 // The properties-only case failed because there were probably elements on the |
| 443 // receiver. | 425 // receiver. |
| 444 return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion); | 426 return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion); |
| 445 } | 427 } |
| 446 | 428 |
| 429 MaybeHandle<FixedArray> |
| 430 FastKeyAccumulator::GetOwnKeysWithUninitializedEnumCache() { |
| 431 Handle<JSObject> object = Handle<JSObject>::cast(receiver_); |
| 432 // Uninitalized enum cache |
| 433 Map* map = object->map(); |
| 434 if (object->elements()->length() != 0) { |
| 435 // Assume that there are elements. |
| 436 return MaybeHandle<FixedArray>(); |
| 437 } |
| 438 int number_of_own_descriptors = map->NumberOfOwnDescriptors(); |
| 439 if (number_of_own_descriptors == 0) { |
| 440 map->SetEnumLength(0); |
| 441 return isolate_->factory()->empty_fixed_array(); |
| 442 } |
| 443 // We have no elements but possibly enumerable property keys, hence we can |
| 444 // directly initialize the enum cache. |
| 445 Handle<FixedArray> keys = GetFastEnumPropertyKeys(isolate_, object); |
| 446 if (is_for_in_) return keys; |
| 447 // Do not leak the enum cache as it might end up as an elements backing store. |
| 448 return isolate_->factory()->CopyFixedArray(keys); |
| 449 } |
| 450 |
| 447 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( | 451 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( |
| 448 GetKeysConversion keys_conversion) { | 452 GetKeysConversion keys_conversion) { |
| 449 KeyAccumulator accumulator(isolate_, mode_, filter_); | 453 KeyAccumulator accumulator(isolate_, mode_, filter_); |
| 450 accumulator.set_is_for_in(is_for_in_); | 454 accumulator.set_is_for_in(is_for_in_); |
| 451 accumulator.set_last_non_empty_prototype(last_non_empty_prototype_); | 455 accumulator.set_last_non_empty_prototype(last_non_empty_prototype_); |
| 452 | 456 |
| 453 MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_), | 457 MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_), |
| 454 MaybeHandle<FixedArray>()); | 458 MaybeHandle<FixedArray>()); |
| 455 return accumulator.GetKeys(keys_conversion); | 459 return accumulator.GetKeys(keys_conversion); |
| 456 } | 460 } |
| (...skipping 406 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 863 isolate_, keys, | 867 isolate_, keys, |
| 864 KeyAccumulator::GetKeys(target, KeyCollectionMode::kOwnOnly, filter_, | 868 KeyAccumulator::GetKeys(target, KeyCollectionMode::kOwnOnly, filter_, |
| 865 GetKeysConversion::kConvertToString, is_for_in_), | 869 GetKeysConversion::kConvertToString, is_for_in_), |
| 866 Nothing<bool>()); | 870 Nothing<bool>()); |
| 867 Maybe<bool> result = AddKeysFromJSProxy(proxy, keys); | 871 Maybe<bool> result = AddKeysFromJSProxy(proxy, keys); |
| 868 return result; | 872 return result; |
| 869 } | 873 } |
| 870 | 874 |
| 871 } // namespace internal | 875 } // namespace internal |
| 872 } // namespace v8 | 876 } // namespace v8 |
| OLD | NEW |