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