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" |
11 #include "src/isolate-inl.h" | 11 #include "src/isolate-inl.h" |
12 #include "src/objects-inl.h" | 12 #include "src/objects-inl.h" |
13 #include "src/property-descriptor.h" | 13 #include "src/property-descriptor.h" |
14 #include "src/prototype.h" | 14 #include "src/prototype.h" |
15 | 15 |
16 namespace v8 { | 16 namespace v8 { |
17 namespace internal { | 17 namespace internal { |
18 | 18 |
19 KeyAccumulator::~KeyAccumulator() { | 19 KeyAccumulator::~KeyAccumulator() { |
| 20 for (size_t i = 0; i < elements_.size(); i++) { |
| 21 delete elements_[i]; |
| 22 } |
20 } | 23 } |
21 | 24 |
22 namespace { | 25 namespace { |
23 | 26 |
24 static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { | 27 static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { |
25 int len = array->length(); | 28 int len = array->length(); |
26 for (int i = 0; i < len; i++) { | 29 for (int i = 0; i < len; i++) { |
27 Object* e = array->get(i); | 30 Object* e = array->get(i); |
28 if (!(e->IsName() || e->IsNumber())) return false; | 31 if (!(e->IsName() || e->IsNumber())) return false; |
29 } | 32 } |
30 return true; | 33 return true; |
31 } | 34 } |
32 | 35 |
33 } // namespace | 36 } // namespace |
34 | 37 |
35 MaybeHandle<FixedArray> KeyAccumulator::GetKeys( | 38 MaybeHandle<FixedArray> KeyAccumulator::GetKeys( |
36 Handle<JSReceiver> object, KeyCollectionType type, PropertyFilter filter, | 39 Handle<JSReceiver> object, KeyCollectionType type, PropertyFilter filter, |
37 GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in) { | 40 GetKeysConversion keys_conversion, bool filter_proxy_keys) { |
38 USE(ContainsOnlyValidKeys); | 41 USE(ContainsOnlyValidKeys); |
39 Isolate* isolate = object->GetIsolate(); | 42 Isolate* isolate = object->GetIsolate(); |
40 KeyAccumulator accumulator(isolate, type, filter); | 43 KeyAccumulator accumulator(isolate, type, filter); |
41 accumulator.set_filter_proxy_keys(filter_proxy_keys); | 44 accumulator.set_filter_proxy_keys(filter_proxy_keys); |
42 accumulator.set_is_for_in(is_for_in); | |
43 MAYBE_RETURN(accumulator.CollectKeys(object, object), | 45 MAYBE_RETURN(accumulator.CollectKeys(object, object), |
44 MaybeHandle<FixedArray>()); | 46 MaybeHandle<FixedArray>()); |
45 Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion); | 47 Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion); |
46 DCHECK(ContainsOnlyValidKeys(keys)); | 48 DCHECK(ContainsOnlyValidKeys(keys)); |
47 return keys; | 49 return keys; |
48 } | 50 } |
49 | 51 |
50 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) { | 52 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) { |
51 if (keys_.is_null()) { | 53 if (length_ == 0) { |
52 return isolate_->factory()->empty_fixed_array(); | 54 return isolate_->factory()->empty_fixed_array(); |
53 } | 55 } |
54 if (type_ == OWN_ONLY && | 56 // Make sure we have all the lengths collected. |
55 keys_->map() == isolate_->heap()->fixed_array_map()) { | 57 NextPrototype(); |
56 return Handle<FixedArray>::cast(keys_); | 58 |
| 59 if (type_ == OWN_ONLY && !ownProxyKeys_.is_null()) { |
| 60 return ownProxyKeys_; |
57 } | 61 } |
58 return OrderedHashSet::ConvertToKeysArray(keys(), convert); | 62 // Assemble the result array by first adding the element keys and then the |
| 63 // property keys. We use the total number of String + Symbol keys per level in |
| 64 // |level_lengths_| and the available element keys in the corresponding bucket |
| 65 // in |elements_| to deduce the number of keys to take from the |
| 66 // |string_properties_| and |symbol_properties_| set. |
| 67 Handle<FixedArray> result = isolate_->factory()->NewFixedArray(length_); |
| 68 int insertion_index = 0; |
| 69 int string_properties_index = 0; |
| 70 int symbol_properties_index = 0; |
| 71 // String and Symbol lengths always come in pairs: |
| 72 size_t max_level = level_lengths_.size() / 2; |
| 73 for (size_t level = 0; level < max_level; level++) { |
| 74 int num_string_properties = level_lengths_[level * 2]; |
| 75 int num_symbol_properties = level_lengths_[level * 2 + 1]; |
| 76 int num_elements = 0; |
| 77 if (num_string_properties < 0) { |
| 78 // If the |num_string_properties| is negative, the current level contains |
| 79 // properties from a proxy, hence we skip the integer keys in |elements_| |
| 80 // since proxies define the complete ordering. |
| 81 num_string_properties = -num_string_properties; |
| 82 } else if (level < elements_.size()) { |
| 83 // Add the element indices for this prototype level. |
| 84 std::vector<uint32_t>* elements = elements_[level]; |
| 85 num_elements = static_cast<int>(elements->size()); |
| 86 for (int i = 0; i < num_elements; i++) { |
| 87 Handle<Object> key; |
| 88 if (convert == KEEP_NUMBERS) { |
| 89 key = isolate_->factory()->NewNumberFromUint(elements->at(i)); |
| 90 } else { |
| 91 key = isolate_->factory()->Uint32ToString(elements->at(i)); |
| 92 } |
| 93 result->set(insertion_index, *key); |
| 94 insertion_index++; |
| 95 } |
| 96 } |
| 97 // Add the string property keys for this prototype level. |
| 98 for (int i = 0; i < num_string_properties; i++) { |
| 99 Object* key = string_properties_->KeyAt(string_properties_index); |
| 100 result->set(insertion_index, key); |
| 101 insertion_index++; |
| 102 string_properties_index++; |
| 103 } |
| 104 // Add the symbol property keys for this prototype level. |
| 105 for (int i = 0; i < num_symbol_properties; i++) { |
| 106 Object* key = symbol_properties_->KeyAt(symbol_properties_index); |
| 107 result->set(insertion_index, key); |
| 108 insertion_index++; |
| 109 symbol_properties_index++; |
| 110 } |
| 111 if (FLAG_trace_for_in_enumerate) { |
| 112 PrintF("| strings=%d symbols=%d elements=%i ", num_string_properties, |
| 113 num_symbol_properties, num_elements); |
| 114 } |
| 115 } |
| 116 if (FLAG_trace_for_in_enumerate) { |
| 117 PrintF("|| prototypes=%zu ||\n", max_level); |
| 118 } |
| 119 |
| 120 DCHECK_EQ(insertion_index, length_); |
| 121 return result; |
59 } | 122 } |
60 | 123 |
61 void KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) { | 124 namespace { |
62 AddKey(handle(key, isolate_), convert); | 125 |
| 126 bool AccumulatorHasKey(std::vector<uint32_t>* sub_elements, uint32_t key) { |
| 127 return std::binary_search(sub_elements->begin(), sub_elements->end(), key); |
63 } | 128 } |
64 | 129 |
65 void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) { | 130 } // namespace |
| 131 |
| 132 bool KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) { |
| 133 return AddKey(handle(key, isolate_), convert); |
| 134 } |
| 135 |
| 136 bool KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) { |
66 if (key->IsSymbol()) { | 137 if (key->IsSymbol()) { |
67 if (filter_ & SKIP_SYMBOLS) return; | 138 if (filter_ & SKIP_SYMBOLS) return false; |
68 if (Handle<Symbol>::cast(key)->is_private()) return; | 139 if (Handle<Symbol>::cast(key)->is_private()) return false; |
69 } else if (filter_ & SKIP_STRINGS) { | 140 return AddSymbolKey(key); |
70 return; | |
71 } | 141 } |
72 if (keys_.is_null()) { | 142 if (filter_ & SKIP_STRINGS) return false; |
73 keys_ = OrderedHashSet::Allocate(isolate_, 16); | 143 // Make sure we do not add keys to a proxy-level (see AddKeysFromJSProxy). |
| 144 DCHECK_LE(0, level_string_length_); |
| 145 // In some cases (e.g. proxies) we might get in String-converted ints which |
| 146 // should be added to the elements list instead of the properties. For |
| 147 // proxies we have to convert as well but also respect the original order. |
| 148 // Therefore we add a converted key to both sides |
| 149 if (convert == CONVERT_TO_ARRAY_INDEX || convert == PROXY_MAGIC) { |
| 150 uint32_t index = 0; |
| 151 int prev_length = length_; |
| 152 int prev_proto = level_string_length_; |
| 153 if ((key->IsString() && Handle<String>::cast(key)->AsArrayIndex(&index)) || |
| 154 key->ToArrayIndex(&index)) { |
| 155 bool key_was_added = AddIntegerKey(index); |
| 156 if (convert == CONVERT_TO_ARRAY_INDEX) return key_was_added; |
| 157 if (convert == PROXY_MAGIC) { |
| 158 // If we had an array index (number) and it wasn't added, the key |
| 159 // already existed before, hence we cannot add it to the properties |
| 160 // keys as it would lead to duplicate entries. |
| 161 if (!key_was_added) { |
| 162 return false; |
| 163 } |
| 164 length_ = prev_length; |
| 165 level_string_length_ = prev_proto; |
| 166 } |
| 167 } |
74 } | 168 } |
75 uint32_t index; | 169 return AddStringKey(key, convert); |
76 if (convert == CONVERT_TO_ARRAY_INDEX && key->IsString() && | 170 } |
77 Handle<String>::cast(key)->AsArrayIndex(&index)) { | 171 |
78 key = isolate_->factory()->NewNumberFromUint(index); | 172 bool KeyAccumulator::AddKey(uint32_t key) { return AddIntegerKey(key); } |
| 173 |
| 174 bool KeyAccumulator::AddIntegerKey(uint32_t key) { |
| 175 // Make sure we do not add keys to a proxy-level (see AddKeysFromJSProxy). |
| 176 // We mark proxy-levels with a negative length |
| 177 DCHECK_LE(0, level_string_length_); |
| 178 // Binary search over all but the last level. The last one might not be |
| 179 // sorted yet. |
| 180 for (size_t i = 1; i < elements_.size(); i++) { |
| 181 if (AccumulatorHasKey(elements_[i - 1], key)) return false; |
79 } | 182 } |
80 keys_ = OrderedHashSet::Add(keys(), key); | 183 elements_.back()->push_back(key); |
| 184 length_++; |
| 185 return true; |
| 186 } |
| 187 |
| 188 bool KeyAccumulator::AddStringKey(Handle<Object> key, |
| 189 AddKeyConversion convert) { |
| 190 if (string_properties_.is_null()) { |
| 191 string_properties_ = OrderedHashSet::Allocate(isolate_, 16); |
| 192 } |
| 193 // TODO(cbruni): remove this conversion once we throw the correct TypeError |
| 194 // for non-string/symbol elements returned by proxies |
| 195 if (convert == PROXY_MAGIC && key->IsNumber()) { |
| 196 key = isolate_->factory()->NumberToString(key); |
| 197 } |
| 198 int prev_size = string_properties_->NumberOfElements(); |
| 199 string_properties_ = OrderedHashSet::Add(string_properties_, key); |
| 200 if (prev_size < string_properties_->NumberOfElements()) { |
| 201 length_++; |
| 202 level_string_length_++; |
| 203 return true; |
| 204 } else { |
| 205 return false; |
| 206 } |
| 207 } |
| 208 |
| 209 bool KeyAccumulator::AddSymbolKey(Handle<Object> key) { |
| 210 if (symbol_properties_.is_null()) { |
| 211 symbol_properties_ = OrderedHashSet::Allocate(isolate_, 16); |
| 212 } |
| 213 int prev_size = symbol_properties_->NumberOfElements(); |
| 214 symbol_properties_ = OrderedHashSet::Add(symbol_properties_, key); |
| 215 if (prev_size < symbol_properties_->NumberOfElements()) { |
| 216 length_++; |
| 217 level_symbol_length_++; |
| 218 return true; |
| 219 } else { |
| 220 return false; |
| 221 } |
81 } | 222 } |
82 | 223 |
83 void KeyAccumulator::AddKeys(Handle<FixedArray> array, | 224 void KeyAccumulator::AddKeys(Handle<FixedArray> array, |
84 AddKeyConversion convert) { | 225 AddKeyConversion convert) { |
85 int add_length = array->length(); | 226 int add_length = array->length(); |
| 227 if (add_length == 0) return; |
86 for (int i = 0; i < add_length; i++) { | 228 for (int i = 0; i < add_length; i++) { |
87 Handle<Object> current(array->get(i), isolate_); | 229 Handle<Object> current(array->get(i), isolate_); |
88 AddKey(current, convert); | 230 AddKey(current, convert); |
89 } | 231 } |
90 } | 232 } |
91 | 233 |
92 void KeyAccumulator::AddKeys(Handle<JSObject> array_like, | 234 void KeyAccumulator::AddKeys(Handle<JSObject> array_like, |
93 AddKeyConversion convert) { | 235 AddKeyConversion convert) { |
94 DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements()); | 236 DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements()); |
95 ElementsAccessor* accessor = array_like->GetElementsAccessor(); | 237 ElementsAccessor* accessor = array_like->GetElementsAccessor(); |
(...skipping 26 matching lines...) Expand all Loading... |
122 } | 264 } |
123 if (store_position == 0) return isolate->factory()->empty_fixed_array(); | 265 if (store_position == 0) return isolate->factory()->empty_fixed_array(); |
124 keys->Shrink(store_position); | 266 keys->Shrink(store_position); |
125 return keys; | 267 return keys; |
126 } | 268 } |
127 | 269 |
128 // Returns "nothing" in case of exception, "true" on success. | 270 // Returns "nothing" in case of exception, "true" on success. |
129 Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy, | 271 Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy, |
130 Handle<FixedArray> keys) { | 272 Handle<FixedArray> keys) { |
131 if (filter_proxy_keys_) { | 273 if (filter_proxy_keys_) { |
132 DCHECK(!is_for_in_); | |
133 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 274 ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
134 isolate_, keys, FilterProxyKeys(isolate_, proxy, keys, filter_), | 275 isolate_, keys, FilterProxyKeys(isolate_, proxy, keys, filter_), |
135 Nothing<bool>()); | 276 Nothing<bool>()); |
136 } | 277 } |
137 if (type_ == OWN_ONLY && !is_for_in_) { | 278 // Proxies define a complete list of keys with no distinction of |
138 // If we collect only the keys from a JSProxy do not sort or deduplicate it. | 279 // elements and properties, which breaks the normal assumption for the |
139 keys_ = keys; | 280 // KeyAccumulator. |
140 return Just(true); | 281 if (type_ == OWN_ONLY) { |
| 282 ownProxyKeys_ = keys; |
| 283 level_string_length_ = keys->length(); |
| 284 length_ = level_string_length_; |
| 285 } else { |
| 286 AddKeys(keys, PROXY_MAGIC); |
141 } | 287 } |
142 AddKeys(keys, is_for_in_ ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT); | 288 // Invert the current length to indicate a present proxy, so we can ignore |
| 289 // element keys for this level. Otherwise we would not fully respect the order |
| 290 // given by the proxy. |
| 291 level_string_length_ = -level_string_length_; |
143 return Just(true); | 292 return Just(true); |
144 } | 293 } |
145 | 294 |
| 295 void KeyAccumulator::AddElementKeysFromInterceptor( |
| 296 Handle<JSObject> array_like) { |
| 297 AddKeys(array_like, CONVERT_TO_ARRAY_INDEX); |
| 298 // The interceptor might introduce duplicates for the current level, since |
| 299 // these keys get added after the objects's normal element keys. |
| 300 SortCurrentElementsListRemoveDuplicates(); |
| 301 } |
| 302 |
| 303 void KeyAccumulator::SortCurrentElementsListRemoveDuplicates() { |
| 304 // Sort and remove duplicates from the current elements level and adjust. |
| 305 // the lengths accordingly. |
| 306 auto last_level = elements_.back(); |
| 307 size_t nof_removed_keys = last_level->size(); |
| 308 std::sort(last_level->begin(), last_level->end()); |
| 309 last_level->erase(std::unique(last_level->begin(), last_level->end()), |
| 310 last_level->end()); |
| 311 // Adjust total length by the number of removed duplicates. |
| 312 nof_removed_keys -= last_level->size(); |
| 313 length_ -= static_cast<int>(nof_removed_keys); |
| 314 } |
| 315 |
| 316 void KeyAccumulator::SortCurrentElementsList() { |
| 317 if (elements_.empty()) return; |
| 318 auto element_keys = elements_.back(); |
| 319 std::sort(element_keys->begin(), element_keys->end()); |
| 320 } |
| 321 |
| 322 void KeyAccumulator::NextPrototype() { |
| 323 // Store the protoLength on the first call of this method. |
| 324 if (!elements_.empty()) { |
| 325 level_lengths_.push_back(level_string_length_); |
| 326 level_lengths_.push_back(level_symbol_length_); |
| 327 } |
| 328 elements_.push_back(new std::vector<uint32_t>()); |
| 329 level_string_length_ = 0; |
| 330 level_symbol_length_ = 0; |
| 331 } |
| 332 |
146 Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver, | 333 Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver, |
147 Handle<JSReceiver> object) { | 334 Handle<JSReceiver> object) { |
148 // Proxies have no hidden prototype and we should not trigger the | 335 // Proxies have no hidden prototype and we should not trigger the |
149 // [[GetPrototypeOf]] trap on the last iteration when using | 336 // [[GetPrototypeOf]] trap on the last iteration when using |
150 // AdvanceFollowingProxies. | 337 // AdvanceFollowingProxies. |
151 if (type_ == OWN_ONLY && object->IsJSProxy()) { | 338 if (type_ == OWN_ONLY && object->IsJSProxy()) { |
152 MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)), | 339 MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)), |
153 Nothing<bool>()); | 340 Nothing<bool>()); |
154 return Just(true); | 341 return Just(true); |
155 } | 342 } |
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 return keys; | 588 return keys; |
402 } | 589 } |
403 } | 590 } |
404 // The properties-only case failed because there were probably elements on the | 591 // The properties-only case failed because there were probably elements on the |
405 // receiver. | 592 // receiver. |
406 return GetOwnKeysWithElements<true>(isolate_, object, convert); | 593 return GetOwnKeysWithElements<true>(isolate_, object, convert); |
407 } | 594 } |
408 | 595 |
409 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( | 596 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( |
410 GetKeysConversion convert) { | 597 GetKeysConversion convert) { |
411 return KeyAccumulator::GetKeys(receiver_, type_, filter_, KEEP_NUMBERS, | 598 return JSReceiver::GetKeys(receiver_, type_, filter_, KEEP_NUMBERS, |
412 filter_proxy_keys_, is_for_in_); | 599 filter_proxy_keys_); |
413 } | 600 } |
414 | 601 |
415 namespace { | |
416 | |
417 enum IndexedOrNamed { kIndexed, kNamed }; | 602 enum IndexedOrNamed { kIndexed, kNamed }; |
418 | 603 |
419 // Returns |true| on success, |nothing| on exception. | 604 // Returns |true| on success, |nothing| on exception. |
420 template <class Callback, IndexedOrNamed type> | 605 template <class Callback, IndexedOrNamed type> |
421 Maybe<bool> GetKeysFromInterceptor(Handle<JSReceiver> receiver, | 606 static Maybe<bool> GetKeysFromInterceptor(Handle<JSReceiver> receiver, |
422 Handle<JSObject> object, | 607 Handle<JSObject> object, |
423 KeyAccumulator* accumulator) { | 608 KeyAccumulator* accumulator) { |
424 Isolate* isolate = accumulator->isolate(); | 609 Isolate* isolate = accumulator->isolate(); |
425 if (type == kIndexed) { | 610 if (type == kIndexed) { |
426 if (!object->HasIndexedInterceptor()) return Just(true); | 611 if (!object->HasIndexedInterceptor()) return Just(true); |
427 } else { | 612 } else { |
428 if (!object->HasNamedInterceptor()) return Just(true); | 613 if (!object->HasNamedInterceptor()) return Just(true); |
429 } | 614 } |
430 Handle<InterceptorInfo> interceptor(type == kIndexed | 615 Handle<InterceptorInfo> interceptor(type == kIndexed |
431 ? object->GetIndexedInterceptor() | 616 ? object->GetIndexedInterceptor() |
432 : object->GetNamedInterceptor(), | 617 : object->GetNamedInterceptor(), |
433 isolate); | 618 isolate); |
434 if ((accumulator->filter() & ONLY_ALL_CAN_READ) && | 619 if ((accumulator->filter() & ONLY_ALL_CAN_READ) && |
435 !interceptor->all_can_read()) { | 620 !interceptor->all_can_read()) { |
436 return Just(true); | 621 return Just(true); |
437 } | 622 } |
438 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, | 623 PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, |
439 *object, Object::DONT_THROW); | 624 *object, Object::DONT_THROW); |
440 Handle<JSObject> result; | 625 Handle<JSObject> result; |
441 if (!interceptor->enumerator()->IsUndefined()) { | 626 if (!interceptor->enumerator()->IsUndefined()) { |
442 Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator()); | 627 Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator()); |
443 const char* log_tag = type == kIndexed ? "interceptor-indexed-enum" | 628 const char* log_tag = type == kIndexed ? "interceptor-indexed-enum" |
444 : "interceptor-named-enum"; | 629 : "interceptor-named-enum"; |
445 LOG(isolate, ApiObjectAccess(log_tag, *object)); | 630 LOG(isolate, ApiObjectAccess(log_tag, *object)); |
446 result = args.Call(enum_fun); | 631 result = args.Call(enum_fun); |
447 } | 632 } |
448 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 633 RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); |
449 if (result.is_null()) return Just(true); | 634 if (result.is_null()) return Just(true); |
450 accumulator->AddKeys( | 635 DCHECK(result->IsJSArray() || result->HasSloppyArgumentsElements()); |
451 result, type == kIndexed ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT); | 636 // The accumulator takes care of string/symbol filtering. |
| 637 if (type == kIndexed) { |
| 638 accumulator->AddElementKeysFromInterceptor(result); |
| 639 } else { |
| 640 accumulator->AddKeys(result, DO_NOT_CONVERT); |
| 641 } |
452 return Just(true); | 642 return Just(true); |
453 } | 643 } |
454 | 644 |
455 } // namespace | 645 void KeyAccumulator::CollectOwnElementIndices(Handle<JSObject> object) { |
456 | 646 if (filter_ & SKIP_STRINGS) return; |
457 Maybe<bool> KeyAccumulator::CollectOwnElementIndices( | |
458 Handle<JSReceiver> receiver, Handle<JSObject> object) { | |
459 if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true); | |
460 | |
461 ElementsAccessor* accessor = object->GetElementsAccessor(); | 647 ElementsAccessor* accessor = object->GetElementsAccessor(); |
462 accessor->CollectElementIndices(object, this); | 648 accessor->CollectElementIndices(object, this); |
463 | |
464 return GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, | |
465 kIndexed>(receiver, object, this); | |
466 } | 649 } |
467 | 650 |
468 namespace { | 651 void KeyAccumulator::CollectOwnPropertyNames(Handle<JSObject> object) { |
469 | 652 if (object->HasFastProperties()) { |
470 template <bool skip_symbols> | 653 int real_size = object->map()->NumberOfOwnDescriptors(); |
471 int CollectOwnPropertyNamesInternal(Handle<JSObject> object, | 654 Handle<DescriptorArray> descs(object->map()->instance_descriptors(), |
472 KeyAccumulator* keys, | 655 isolate_); |
473 Handle<DescriptorArray> descs, | 656 for (int i = 0; i < real_size; i++) { |
474 int start_index, int limit) { | 657 PropertyDetails details = descs->GetDetails(i); |
475 int first_skipped = -1; | 658 if ((details.attributes() & filter_) != 0) continue; |
476 for (int i = start_index; i < limit; i++) { | 659 if (filter_ & ONLY_ALL_CAN_READ) { |
477 PropertyDetails details = descs->GetDetails(i); | 660 if (details.kind() != kAccessor) continue; |
478 if ((details.attributes() & keys->filter()) != 0) continue; | 661 Object* accessors = descs->GetValue(i); |
479 if (keys->filter() & ONLY_ALL_CAN_READ) { | 662 if (!accessors->IsAccessorInfo()) continue; |
480 if (details.kind() != kAccessor) continue; | 663 if (!AccessorInfo::cast(accessors)->all_can_read()) continue; |
481 Object* accessors = descs->GetValue(i); | 664 } |
482 if (!accessors->IsAccessorInfo()) continue; | 665 Name* key = descs->GetKey(i); |
483 if (!AccessorInfo::cast(accessors)->all_can_read()) continue; | 666 if (key->FilterKey(filter_)) continue; |
| 667 AddKey(key, DO_NOT_CONVERT); |
484 } | 668 } |
485 Name* key = descs->GetKey(i); | 669 } else if (object->IsJSGlobalObject()) { |
486 if (skip_symbols == key->IsSymbol()) { | 670 GlobalDictionary::CollectKeysTo( |
487 if (first_skipped == -1) first_skipped = i; | 671 handle(object->global_dictionary(), isolate_), this, filter_); |
488 continue; | 672 } else { |
489 } | 673 NameDictionary::CollectKeysTo( |
490 if (key->FilterKey(keys->filter())) continue; | 674 handle(object->property_dictionary(), isolate_), this, filter_); |
491 keys->AddKey(key, DO_NOT_CONVERT); | |
492 } | 675 } |
493 return first_skipped; | |
494 } | |
495 | |
496 } // namespace | |
497 | |
498 Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver, | |
499 Handle<JSObject> object) { | |
500 if (filter_ == ENUMERABLE_STRINGS) { | |
501 Handle<FixedArray> enum_keys = | |
502 KeyAccumulator::GetEnumPropertyKeys(isolate_, object); | |
503 AddKeys(enum_keys, DO_NOT_CONVERT); | |
504 } else { | |
505 if (object->HasFastProperties()) { | |
506 int limit = object->map()->NumberOfOwnDescriptors(); | |
507 Handle<DescriptorArray> descs(object->map()->instance_descriptors(), | |
508 isolate_); | |
509 // First collect the strings, | |
510 int first_symbol = | |
511 CollectOwnPropertyNamesInternal<true>(object, this, descs, 0, limit); | |
512 // then the symbols. | |
513 if (first_symbol != -1) { | |
514 CollectOwnPropertyNamesInternal<false>(object, this, descs, | |
515 first_symbol, limit); | |
516 } | |
517 } else if (object->IsJSGlobalObject()) { | |
518 GlobalDictionary::CollectKeysTo( | |
519 handle(object->global_dictionary(), isolate_), this, filter_); | |
520 } else { | |
521 NameDictionary::CollectKeysTo( | |
522 handle(object->property_dictionary(), isolate_), this, filter_); | |
523 } | |
524 } | |
525 // Add the property keys from the interceptor. | |
526 return GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback, | |
527 kNamed>(receiver, object, this); | |
528 } | 676 } |
529 | 677 |
530 // Returns |true| on success, |false| if prototype walking should be stopped, | 678 // Returns |true| on success, |false| if prototype walking should be stopped, |
531 // |nothing| if an exception was thrown. | 679 // |nothing| if an exception was thrown. |
532 Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver, | 680 Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver, |
533 Handle<JSObject> object) { | 681 Handle<JSObject> object) { |
| 682 NextPrototype(); |
534 // Check access rights if required. | 683 // Check access rights if required. |
535 if (object->IsAccessCheckNeeded() && | 684 if (object->IsAccessCheckNeeded() && |
536 !isolate_->MayAccess(handle(isolate_->context()), object)) { | 685 !isolate_->MayAccess(handle(isolate_->context()), object)) { |
537 // The cross-origin spec says that [[Enumerate]] shall return an empty | 686 // The cross-origin spec says that [[Enumerate]] shall return an empty |
538 // iterator when it doesn't have access... | 687 // iterator when it doesn't have access... |
539 if (type_ == INCLUDE_PROTOS) { | 688 if (type_ == INCLUDE_PROTOS) { |
540 return Just(false); | 689 return Just(false); |
541 } | 690 } |
542 // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties. | 691 // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties. |
543 DCHECK_EQ(OWN_ONLY, type_); | 692 DCHECK_EQ(OWN_ONLY, type_); |
544 filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ); | 693 filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ); |
545 } | 694 } |
546 MAYBE_RETURN(CollectOwnElementIndices(receiver, object), Nothing<bool>()); | 695 |
547 MAYBE_RETURN(CollectOwnPropertyNames(receiver, object), Nothing<bool>()); | 696 CollectOwnElementIndices(object); |
| 697 |
| 698 // Add the element keys from the interceptor. |
| 699 Maybe<bool> success = |
| 700 GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, kIndexed>( |
| 701 receiver, object, this); |
| 702 MAYBE_RETURN(success, Nothing<bool>()); |
| 703 |
| 704 if (filter_ == ENUMERABLE_STRINGS) { |
| 705 Handle<FixedArray> enum_keys = |
| 706 KeyAccumulator::GetEnumPropertyKeys(isolate_, object); |
| 707 AddKeys(enum_keys, DO_NOT_CONVERT); |
| 708 } else { |
| 709 CollectOwnPropertyNames(object); |
| 710 } |
| 711 |
| 712 // Add the property keys from the interceptor. |
| 713 success = GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback, |
| 714 kNamed>(receiver, object, this); |
| 715 MAYBE_RETURN(success, Nothing<bool>()); |
548 return Just(true); | 716 return Just(true); |
549 } | 717 } |
550 | 718 |
551 // static | 719 // static |
552 Handle<FixedArray> KeyAccumulator::GetEnumPropertyKeys( | 720 Handle<FixedArray> KeyAccumulator::GetEnumPropertyKeys( |
553 Isolate* isolate, Handle<JSObject> object) { | 721 Isolate* isolate, Handle<JSObject> object) { |
554 if (object->HasFastProperties()) { | 722 if (object->HasFastProperties()) { |
555 return GetFastEnumPropertyKeys(isolate, object); | 723 return GetFastEnumPropertyKeys(isolate, object); |
556 } else if (object->IsJSGlobalObject()) { | 724 } else if (object->IsJSGlobalObject()) { |
557 Handle<GlobalDictionary> dictionary(object->global_dictionary(), isolate); | 725 Handle<GlobalDictionary> dictionary(object->global_dictionary(), isolate); |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
647 target_keys->get(i)); | 815 target_keys->get(i)); |
648 nonconfigurable_keys_length++; | 816 nonconfigurable_keys_length++; |
649 // The key was moved, null it out in the original list. | 817 // The key was moved, null it out in the original list. |
650 target_keys->set(i, Smi::FromInt(0)); | 818 target_keys->set(i, Smi::FromInt(0)); |
651 } else { | 819 } else { |
652 // 14c. Else, | 820 // 14c. Else, |
653 // 14c i. Append key as an element of targetConfigurableKeys. | 821 // 14c i. Append key as an element of targetConfigurableKeys. |
654 // (No-op, just keep it in |target_keys|.) | 822 // (No-op, just keep it in |target_keys|.) |
655 } | 823 } |
656 } | 824 } |
| 825 NextPrototype(); // Prepare for accumulating keys. |
657 // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty, | 826 // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty, |
658 // then: | 827 // then: |
659 if (extensible_target && nonconfigurable_keys_length == 0) { | 828 if (extensible_target && nonconfigurable_keys_length == 0) { |
660 // 15a. Return trapResult. | 829 // 15a. Return trapResult. |
661 return AddKeysFromJSProxy(proxy, trap_result); | 830 return AddKeysFromJSProxy(proxy, trap_result); |
662 } | 831 } |
663 // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult. | 832 // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult. |
664 Zone set_zone(isolate_->allocator()); | 833 Zone set_zone(isolate_->allocator()); |
665 const int kPresent = 1; | 834 const int kPresent = 1; |
666 const int kGone = 0; | 835 const int kGone = 0; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
720 // 21. Return trapResult. | 889 // 21. Return trapResult. |
721 return AddKeysFromJSProxy(proxy, trap_result); | 890 return AddKeysFromJSProxy(proxy, trap_result); |
722 } | 891 } |
723 | 892 |
724 Maybe<bool> KeyAccumulator::CollectOwnJSProxyTargetKeys( | 893 Maybe<bool> KeyAccumulator::CollectOwnJSProxyTargetKeys( |
725 Handle<JSProxy> proxy, Handle<JSReceiver> target) { | 894 Handle<JSProxy> proxy, Handle<JSReceiver> target) { |
726 // TODO(cbruni): avoid creating another KeyAccumulator | 895 // TODO(cbruni): avoid creating another KeyAccumulator |
727 Handle<FixedArray> keys; | 896 Handle<FixedArray> keys; |
728 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 897 ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
729 isolate_, keys, JSReceiver::OwnPropertyKeys(target), Nothing<bool>()); | 898 isolate_, keys, JSReceiver::OwnPropertyKeys(target), Nothing<bool>()); |
| 899 NextPrototype(); // Prepare for accumulating keys. |
730 bool prev_filter_proxy_keys_ = filter_proxy_keys_; | 900 bool prev_filter_proxy_keys_ = filter_proxy_keys_; |
731 filter_proxy_keys_ = false; | 901 filter_proxy_keys_ = false; |
732 Maybe<bool> result = AddKeysFromJSProxy(proxy, keys); | 902 Maybe<bool> result = AddKeysFromJSProxy(proxy, keys); |
733 filter_proxy_keys_ = prev_filter_proxy_keys_; | 903 filter_proxy_keys_ = prev_filter_proxy_keys_; |
734 return result; | 904 return result; |
735 } | 905 } |
736 | 906 |
737 } // namespace internal | 907 } // namespace internal |
738 } // namespace v8 | 908 } // namespace v8 |
OLD | NEW |