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 |