Chromium Code Reviews| 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/elements.h" | 8 #include "src/elements.h" |
| 8 #include "src/factory.h" | 9 #include "src/factory.h" |
| 10 #include "src/identity-map.h" | |
| 9 #include "src/isolate-inl.h" | 11 #include "src/isolate-inl.h" |
| 10 #include "src/objects-inl.h" | 12 #include "src/objects-inl.h" |
| 13 #include "src/objects.h" | |
|
Jakob Kummerow
2016/05/03 14:56:12
nit: no need to include objects.h if you already h
| |
| 11 #include "src/property-descriptor.h" | 14 #include "src/property-descriptor.h" |
| 12 #include "src/prototype.h" | 15 #include "src/prototype.h" |
| 13 | 16 |
| 14 namespace v8 { | 17 namespace v8 { |
| 15 namespace internal { | 18 namespace internal { |
| 16 | 19 |
| 17 KeyAccumulator::~KeyAccumulator() { | 20 KeyAccumulator::~KeyAccumulator() { |
| 18 for (size_t i = 0; i < elements_.size(); i++) { | 21 for (size_t i = 0; i < elements_.size(); i++) { |
| 19 delete elements_[i]; | 22 delete elements_[i]; |
| 20 } | 23 } |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 // Store the protoLength on the first call of this method. | 308 // Store the protoLength on the first call of this method. |
| 306 if (!elements_.empty()) { | 309 if (!elements_.empty()) { |
| 307 level_lengths_.push_back(level_string_length_); | 310 level_lengths_.push_back(level_string_length_); |
| 308 level_lengths_.push_back(level_symbol_length_); | 311 level_lengths_.push_back(level_symbol_length_); |
| 309 } | 312 } |
| 310 elements_.push_back(new std::vector<uint32_t>()); | 313 elements_.push_back(new std::vector<uint32_t>()); |
| 311 level_string_length_ = 0; | 314 level_string_length_ = 0; |
| 312 level_symbol_length_ = 0; | 315 level_symbol_length_ = 0; |
| 313 } | 316 } |
| 314 | 317 |
| 318 // Helper function for JSReceiver::GetKeys() below. Can be called recursively. | |
|
Jakob Kummerow
2016/05/03 14:56:12
nit: outdated comment
| |
| 319 // Returns |true| or |nothing|. | |
| 320 Maybe<bool> KeyAccumulator::GetKeys_Internal(Handle<JSReceiver> receiver, | |
| 321 Handle<JSReceiver> object, | |
| 322 KeyCollectionType type) { | |
| 323 // Proxies have no hidden prototype and we should not trigger the | |
| 324 // [[GetPrototypeOf]] trap on the last iteration when using | |
| 325 // AdvanceFollowingProxies. | |
| 326 if (type == OWN_ONLY && object->IsJSProxy()) { | |
| 327 MAYBE_RETURN( | |
| 328 JSProxyOwnPropertyKeys(receiver, Handle<JSProxy>::cast(object)), | |
| 329 Nothing<bool>()); | |
| 330 return Just(true); | |
| 331 } | |
| 332 | |
| 333 PrototypeIterator::WhereToEnd end = type == OWN_ONLY | |
| 334 ? PrototypeIterator::END_AT_NON_HIDDEN | |
| 335 : PrototypeIterator::END_AT_NULL; | |
| 336 for (PrototypeIterator iter(isolate_, object, | |
| 337 PrototypeIterator::START_AT_RECEIVER, end); | |
| 338 !iter.IsAtEnd();) { | |
| 339 Handle<JSReceiver> current = | |
| 340 PrototypeIterator::GetCurrent<JSReceiver>(iter); | |
| 341 Maybe<bool> result = Just(false); // Dummy initialization. | |
| 342 if (current->IsJSProxy()) { | |
| 343 result = JSProxyOwnPropertyKeys(receiver, Handle<JSProxy>::cast(current)); | |
| 344 } else { | |
| 345 DCHECK(current->IsJSObject()); | |
| 346 result = | |
| 347 GetKeysFromJSObject(receiver, Handle<JSObject>::cast(current), type); | |
| 348 } | |
| 349 MAYBE_RETURN(result, Nothing<bool>()); | |
| 350 if (!result.FromJust()) break; // |false| means "stop iterating". | |
| 351 // Iterate through proxies but ignore access checks for the ALL_CAN_READ | |
| 352 // case on API objects for OWN_ONLY keys handled in GetKeysFromJSObject. | |
| 353 if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) { | |
| 354 return Nothing<bool>(); | |
| 355 } | |
| 356 } | |
| 357 return Just(true); | |
| 358 } | |
| 359 | |
| 315 namespace { | 360 namespace { |
| 316 | 361 |
| 317 void TrySettingEmptyEnumCache(JSReceiver* object) { | 362 void TrySettingEmptyEnumCache(JSReceiver* object) { |
| 318 Map* map = object->map(); | 363 Map* map = object->map(); |
| 319 DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength()); | 364 DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength()); |
| 320 if (!map->OnlyHasSimpleProperties()) return; | 365 if (!map->OnlyHasSimpleProperties()) return; |
| 321 if (map->IsJSProxyMap()) return; | 366 if (map->IsJSProxyMap()) return; |
| 322 if (map->NumberOfOwnDescriptors() > 0) { | 367 if (map->NumberOfOwnDescriptors() > 0) { |
| 323 int number_of_enumerable_own_properties = | 368 int number_of_enumerable_own_properties = |
| 324 map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS); | 369 map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 356 USE(first_non_empty_prototype); | 401 USE(first_non_empty_prototype); |
| 357 return; | 402 return; |
| 358 } | 403 } |
| 359 DCHECK(has_empty_prototype_); | 404 DCHECK(has_empty_prototype_); |
| 360 is_receiver_simple_enum_ = | 405 is_receiver_simple_enum_ = |
| 361 receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && | 406 receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && |
| 362 !JSObject::cast(*receiver_)->HasEnumerableElements(); | 407 !JSObject::cast(*receiver_)->HasEnumerableElements(); |
| 363 } | 408 } |
| 364 | 409 |
| 365 namespace { | 410 namespace { |
| 411 static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate, | |
| 412 Handle<FixedArray> array, | |
| 413 int length) { | |
| 414 DCHECK_LE(length, array->length()); | |
| 415 if (array->length() == length) return array; | |
| 416 return isolate->factory()->CopyFixedArrayUpTo(array, length); | |
| 417 } | |
| 418 | |
| 419 Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate, | |
| 420 Handle<JSObject> object) { | |
| 421 Handle<Map> map(object->map()); | |
| 422 bool cache_enum_length = map->OnlyHasSimpleProperties(); | |
| 423 | |
| 424 Handle<DescriptorArray> descs = | |
| 425 Handle<DescriptorArray>(map->instance_descriptors(), isolate); | |
| 426 int own_property_count = map->EnumLength(); | |
| 427 // If the enum length of the given map is set to kInvalidEnumCache, this | |
| 428 // means that the map itself has never used the present enum cache. The | |
| 429 // first step to using the cache is to set the enum length of the map by | |
| 430 // counting the number of own descriptors that are ENUMERABLE_STRINGS. | |
| 431 if (own_property_count == kInvalidEnumCacheSentinel) { | |
| 432 own_property_count = | |
| 433 map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS); | |
| 434 } else { | |
| 435 DCHECK( | |
| 436 own_property_count == | |
| 437 map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS)); | |
| 438 } | |
| 439 | |
| 440 if (descs->HasEnumCache()) { | |
| 441 Handle<FixedArray> keys(descs->GetEnumCache(), isolate); | |
| 442 // In case the number of properties required in the enum are actually | |
| 443 // present, we can reuse the enum cache. Otherwise, this means that the | |
| 444 // enum cache was generated for a previous (smaller) version of the | |
| 445 // Descriptor Array. In that case we regenerate the enum cache. | |
| 446 if (own_property_count <= keys->length()) { | |
| 447 isolate->counters()->enum_cache_hits()->Increment(); | |
| 448 if (cache_enum_length) map->SetEnumLength(own_property_count); | |
| 449 return ReduceFixedArrayTo(isolate, keys, own_property_count); | |
| 450 } | |
| 451 } | |
| 452 | |
| 453 if (descs->IsEmpty()) { | |
| 454 isolate->counters()->enum_cache_hits()->Increment(); | |
| 455 if (cache_enum_length) map->SetEnumLength(0); | |
| 456 return isolate->factory()->empty_fixed_array(); | |
| 457 } | |
| 458 | |
| 459 isolate->counters()->enum_cache_misses()->Increment(); | |
| 460 | |
| 461 Handle<FixedArray> storage = | |
| 462 isolate->factory()->NewFixedArray(own_property_count); | |
| 463 Handle<FixedArray> indices = | |
| 464 isolate->factory()->NewFixedArray(own_property_count); | |
| 465 | |
| 466 int size = map->NumberOfOwnDescriptors(); | |
| 467 int index = 0; | |
| 468 | |
| 469 for (int i = 0; i < size; i++) { | |
| 470 PropertyDetails details = descs->GetDetails(i); | |
| 471 if (details.IsDontEnum()) continue; | |
| 472 Object* key = descs->GetKey(i); | |
| 473 if (key->IsSymbol()) continue; | |
| 474 storage->set(index, key); | |
| 475 if (!indices.is_null()) { | |
| 476 if (details.type() != DATA) { | |
| 477 indices = Handle<FixedArray>(); | |
| 478 } else { | |
| 479 FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); | |
| 480 int load_by_field_index = field_index.GetLoadByFieldIndex(); | |
| 481 indices->set(index, Smi::FromInt(load_by_field_index)); | |
| 482 } | |
| 483 } | |
| 484 index++; | |
| 485 } | |
| 486 DCHECK(index == storage->length()); | |
| 487 | |
| 488 DescriptorArray::SetEnumCache(descs, isolate, storage, indices); | |
| 489 if (cache_enum_length) { | |
| 490 map->SetEnumLength(own_property_count); | |
| 491 } | |
| 492 return storage; | |
| 493 } | |
| 366 | 494 |
| 367 template <bool fast_properties> | 495 template <bool fast_properties> |
| 368 Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate, | 496 Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate, |
| 369 Handle<JSObject> object, | 497 Handle<JSObject> object, |
| 370 GetKeysConversion convert) { | 498 GetKeysConversion convert) { |
| 371 Handle<FixedArray> keys; | 499 Handle<FixedArray> keys; |
| 372 ElementsAccessor* accessor = object->GetElementsAccessor(); | 500 ElementsAccessor* accessor = object->GetElementsAccessor(); |
| 373 if (fast_properties) { | 501 if (fast_properties) { |
| 374 keys = JSObject::GetFastEnumPropertyKeys(isolate, object); | 502 keys = GetFastEnumPropertyKeys(isolate, object); |
| 375 } else { | 503 } else { |
| 376 // TODO(cbruni): preallocate big enough array to also hold elements. | 504 // TODO(cbruni): preallocate big enough array to also hold elements. |
| 377 keys = JSObject::GetEnumPropertyKeys(object); | 505 keys = KeyAccumulator::GetEnumPropertyKeys(isolate, object); |
| 378 } | 506 } |
| 379 Handle<FixedArray> result = | 507 Handle<FixedArray> result = |
| 380 accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE); | 508 accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE); |
| 381 | 509 |
| 382 if (FLAG_trace_for_in_enumerate) { | 510 if (FLAG_trace_for_in_enumerate) { |
| 383 PrintF("| strings=%d symbols=0 elements=%u || prototypes>=1 ||\n", | 511 PrintF("| strings=%d symbols=0 elements=%u || prototypes>=1 ||\n", |
| 384 keys->length(), result->length() - keys->length()); | 512 keys->length(), result->length() - keys->length()); |
| 385 } | 513 } |
| 386 return result; | 514 return result; |
| 387 } | 515 } |
| 388 | 516 |
| 389 MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache( | 517 MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache( |
| 390 Isolate* isolate, Handle<JSObject> object) { | 518 Isolate* isolate, Handle<JSObject> object) { |
| 391 // Uninitalized enum cache | 519 // Uninitalized enum cache |
| 392 Map* map = object->map(); | 520 Map* map = object->map(); |
| 393 if (object->elements() != isolate->heap()->empty_fixed_array() || | 521 if (object->elements() != isolate->heap()->empty_fixed_array() || |
| 394 object->elements() != isolate->heap()->empty_slow_element_dictionary()) { | 522 object->elements() != isolate->heap()->empty_slow_element_dictionary()) { |
| 395 // Assume that there are elements. | 523 // Assume that there are elements. |
| 396 return MaybeHandle<FixedArray>(); | 524 return MaybeHandle<FixedArray>(); |
| 397 } | 525 } |
| 398 int number_of_own_descriptors = map->NumberOfOwnDescriptors(); | 526 int number_of_own_descriptors = map->NumberOfOwnDescriptors(); |
| 399 if (number_of_own_descriptors == 0) { | 527 if (number_of_own_descriptors == 0) { |
| 400 map->SetEnumLength(0); | 528 map->SetEnumLength(0); |
| 401 return isolate->factory()->empty_fixed_array(); | 529 return isolate->factory()->empty_fixed_array(); |
| 402 } | 530 } |
| 403 // We have no elements but possibly enumerable property keys, hence we can | 531 // We have no elements but possibly enumerable property keys, hence we can |
| 404 // directly initialize the enum cache. | 532 // directly initialize the enum cache. |
| 405 return JSObject::GetFastEnumPropertyKeys(isolate, object); | 533 return GetFastEnumPropertyKeys(isolate, object); |
| 406 } | 534 } |
| 407 | 535 |
| 408 bool OnlyHasSimpleProperties(Map* map) { | 536 bool OnlyHasSimpleProperties(Map* map) { |
| 409 return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; | 537 return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; |
| 410 } | 538 } |
| 411 | 539 |
| 412 } // namespace | 540 } // namespace |
| 413 | 541 |
| 414 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) { | 542 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) { |
| 415 Handle<FixedArray> keys; | 543 Handle<FixedArray> keys; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 454 // receiver. | 582 // receiver. |
| 455 return GetOwnKeysWithElements<true>(isolate_, object, convert); | 583 return GetOwnKeysWithElements<true>(isolate_, object, convert); |
| 456 } | 584 } |
| 457 | 585 |
| 458 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( | 586 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( |
| 459 GetKeysConversion convert) { | 587 GetKeysConversion convert) { |
| 460 return JSReceiver::GetKeys(receiver_, type_, ENUMERABLE_STRINGS, KEEP_NUMBERS, | 588 return JSReceiver::GetKeys(receiver_, type_, ENUMERABLE_STRINGS, KEEP_NUMBERS, |
| 461 filter_proxy_keys_); | 589 filter_proxy_keys_); |
| 462 } | 590 } |
| 463 | 591 |
| 592 enum IndexedOrNamed { kIndexed, kNamed }; | |
| 593 | |
| 594 // Returns |true| on success, |nothing| on exception. | |
| 595 template <class Callback, IndexedOrNamed type> | |
| 596 static Maybe<bool> GetKeysFromInterceptor(Handle<JSReceiver> receiver, | |
| 597 Handle<JSObject> object, | |
| 598 KeyAccumulator* accumulator) { | |
| 599 Isolate* isolate = accumulator->isolate(); | |
| 600 if (type == kIndexed) { | |
| 601 if (!object->HasIndexedInterceptor()) return Just(true); | |
| 602 } else { | |
| 603 if (!object->HasNamedInterceptor()) return Just(true); | |
| 604 } | |
| 605 Handle<InterceptorInfo> interceptor(type == kIndexed | |
| 606 ? object->GetIndexedInterceptor() | |
| 607 : object->GetNamedInterceptor(), | |
| 608 isolate); | |
| 609 if ((accumulator->filter() & ONLY_ALL_CAN_READ) && | |
| 610 !interceptor->all_can_read()) { | |
| 611 return Just(true); | |
| 612 } | |
| 613 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, | |
| 614 *object, Object::DONT_THROW); | |
| 615 Handle<JSObject> result; | |
| 616 if (!interceptor->enumerator()->IsUndefined()) { | |
| 617 Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator()); | |
| 618 const char* log_tag = type == kIndexed ? "interceptor-indexed-enum" | |
| 619 : "interceptor-named-enum"; | |
| 620 LOG(isolate, ApiObjectAccess(log_tag, *object)); | |
| 621 result = args.Call(enum_fun); | |
| 622 } | |
| 623 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | |
| 624 if (result.is_null()) return Just(true); | |
| 625 DCHECK(result->IsJSArray() || result->HasSloppyArgumentsElements()); | |
| 626 // The accumulator takes care of string/symbol filtering. | |
| 627 if (type == kIndexed) { | |
| 628 accumulator->AddElementKeysFromInterceptor(result); | |
| 629 } else { | |
| 630 accumulator->AddKeys(result, DO_NOT_CONVERT); | |
| 631 } | |
| 632 return Just(true); | |
| 633 } | |
| 634 | |
| 635 void KeyAccumulator::CollectOwnElementKeys(Handle<JSObject> object) { | |
| 636 if (filter_ & SKIP_STRINGS) return; | |
| 637 ElementsAccessor* accessor = object->GetElementsAccessor(); | |
| 638 accessor->CollectElementIndices(object, this, kMaxUInt32, filter_, 0); | |
| 639 } | |
| 640 | |
| 641 void KeyAccumulator::CollectOwnPropertyNames(Handle<JSObject> object) { | |
| 642 if (object->HasFastProperties()) { | |
| 643 int real_size = object->map()->NumberOfOwnDescriptors(); | |
| 644 Handle<DescriptorArray> descs(object->map()->instance_descriptors(), | |
| 645 isolate_); | |
| 646 for (int i = 0; i < real_size; i++) { | |
| 647 PropertyDetails details = descs->GetDetails(i); | |
| 648 if ((details.attributes() & filter_) != 0) continue; | |
| 649 if (filter_ & ONLY_ALL_CAN_READ) { | |
| 650 if (details.kind() != kAccessor) continue; | |
| 651 Object* accessors = descs->GetValue(i); | |
| 652 if (!accessors->IsAccessorInfo()) continue; | |
| 653 if (!AccessorInfo::cast(accessors)->all_can_read()) continue; | |
| 654 } | |
| 655 Name* key = descs->GetKey(i); | |
| 656 if (key->FilterKey(filter_)) continue; | |
| 657 this->AddKey(key, DO_NOT_CONVERT); | |
| 658 } | |
| 659 } else if (object->IsJSGlobalObject()) { | |
| 660 GlobalDictionary::CollectKeysTo( | |
| 661 handle(object->global_dictionary(), isolate_), this, filter_); | |
| 662 } else { | |
| 663 NameDictionary::CollectKeysTo( | |
| 664 handle(object->property_dictionary(), isolate_), this, filter_); | |
| 665 } | |
| 666 } | |
| 667 | |
| 668 // Returns |true| on success, |false| if prototype walking should be stopped, | |
| 669 // |nothing| if an exception was thrown. | |
| 670 Maybe<bool> KeyAccumulator::GetKeysFromJSObject(Handle<JSReceiver> receiver, | |
| 671 Handle<JSObject> object, | |
| 672 KeyCollectionType type) { | |
| 673 this->NextPrototype(); | |
| 674 // Check access rights if required. | |
| 675 if (object->IsAccessCheckNeeded() && | |
| 676 !isolate_->MayAccess(handle(isolate_->context()), object)) { | |
| 677 // The cross-origin spec says that [[Enumerate]] shall return an empty | |
| 678 // iterator when it doesn't have access... | |
| 679 if (type == INCLUDE_PROTOS) { | |
| 680 return Just(false); | |
| 681 } | |
| 682 // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties. | |
| 683 DCHECK_EQ(OWN_ONLY, type); | |
| 684 filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ); | |
| 685 } | |
| 686 | |
| 687 this->CollectOwnElementKeys(object); | |
| 688 | |
| 689 // Add the element keys from the interceptor. | |
| 690 Maybe<bool> success = | |
| 691 GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, kIndexed>( | |
| 692 receiver, object, this); | |
| 693 MAYBE_RETURN(success, Nothing<bool>()); | |
| 694 | |
| 695 if (filter_ == ENUMERABLE_STRINGS) { | |
| 696 Handle<FixedArray> enum_keys = | |
| 697 KeyAccumulator::GetEnumPropertyKeys(isolate_, object); | |
| 698 this->AddKeys(enum_keys, DO_NOT_CONVERT); | |
| 699 } else { | |
| 700 this->CollectOwnPropertyNames(object); | |
| 701 } | |
| 702 | |
| 703 // Add the property keys from the interceptor. | |
| 704 success = GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback, | |
| 705 kNamed>(receiver, object, this); | |
| 706 MAYBE_RETURN(success, Nothing<bool>()); | |
| 707 return Just(true); | |
| 708 } | |
| 709 | |
| 710 // static | |
| 711 Handle<FixedArray> KeyAccumulator::GetEnumPropertyKeys( | |
| 712 Isolate* isolate, Handle<JSObject> object) { | |
| 713 if (object->HasFastProperties()) { | |
| 714 return GetFastEnumPropertyKeys(isolate, object); | |
| 715 } else if (object->IsJSGlobalObject()) { | |
| 716 Handle<GlobalDictionary> dictionary(object->global_dictionary(), isolate); | |
| 717 int length = dictionary->NumberOfEnumElements(); | |
| 718 if (length == 0) { | |
| 719 return isolate->factory()->empty_fixed_array(); | |
| 720 } | |
| 721 Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length); | |
| 722 dictionary->CopyEnumKeysTo(*storage); | |
| 723 return storage; | |
| 724 } else { | |
| 725 Handle<NameDictionary> dictionary(object->property_dictionary(), isolate); | |
| 726 int length = dictionary->NumberOfEnumElements(); | |
| 727 if (length == 0) { | |
| 728 return isolate->factory()->empty_fixed_array(); | |
| 729 } | |
| 730 Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length); | |
| 731 dictionary->CopyEnumKeysTo(*storage); | |
| 732 return storage; | |
| 733 } | |
| 734 } | |
| 735 | |
| 736 // ES6 9.5.12 | |
| 737 // Returns |true| on success, |nothing| in case of exception. | |
| 738 Maybe<bool> KeyAccumulator::JSProxyOwnPropertyKeys(Handle<JSReceiver> receiver, | |
| 739 Handle<JSProxy> proxy) { | |
| 740 STACK_CHECK(isolate_, Nothing<bool>()); | |
| 741 // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O. | |
| 742 Handle<Object> handler(proxy->handler(), isolate_); | |
| 743 // 2. If handler is null, throw a TypeError exception. | |
| 744 // 3. Assert: Type(handler) is Object. | |
| 745 if (proxy->IsRevoked()) { | |
| 746 isolate_->Throw(*isolate_->factory()->NewTypeError( | |
| 747 MessageTemplate::kProxyRevoked, isolate_->factory()->ownKeys_string())); | |
| 748 return Nothing<bool>(); | |
| 749 } | |
| 750 // 4. Let target be the value of the [[ProxyTarget]] internal slot of O. | |
| 751 Handle<JSReceiver> target(proxy->target(), isolate_); | |
| 752 // 5. Let trap be ? GetMethod(handler, "ownKeys"). | |
| 753 Handle<Object> trap; | |
| 754 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | |
| 755 isolate_, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), | |
| 756 isolate_->factory()->ownKeys_string()), | |
| 757 Nothing<bool>()); | |
| 758 // 6. If trap is undefined, then | |
| 759 if (trap->IsUndefined()) { | |
| 760 // 6a. Return target.[[OwnPropertyKeys]](). | |
| 761 return this->GetKeys_Internal(receiver, target, OWN_ONLY); | |
| 762 } | |
| 763 // 7. Let trapResultArray be Call(trap, handler, «target»). | |
| 764 Handle<Object> trap_result_array; | |
| 765 Handle<Object> args[] = {target}; | |
| 766 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | |
| 767 isolate_, trap_result_array, | |
| 768 Execution::Call(isolate_, trap, handler, arraysize(args), args), | |
| 769 Nothing<bool>()); | |
| 770 // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, | |
| 771 // «String, Symbol»). | |
| 772 Handle<FixedArray> trap_result; | |
| 773 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | |
| 774 isolate_, trap_result, | |
| 775 Object::CreateListFromArrayLike(isolate_, trap_result_array, | |
| 776 ElementTypes::kStringAndSymbol), | |
| 777 Nothing<bool>()); | |
| 778 // 9. Let extensibleTarget be ? IsExtensible(target). | |
| 779 Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target); | |
| 780 MAYBE_RETURN(maybe_extensible, Nothing<bool>()); | |
| 781 bool extensible_target = maybe_extensible.FromJust(); | |
| 782 // 10. Let targetKeys be ? target.[[OwnPropertyKeys]](). | |
| 783 Handle<FixedArray> target_keys; | |
| 784 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, target_keys, | |
| 785 JSReceiver::OwnPropertyKeys(target), | |
| 786 Nothing<bool>()); | |
| 787 // 11. (Assert) | |
| 788 // 12. Let targetConfigurableKeys be an empty List. | |
| 789 // To save memory, we're re-using target_keys and will modify it in-place. | |
| 790 Handle<FixedArray> target_configurable_keys = target_keys; | |
| 791 // 13. Let targetNonconfigurableKeys be an empty List. | |
| 792 Handle<FixedArray> target_nonconfigurable_keys = | |
| 793 isolate_->factory()->NewFixedArray(target_keys->length()); | |
| 794 int nonconfigurable_keys_length = 0; | |
| 795 // 14. Repeat, for each element key of targetKeys: | |
| 796 for (int i = 0; i < target_keys->length(); ++i) { | |
| 797 // 14a. Let desc be ? target.[[GetOwnProperty]](key). | |
| 798 PropertyDescriptor desc; | |
| 799 Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor( | |
| 800 isolate_, target, handle(target_keys->get(i), isolate_), &desc); | |
| 801 MAYBE_RETURN(found, Nothing<bool>()); | |
| 802 // 14b. If desc is not undefined and desc.[[Configurable]] is false, then | |
| 803 if (found.FromJust() && !desc.configurable()) { | |
| 804 // 14b i. Append key as an element of targetNonconfigurableKeys. | |
| 805 target_nonconfigurable_keys->set(nonconfigurable_keys_length, | |
| 806 target_keys->get(i)); | |
| 807 nonconfigurable_keys_length++; | |
| 808 // The key was moved, null it out in the original list. | |
| 809 target_keys->set(i, Smi::FromInt(0)); | |
| 810 } else { | |
| 811 // 14c. Else, | |
| 812 // 14c i. Append key as an element of targetConfigurableKeys. | |
| 813 // (No-op, just keep it in |target_keys|.) | |
| 814 } | |
| 815 } | |
| 816 this->NextPrototype(); // Prepare for accumulating keys. | |
| 817 // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty, | |
| 818 // then: | |
| 819 if (extensible_target && nonconfigurable_keys_length == 0) { | |
| 820 // 15a. Return trapResult. | |
| 821 return this->AddKeysFromProxy(proxy, trap_result); | |
| 822 } | |
| 823 // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult. | |
| 824 Zone set_zone(isolate_->allocator()); | |
| 825 const int kPresent = 1; | |
| 826 const int kGone = 0; | |
| 827 IdentityMap<int> unchecked_result_keys(isolate_->heap(), &set_zone); | |
| 828 int unchecked_result_keys_size = 0; | |
| 829 for (int i = 0; i < trap_result->length(); ++i) { | |
| 830 DCHECK(trap_result->get(i)->IsUniqueName()); | |
| 831 Object* key = trap_result->get(i); | |
| 832 int* entry = unchecked_result_keys.Get(key); | |
| 833 if (*entry != kPresent) { | |
| 834 *entry = kPresent; | |
| 835 unchecked_result_keys_size++; | |
| 836 } | |
| 837 } | |
| 838 // 17. Repeat, for each key that is an element of targetNonconfigurableKeys: | |
| 839 for (int i = 0; i < nonconfigurable_keys_length; ++i) { | |
| 840 Object* key = target_nonconfigurable_keys->get(i); | |
| 841 // 17a. If key is not an element of uncheckedResultKeys, throw a | |
| 842 // TypeError exception. | |
| 843 int* found = unchecked_result_keys.Find(key); | |
| 844 if (found == nullptr || *found == kGone) { | |
| 845 isolate_->Throw(*isolate_->factory()->NewTypeError( | |
| 846 MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate_))); | |
| 847 return Nothing<bool>(); | |
| 848 } | |
| 849 // 17b. Remove key from uncheckedResultKeys. | |
| 850 *found = kGone; | |
| 851 unchecked_result_keys_size--; | |
| 852 } | |
| 853 // 18. If extensibleTarget is true, return trapResult. | |
| 854 if (extensible_target) { | |
| 855 return this->AddKeysFromProxy(proxy, trap_result); | |
| 856 } | |
| 857 // 19. Repeat, for each key that is an element of targetConfigurableKeys: | |
| 858 for (int i = 0; i < target_configurable_keys->length(); ++i) { | |
| 859 Object* key = target_configurable_keys->get(i); | |
| 860 if (key->IsSmi()) continue; // Zapped entry, was nonconfigurable. | |
| 861 // 19a. If key is not an element of uncheckedResultKeys, throw a | |
| 862 // TypeError exception. | |
| 863 int* found = unchecked_result_keys.Find(key); | |
| 864 if (found == nullptr || *found == kGone) { | |
| 865 isolate_->Throw(*isolate_->factory()->NewTypeError( | |
| 866 MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate_))); | |
| 867 return Nothing<bool>(); | |
| 868 } | |
| 869 // 19b. Remove key from uncheckedResultKeys. | |
| 870 *found = kGone; | |
| 871 unchecked_result_keys_size--; | |
| 872 } | |
| 873 // 20. If uncheckedResultKeys is not empty, throw a TypeError exception. | |
| 874 if (unchecked_result_keys_size != 0) { | |
| 875 DCHECK_GT(unchecked_result_keys_size, 0); | |
| 876 isolate_->Throw(*isolate_->factory()->NewTypeError( | |
| 877 MessageTemplate::kProxyOwnKeysNonExtensible)); | |
| 878 return Nothing<bool>(); | |
| 879 } | |
| 880 // 21. Return trapResult. | |
| 881 return this->AddKeysFromProxy(proxy, trap_result); | |
| 882 } | |
| 883 | |
| 464 } // namespace internal | 884 } // namespace internal |
| 465 } // namespace v8 | 885 } // namespace v8 |
| OLD | NEW |