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 16 matching lines...) Expand all Loading... | |
27 Object* e = array->get(i); | 27 Object* e = array->get(i); |
28 if (!(e->IsName() || e->IsNumber())) return false; | 28 if (!(e->IsName() || e->IsNumber())) return false; |
29 } | 29 } |
30 return true; | 30 return true; |
31 } | 31 } |
32 | 32 |
33 } // namespace | 33 } // namespace |
34 | 34 |
35 MaybeHandle<FixedArray> KeyAccumulator::GetKeys( | 35 MaybeHandle<FixedArray> KeyAccumulator::GetKeys( |
36 Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter, | 36 Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter, |
37 GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in) { | 37 GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in, |
38 JSReceiver* first_non_empty_prototype, | |
39 JSReceiver* last_non_empty_prototype) { | |
38 USE(ContainsOnlyValidKeys); | 40 USE(ContainsOnlyValidKeys); |
39 Isolate* isolate = object->GetIsolate(); | 41 Isolate* isolate = object->GetIsolate(); |
40 KeyAccumulator accumulator(isolate, mode, filter); | 42 KeyAccumulator accumulator(isolate, mode, filter); |
41 accumulator.set_filter_proxy_keys(filter_proxy_keys); | 43 accumulator.set_filter_proxy_keys(filter_proxy_keys); |
42 accumulator.set_is_for_in(is_for_in); | 44 accumulator.set_is_for_in(is_for_in); |
45 if (first_non_empty_prototype != nullptr) { | |
46 accumulator.set_first_non_empty_prototype(first_non_empty_prototype); | |
47 } | |
48 if (first_non_empty_prototype != nullptr) { | |
49 accumulator.set_last_non_empty_prototype(last_non_empty_prototype); | |
50 } | |
43 MAYBE_RETURN(accumulator.CollectKeys(object, object), | 51 MAYBE_RETURN(accumulator.CollectKeys(object, object), |
44 MaybeHandle<FixedArray>()); | 52 MaybeHandle<FixedArray>()); |
45 Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion); | 53 Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion); |
46 DCHECK(ContainsOnlyValidKeys(keys)); | 54 DCHECK(ContainsOnlyValidKeys(keys)); |
47 return keys; | 55 return keys; |
48 } | 56 } |
49 | 57 |
50 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) { | 58 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) { |
51 if (keys_.is_null()) { | 59 if (keys_.is_null()) { |
52 return isolate_->factory()->empty_fixed_array(); | 60 return isolate_->factory()->empty_fixed_array(); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
150 // AdvanceFollowingProxies. | 158 // AdvanceFollowingProxies. |
151 if (mode_ == KeyCollectionMode::kOwnOnly && object->IsJSProxy()) { | 159 if (mode_ == KeyCollectionMode::kOwnOnly && object->IsJSProxy()) { |
152 MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)), | 160 MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)), |
153 Nothing<bool>()); | 161 Nothing<bool>()); |
154 return Just(true); | 162 return Just(true); |
155 } | 163 } |
156 | 164 |
157 PrototypeIterator::WhereToEnd end = mode_ == KeyCollectionMode::kOwnOnly | 165 PrototypeIterator::WhereToEnd end = mode_ == KeyCollectionMode::kOwnOnly |
158 ? PrototypeIterator::END_AT_NON_HIDDEN | 166 ? PrototypeIterator::END_AT_NON_HIDDEN |
159 : PrototypeIterator::END_AT_NULL; | 167 : PrototypeIterator::END_AT_NULL; |
168 if (!first_non_empty_prototype_.is_null()) { | |
169 object = first_non_empty_prototype_; | |
170 } | |
160 for (PrototypeIterator iter(isolate_, object, | 171 for (PrototypeIterator iter(isolate_, object, |
161 PrototypeIterator::START_AT_RECEIVER, end); | 172 PrototypeIterator::START_AT_RECEIVER, end); |
162 !iter.IsAtEnd();) { | 173 !iter.IsAtEnd();) { |
163 Handle<JSReceiver> current = | 174 Handle<JSReceiver> current = |
164 PrototypeIterator::GetCurrent<JSReceiver>(iter); | 175 PrototypeIterator::GetCurrent<JSReceiver>(iter); |
165 Maybe<bool> result = Just(false); // Dummy initialization. | 176 Maybe<bool> result = Just(false); // Dummy initialization. |
166 if (current->IsJSProxy()) { | 177 if (current->IsJSProxy()) { |
167 result = CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(current)); | 178 result = CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(current)); |
168 } else { | 179 } else { |
169 DCHECK(current->IsJSObject()); | 180 DCHECK(current->IsJSObject()); |
170 result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current)); | 181 result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current)); |
171 } | 182 } |
172 MAYBE_RETURN(result, Nothing<bool>()); | 183 MAYBE_RETURN(result, Nothing<bool>()); |
173 if (!result.FromJust()) break; // |false| means "stop iterating". | 184 if (!result.FromJust()) break; // |false| means "stop iterating". |
174 // Iterate through proxies but ignore access checks for the ALL_CAN_READ | 185 // Iterate through proxies but ignore access checks for the ALL_CAN_READ |
175 // case on API objects for OWN_ONLY keys handled in CollectOwnKeys. | 186 // case on API objects for OWN_ONLY keys handled in CollectOwnKeys. |
176 if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) { | 187 if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) { |
177 return Nothing<bool>(); | 188 return Nothing<bool>(); |
178 } | 189 } |
190 if (!last_non_empty_prototype_.is_null() && | |
191 *last_non_empty_prototype_ == *current) { | |
192 break; | |
193 } | |
179 } | 194 } |
180 return Just(true); | 195 return Just(true); |
181 } | 196 } |
182 | 197 |
183 namespace { | 198 namespace { |
184 | 199 |
185 void TrySettingEmptyEnumCache(JSReceiver* object) { | 200 void TrySettingEmptyEnumCache(JSReceiver* object) { |
186 Map* map = object->map(); | 201 Map* map = object->map(); |
187 DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength()); | 202 DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength()); |
188 if (!map->OnlyHasSimpleProperties()) return; | 203 if (!map->OnlyHasSimpleProperties()) return; |
(...skipping 17 matching lines...) Expand all Loading... | |
206 } | 221 } |
207 } // namespace | 222 } // namespace |
208 | 223 |
209 void FastKeyAccumulator::Prepare() { | 224 void FastKeyAccumulator::Prepare() { |
210 DisallowHeapAllocation no_gc; | 225 DisallowHeapAllocation no_gc; |
211 // Directly go for the fast path for OWN_ONLY keys. | 226 // Directly go for the fast path for OWN_ONLY keys. |
212 if (mode_ == KeyCollectionMode::kOwnOnly) return; | 227 if (mode_ == KeyCollectionMode::kOwnOnly) return; |
213 // Fully walk the prototype chain and find the last prototype with keys. | 228 // Fully walk the prototype chain and find the last prototype with keys. |
214 is_receiver_simple_enum_ = false; | 229 is_receiver_simple_enum_ = false; |
215 has_empty_prototype_ = true; | 230 has_empty_prototype_ = true; |
216 JSReceiver* first_non_empty_prototype; | 231 JSReceiver* first_prototype; |
232 JSReceiver* last_prototype; | |
217 for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd(); | 233 for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd(); |
218 iter.Advance()) { | 234 iter.Advance()) { |
219 JSReceiver* current = iter.GetCurrent<JSReceiver>(); | 235 JSReceiver* current = iter.GetCurrent<JSReceiver>(); |
220 if (CheckAndInitalizeSimpleEnumCache(current)) continue; | 236 bool has_no_properties = CheckAndInitalizeSimpleEnumCache(current); |
221 has_empty_prototype_ = false; | 237 if (has_no_properties) continue; |
222 first_non_empty_prototype = current; | 238 last_prototype = current; |
223 // TODO(cbruni): use the first non-empty prototype. | 239 if (has_empty_prototype_) { |
224 USE(first_non_empty_prototype); | 240 has_empty_prototype_ = false; |
225 return; | 241 first_prototype = current; |
242 } | |
226 } | 243 } |
227 DCHECK(has_empty_prototype_); | 244 if (has_empty_prototype_) { |
228 is_receiver_simple_enum_ = | 245 is_receiver_simple_enum_ = |
229 receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && | 246 receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && |
230 !JSObject::cast(*receiver_)->HasEnumerableElements(); | 247 !JSObject::cast(*receiver_)->HasEnumerableElements(); |
248 } else { | |
249 first_non_empty_prototype_ = handle(first_prototype, isolate_); | |
250 last_non_empty_prototype_ = handle(last_prototype, isolate_); | |
251 } | |
231 } | 252 } |
232 | 253 |
233 namespace { | 254 namespace { |
234 static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate, | 255 static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate, |
235 Handle<FixedArray> array, | 256 Handle<FixedArray> array, |
236 int length) { | 257 int length) { |
237 DCHECK_LE(length, array->length()); | 258 DCHECK_LE(length, array->length()); |
238 if (array->length() == length) return array; | 259 if (array->length() == length) return array; |
239 return isolate->factory()->CopyFixedArrayUpTo(array, length); | 260 return isolate->factory()->CopyFixedArrayUpTo(array, length); |
240 } | 261 } |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
357 } | 378 } |
358 | 379 |
359 bool OnlyHasSimpleProperties(Map* map) { | 380 bool OnlyHasSimpleProperties(Map* map) { |
360 return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; | 381 return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; |
361 } | 382 } |
362 | 383 |
363 } // namespace | 384 } // namespace |
364 | 385 |
365 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) { | 386 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) { |
366 Handle<FixedArray> keys; | 387 Handle<FixedArray> keys; |
367 if (GetKeysFast(convert).ToHandle(&keys)) { | 388 if (filter_ == ENUMERABLE_STRINGS && GetKeysFast(convert).ToHandle(&keys)) { |
368 return keys; | 389 return keys; |
369 } | 390 } |
370 return GetKeysSlow(convert); | 391 return GetKeysSlow(convert); |
371 } | 392 } |
372 | 393 |
373 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast( | 394 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast( |
374 GetKeysConversion convert) { | 395 GetKeysConversion convert) { |
375 bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly; | 396 bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly; |
376 Map* map = receiver_->map(); | 397 Map* map = receiver_->map(); |
377 if (!own_only || !OnlyHasSimpleProperties(map)) { | 398 if (!own_only || !OnlyHasSimpleProperties(map)) { |
(...skipping 23 matching lines...) Expand all Loading... | |
401 return keys; | 422 return keys; |
402 } | 423 } |
403 } | 424 } |
404 // The properties-only case failed because there were probably elements on the | 425 // The properties-only case failed because there were probably elements on the |
405 // receiver. | 426 // receiver. |
406 return GetOwnKeysWithElements<true>(isolate_, object, convert); | 427 return GetOwnKeysWithElements<true>(isolate_, object, convert); |
407 } | 428 } |
408 | 429 |
409 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( | 430 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( |
410 GetKeysConversion convert) { | 431 GetKeysConversion convert) { |
411 return KeyAccumulator::GetKeys(receiver_, mode_, filter_, | 432 return KeyAccumulator::GetKeys( |
412 GetKeysConversion::kKeepNumbers, | 433 receiver_, mode_, filter_, GetKeysConversion::kKeepNumbers, |
413 filter_proxy_keys_, is_for_in_); | 434 filter_proxy_keys_, is_for_in_, *first_non_empty_prototype_, |
Jakob Kummerow
2016/06/06 12:54:40
any particular reason for the deref/rehandlify dan
| |
435 *last_non_empty_prototype_); | |
414 } | 436 } |
415 | 437 |
416 namespace { | 438 namespace { |
417 | 439 |
418 enum IndexedOrNamed { kIndexed, kNamed }; | 440 enum IndexedOrNamed { kIndexed, kNamed }; |
419 | 441 |
420 // Returns |true| on success, |nothing| on exception. | 442 // Returns |true| on success, |nothing| on exception. |
421 template <class Callback, IndexedOrNamed type> | 443 template <class Callback, IndexedOrNamed type> |
422 Maybe<bool> GetKeysFromInterceptor(Handle<JSReceiver> receiver, | 444 Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver, |
423 Handle<JSObject> object, | 445 Handle<JSObject> object, |
424 KeyAccumulator* accumulator) { | 446 KeyAccumulator* accumulator) { |
425 Isolate* isolate = accumulator->isolate(); | 447 Isolate* isolate = accumulator->isolate(); |
426 if (type == kIndexed) { | 448 if (type == kIndexed) { |
427 if (!object->HasIndexedInterceptor()) return Just(true); | 449 if (!object->HasIndexedInterceptor()) return Just(true); |
428 } else { | 450 } else { |
429 if (!object->HasNamedInterceptor()) return Just(true); | 451 if (!object->HasNamedInterceptor()) return Just(true); |
430 } | 452 } |
431 Handle<InterceptorInfo> interceptor(type == kIndexed | 453 Handle<InterceptorInfo> interceptor(type == kIndexed |
432 ? object->GetIndexedInterceptor() | 454 ? object->GetIndexedInterceptor() |
(...skipping 22 matching lines...) Expand all Loading... | |
455 | 477 |
456 } // namespace | 478 } // namespace |
457 | 479 |
458 Maybe<bool> KeyAccumulator::CollectOwnElementIndices( | 480 Maybe<bool> KeyAccumulator::CollectOwnElementIndices( |
459 Handle<JSReceiver> receiver, Handle<JSObject> object) { | 481 Handle<JSReceiver> receiver, Handle<JSObject> object) { |
460 if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true); | 482 if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true); |
461 | 483 |
462 ElementsAccessor* accessor = object->GetElementsAccessor(); | 484 ElementsAccessor* accessor = object->GetElementsAccessor(); |
463 accessor->CollectElementIndices(object, this); | 485 accessor->CollectElementIndices(object, this); |
464 | 486 |
465 return GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, | 487 return CollectInterceptorKeys<v8::IndexedPropertyEnumeratorCallback, |
466 kIndexed>(receiver, object, this); | 488 kIndexed>(receiver, object, this); |
467 } | 489 } |
468 | 490 |
469 namespace { | 491 namespace { |
470 | 492 |
471 template <bool skip_symbols> | 493 template <bool skip_symbols> |
472 int CollectOwnPropertyNamesInternal(Handle<JSObject> object, | 494 int CollectOwnPropertyNamesInternal(Handle<JSObject> object, |
473 KeyAccumulator* keys, | 495 KeyAccumulator* keys, |
474 Handle<DescriptorArray> descs, | 496 Handle<DescriptorArray> descs, |
475 int start_index, int limit) { | 497 int start_index, int limit) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
517 } | 539 } |
518 } else if (object->IsJSGlobalObject()) { | 540 } else if (object->IsJSGlobalObject()) { |
519 GlobalDictionary::CollectKeysTo( | 541 GlobalDictionary::CollectKeysTo( |
520 handle(object->global_dictionary(), isolate_), this, filter_); | 542 handle(object->global_dictionary(), isolate_), this, filter_); |
521 } else { | 543 } else { |
522 NameDictionary::CollectKeysTo( | 544 NameDictionary::CollectKeysTo( |
523 handle(object->property_dictionary(), isolate_), this, filter_); | 545 handle(object->property_dictionary(), isolate_), this, filter_); |
524 } | 546 } |
525 } | 547 } |
526 // Add the property keys from the interceptor. | 548 // Add the property keys from the interceptor. |
527 return GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback, | 549 return CollectInterceptorKeys<v8::GenericNamedPropertyEnumeratorCallback, |
528 kNamed>(receiver, object, this); | 550 kNamed>(receiver, object, this); |
529 } | 551 } |
530 | 552 |
531 // Returns |true| on success, |false| if prototype walking should be stopped, | 553 // Returns |true| on success, |false| if prototype walking should be stopped, |
532 // |nothing| if an exception was thrown. | 554 // |nothing| if an exception was thrown. |
533 Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver, | 555 Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver, |
534 Handle<JSObject> object) { | 556 Handle<JSObject> object) { |
535 // Check access rights if required. | 557 // Check access rights if required. |
536 if (object->IsAccessCheckNeeded() && | 558 if (object->IsAccessCheckNeeded() && |
537 !isolate_->MayAccess(handle(isolate_->context()), object)) { | 559 !isolate_->MayAccess(handle(isolate_->context()), object)) { |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
730 isolate_, keys, JSReceiver::OwnPropertyKeys(target), Nothing<bool>()); | 752 isolate_, keys, JSReceiver::OwnPropertyKeys(target), Nothing<bool>()); |
731 bool prev_filter_proxy_keys_ = filter_proxy_keys_; | 753 bool prev_filter_proxy_keys_ = filter_proxy_keys_; |
732 filter_proxy_keys_ = false; | 754 filter_proxy_keys_ = false; |
733 Maybe<bool> result = AddKeysFromJSProxy(proxy, keys); | 755 Maybe<bool> result = AddKeysFromJSProxy(proxy, keys); |
734 filter_proxy_keys_ = prev_filter_proxy_keys_; | 756 filter_proxy_keys_ = prev_filter_proxy_keys_; |
735 return result; | 757 return result; |
736 } | 758 } |
737 | 759 |
738 } // namespace internal | 760 } // namespace internal |
739 } // namespace v8 | 761 } // namespace v8 |
OLD | NEW |