Line data Source code
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
3 : // found in the LICENSE file.
4 :
5 : #include "src/keys.h"
6 :
7 : #include "src/api-arguments.h"
8 : #include "src/elements.h"
9 : #include "src/factory.h"
10 : #include "src/identity-map.h"
11 : #include "src/isolate-inl.h"
12 : #include "src/objects-inl.h"
13 : #include "src/property-descriptor.h"
14 : #include "src/prototype.h"
15 :
16 : namespace v8 {
17 : namespace internal {
18 :
19 9030154 : KeyAccumulator::~KeyAccumulator() {
20 9030154 : }
21 :
22 : namespace {
23 :
24 : static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
25 : int len = array->length();
26 : for (int i = 0; i < len; i++) {
27 : Object* e = array->get(i);
28 : if (!(e->IsName() || e->IsNumber())) return false;
29 : }
30 : return true;
31 : }
32 :
33 : } // namespace
34 :
35 : // static
36 2061692 : MaybeHandle<FixedArray> KeyAccumulator::GetKeys(
37 : Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
38 : GetKeysConversion keys_conversion, bool is_for_in) {
39 : Isolate* isolate = object->GetIsolate();
40 : FastKeyAccumulator accumulator(isolate, object, mode, filter);
41 : accumulator.set_is_for_in(is_for_in);
42 2061692 : return accumulator.GetKeys(keys_conversion);
43 : }
44 :
45 9657471 : Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
46 : if (keys_.is_null()) {
47 133512 : return isolate_->factory()->empty_fixed_array();
48 : }
49 10019887 : if (mode_ == KeyCollectionMode::kOwnOnly &&
50 495928 : keys_->map() == isolate_->heap()->fixed_array_map()) {
51 : return Handle<FixedArray>::cast(keys_);
52 : }
53 : USE(ContainsOnlyValidKeys);
54 : Handle<FixedArray> result =
55 9521644 : OrderedHashSet::ConvertToKeysArray(keys(), convert);
56 : DCHECK(ContainsOnlyValidKeys(result));
57 9521644 : return result;
58 : }
59 :
60 9573689 : void KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) {
61 19147378 : AddKey(handle(key, isolate_), convert);
62 9573689 : }
63 :
64 78475721 : void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
65 78475721 : if (key->IsSymbol()) {
66 52584 : if (filter_ & SKIP_SYMBOLS) return;
67 37204 : if (Handle<Symbol>::cast(key)->is_private()) return;
68 78438503 : } else if (filter_ & SKIP_STRINGS) {
69 : return;
70 : }
71 78475700 : if (IsShadowed(key)) return;
72 78460357 : if (keys_.is_null()) {
73 9521686 : keys_ = OrderedHashSet::Allocate(isolate_, 16);
74 : }
75 : uint32_t index;
76 78462838 : if (convert == CONVERT_TO_ARRAY_INDEX && key->IsString() &&
77 1096 : Handle<String>::cast(key)->AsArrayIndex(&index)) {
78 140 : key = isolate_->factory()->NewNumberFromUint(index);
79 : }
80 78460357 : keys_ = OrderedHashSet::Add(keys(), key);
81 : }
82 :
83 16048561 : void KeyAccumulator::AddKeys(Handle<FixedArray> array,
84 : AddKeyConversion convert) {
85 : int add_length = array->length();
86 84649912 : for (int i = 0; i < add_length; i++) {
87 68601351 : Handle<Object> current(array->get(i), isolate_);
88 68601351 : AddKey(current, convert);
89 : }
90 16048561 : }
91 :
92 218 : void KeyAccumulator::AddKeys(Handle<JSObject> array_like,
93 : AddKeyConversion convert) {
94 : DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements());
95 218 : ElementsAccessor* accessor = array_like->GetElementsAccessor();
96 218 : accessor->AddElementsToKeyAccumulator(array_like, this, convert);
97 218 : }
98 :
99 3915 : MaybeHandle<FixedArray> FilterProxyKeys(KeyAccumulator* accumulator,
100 : Handle<JSProxy> owner,
101 : Handle<FixedArray> keys,
102 : PropertyFilter filter) {
103 2441 : if (filter == ALL_PROPERTIES) {
104 : // Nothing to do.
105 : return keys;
106 : }
107 : Isolate* isolate = accumulator->isolate();
108 : int store_position = 0;
109 10516 : for (int i = 0; i < keys->length(); ++i) {
110 : Handle<Name> key(Name::cast(keys->get(i)), isolate);
111 3896 : if (key->FilterKey(filter)) continue; // Skip this key.
112 3714 : if (filter & ONLY_ENUMERABLE) {
113 : PropertyDescriptor desc;
114 : Maybe<bool> found =
115 1974 : JSProxy::GetOwnPropertyDescriptor(isolate, owner, key, &desc);
116 2086 : MAYBE_RETURN(found, MaybeHandle<FixedArray>());
117 2156 : if (!found.FromJust()) continue;
118 1680 : if (!desc.enumerable()) {
119 112 : accumulator->AddShadowingKey(key);
120 112 : continue;
121 : }
122 : }
123 : // Keep this key.
124 3308 : if (store_position != i) {
125 224 : keys->set(store_position, *key);
126 : }
127 3308 : store_position++;
128 : }
129 1362 : if (store_position == 0) return isolate->factory()->empty_fixed_array();
130 1136 : keys->Shrink(store_position);
131 : return keys;
132 : }
133 :
134 : // Returns "nothing" in case of exception, "true" on success.
135 1402991 : Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy,
136 : Handle<FixedArray> keys) {
137 : // Postpone the enumerable check for for-in to the ForInFilter step.
138 1402991 : if (!is_for_in_) {
139 4882 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
140 : isolate_, keys, FilterProxyKeys(this, proxy, keys, filter_),
141 : Nothing<bool>());
142 2329 : if (mode_ == KeyCollectionMode::kOwnOnly) {
143 : // If we collect only the keys from a JSProxy do not sort or deduplicate.
144 2315 : keys_ = keys;
145 : return Just(true);
146 : }
147 : }
148 1400564 : AddKeys(keys, is_for_in_ ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
149 : return Just(true);
150 : }
151 :
152 9654188 : Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver,
153 : Handle<JSReceiver> object) {
154 : // Proxies have no hidden prototype and we should not trigger the
155 : // [[GetPrototypeOf]] trap on the last iteration when using
156 : // AdvanceFollowingProxies.
157 10279894 : if (mode_ == KeyCollectionMode::kOwnOnly && object->IsJSProxy()) {
158 2917 : MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)),
159 : Nothing<bool>());
160 : return Just(true);
161 : }
162 :
163 9651271 : PrototypeIterator::WhereToEnd end = mode_ == KeyCollectionMode::kOwnOnly
164 : ? PrototypeIterator::END_AT_NON_HIDDEN
165 9651271 : : PrototypeIterator::END_AT_NULL;
166 35996087 : for (PrototypeIterator iter(isolate_, object, kStartAtReceiver, end);
167 26344816 : !iter.IsAtEnd();) {
168 : // Start the shadow checks only after the first prototype has added
169 : // shadowing keys.
170 16696341 : if (HasShadowingKeys()) skip_shadow_check_ = false;
171 : Handle<JSReceiver> current =
172 : PrototypeIterator::GetCurrent<JSReceiver>(iter);
173 : Maybe<bool> result = Just(false); // Dummy initialization.
174 16696341 : if (current->IsJSProxy()) {
175 1400648 : result = CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(current));
176 : } else {
177 : DCHECK(current->IsJSObject());
178 15295693 : result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current));
179 : }
180 16696340 : MAYBE_RETURN(result, Nothing<bool>());
181 16696214 : if (!result.FromJust()) break; // |false| means "stop iterating".
182 : // Iterate through proxies but ignore access checks for the ALL_CAN_READ
183 : // case on API objects for OWN_ONLY keys handled in CollectOwnKeys.
184 16696166 : if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
185 : return Nothing<bool>();
186 : }
187 16701713 : if (!last_non_empty_prototype_.is_null() &&
188 : *last_non_empty_prototype_ == *current) {
189 : break;
190 : }
191 : }
192 : return Just(true);
193 : }
194 :
195 0 : bool KeyAccumulator::HasShadowingKeys() { return !shadowing_keys_.is_null(); }
196 :
197 78475702 : bool KeyAccumulator::IsShadowed(Handle<Object> key) {
198 78475702 : if (!HasShadowingKeys() || skip_shadow_check_) return false;
199 32270 : return shadowing_keys_->Has(isolate_, key);
200 : }
201 :
202 16036878 : void KeyAccumulator::AddShadowingKey(Object* key) {
203 32073756 : if (mode_ == KeyCollectionMode::kOwnOnly) return;
204 32067154 : AddShadowingKey(handle(key, isolate_));
205 : }
206 16033689 : void KeyAccumulator::AddShadowingKey(Handle<Object> key) {
207 32067378 : if (mode_ == KeyCollectionMode::kOwnOnly) return;
208 16033577 : if (shadowing_keys_.is_null()) {
209 1332088 : shadowing_keys_ = ObjectHashSet::New(isolate_, 16);
210 : }
211 16033577 : shadowing_keys_ = ObjectHashSet::Add(shadowing_keys_, key);
212 : }
213 :
214 : namespace {
215 :
216 9439 : void TrySettingEmptyEnumCache(JSReceiver* object) {
217 : Map* map = object->map();
218 : DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength());
219 9439 : if (!map->OnlyHasSimpleProperties()) return;
220 7790 : if (map->IsJSProxyMap()) return;
221 7790 : if (map->NumberOfOwnDescriptors() > 0) {
222 : int number_of_enumerable_own_properties =
223 7577 : map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
224 7577 : if (number_of_enumerable_own_properties > 0) return;
225 : }
226 : DCHECK(object->IsJSObject());
227 : map->SetEnumLength(0);
228 : }
229 :
230 159647 : bool CheckAndInitalizeEmptyEnumCache(JSReceiver* object) {
231 159647 : if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) {
232 9439 : TrySettingEmptyEnumCache(object);
233 : }
234 159647 : if (object->map()->EnumLength() != 0) return false;
235 : DCHECK(object->IsJSObject());
236 156920 : return !JSObject::cast(object)->HasEnumerableElements();
237 : }
238 : } // namespace
239 :
240 2149882 : void FastKeyAccumulator::Prepare() {
241 : DisallowHeapAllocation no_gc;
242 : // Directly go for the fast path for OWN_ONLY keys.
243 4299764 : if (mode_ == KeyCollectionMode::kOwnOnly) return;
244 : // Fully walk the prototype chain and find the last prototype with keys.
245 88484 : is_receiver_simple_enum_ = false;
246 88484 : has_empty_prototype_ = true;
247 : JSReceiver* last_prototype = nullptr;
248 336615 : for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
249 159647 : iter.Advance()) {
250 159647 : JSReceiver* current = iter.GetCurrent<JSReceiver>();
251 159647 : bool has_no_properties = CheckAndInitalizeEmptyEnumCache(current);
252 159647 : if (has_no_properties) continue;
253 : last_prototype = current;
254 2785 : has_empty_prototype_ = false;
255 : }
256 88484 : if (has_empty_prototype_) {
257 : is_receiver_simple_enum_ =
258 151862 : receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel &&
259 151862 : !JSObject::cast(*receiver_)->HasEnumerableElements();
260 2656 : } else if (last_prototype != nullptr) {
261 5312 : last_non_empty_prototype_ = handle(last_prototype, isolate_);
262 : }
263 : }
264 :
265 : namespace {
266 14514589 : static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
267 : Handle<FixedArray> array,
268 : int length) {
269 : DCHECK_LE(length, array->length());
270 14514589 : if (array->length() == length) return array;
271 3388467 : return isolate->factory()->CopyFixedArrayUpTo(array, length);
272 : }
273 :
274 : // Initializes and directly returns the enume cache. Users of this function
275 : // have to make sure to never directly leak the enum cache.
276 32143696 : Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
277 : Handle<JSObject> object) {
278 : Handle<Map> map(object->map());
279 16071848 : bool cache_enum_length = map->OnlyHasSimpleProperties();
280 :
281 : Handle<DescriptorArray> descs =
282 : Handle<DescriptorArray>(map->instance_descriptors(), isolate);
283 : int own_property_count = map->EnumLength();
284 : // If the enum length of the given map is set to kInvalidEnumCache, this
285 : // means that the map itself has never used the present enum cache. The
286 : // first step to using the cache is to set the enum length of the map by
287 : // counting the number of own descriptors that are ENUMERABLE_STRINGS.
288 16071848 : if (own_property_count == kInvalidEnumCacheSentinel) {
289 : own_property_count =
290 160927 : map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
291 : } else {
292 : DCHECK(
293 : own_property_count ==
294 : map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS));
295 : }
296 :
297 16071848 : if (descs->HasEnumCache()) {
298 14534594 : Handle<FixedArray> keys(descs->GetEnumCache(), isolate);
299 : // In case the number of properties required in the enum are actually
300 : // present, we can reuse the enum cache. Otherwise, this means that the
301 : // enum cache was generated for a previous (smaller) version of the
302 : // Descriptor Array. In that case we regenerate the enum cache.
303 14534594 : if (own_property_count <= keys->length()) {
304 14514589 : isolate->counters()->enum_cache_hits()->Increment();
305 14514589 : if (cache_enum_length) map->SetEnumLength(own_property_count);
306 14514589 : return ReduceFixedArrayTo(isolate, keys, own_property_count);
307 : }
308 : }
309 :
310 1557259 : if (descs->IsEmpty()) {
311 1442312 : isolate->counters()->enum_cache_hits()->Increment();
312 1442312 : if (cache_enum_length) map->SetEnumLength(0);
313 : return isolate->factory()->empty_fixed_array();
314 : }
315 :
316 114947 : isolate->counters()->enum_cache_misses()->Increment();
317 :
318 : Handle<FixedArray> storage =
319 114947 : isolate->factory()->NewFixedArray(own_property_count);
320 : Handle<FixedArray> indices =
321 114947 : isolate->factory()->NewFixedArray(own_property_count);
322 :
323 : int size = map->NumberOfOwnDescriptors();
324 : int index = 0;
325 :
326 840855 : for (int i = 0; i < size; i++) {
327 725908 : PropertyDetails details = descs->GetDetails(i);
328 725908 : if (details.IsDontEnum()) continue;
329 : Object* key = descs->GetKey(i);
330 648419 : if (key->IsSymbol()) continue;
331 645692 : storage->set(index, key);
332 645692 : if (!indices.is_null()) {
333 : if (details.location() == kField) {
334 : DCHECK_EQ(kData, details.kind());
335 608070 : FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
336 608070 : int load_by_field_index = field_index.GetLoadByFieldIndex();
337 : indices->set(index, Smi::FromInt(load_by_field_index));
338 : } else {
339 : indices = Handle<FixedArray>();
340 : }
341 : }
342 645692 : index++;
343 : }
344 : DCHECK(index == storage->length());
345 :
346 114947 : DescriptorArray::SetEnumCache(descs, isolate, storage, indices);
347 114947 : if (cache_enum_length) {
348 : map->SetEnumLength(own_property_count);
349 : }
350 114947 : return storage;
351 : }
352 :
353 : template <bool fast_properties>
354 1488371 : MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
355 : Handle<JSObject> object,
356 : GetKeysConversion convert) {
357 : Handle<FixedArray> keys;
358 1488371 : ElementsAccessor* accessor = object->GetElementsAccessor();
359 : if (fast_properties) {
360 1476895 : keys = GetFastEnumPropertyKeys(isolate, object);
361 : } else {
362 : // TODO(cbruni): preallocate big enough array to also hold elements.
363 11476 : keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate, object);
364 : }
365 : MaybeHandle<FixedArray> result =
366 1488371 : accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
367 :
368 1488371 : if (FLAG_trace_for_in_enumerate) {
369 0 : PrintF("| strings=%d symbols=0 elements=%u || prototypes>=1 ||\n",
370 0 : keys->length(), result.ToHandleChecked()->length() - keys->length());
371 : }
372 1488371 : return result;
373 : }
374 :
375 : bool OnlyHasSimpleProperties(Map* map) {
376 1549967 : return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER;
377 : }
378 :
379 : } // namespace
380 :
381 2145366 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(
382 : GetKeysConversion keys_conversion) {
383 2145366 : if (filter_ == ENUMERABLE_STRINGS) {
384 : Handle<FixedArray> keys;
385 3105246 : if (GetKeysFast(keys_conversion).ToHandle(&keys)) {
386 : return keys;
387 : }
388 35316 : if (isolate_->has_pending_exception()) return MaybeHandle<FixedArray>();
389 : }
390 :
391 628059 : return GetKeysSlow(keys_conversion);
392 : }
393 :
394 1552623 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
395 : GetKeysConversion keys_conversion) {
396 1552623 : bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly;
397 : Map* map = receiver_->map();
398 3102590 : if (!own_only || !OnlyHasSimpleProperties(map)) {
399 : return MaybeHandle<FixedArray>();
400 : }
401 :
402 : // From this point on we are certiain to only collect own keys.
403 : DCHECK(receiver_->IsJSObject());
404 : Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
405 :
406 : // Do not try to use the enum-cache for dict-mode objects.
407 1517307 : if (map->is_dictionary_map()) {
408 11476 : return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion);
409 : }
410 : int enum_length = receiver_->map()->EnumLength();
411 1505831 : if (enum_length == kInvalidEnumCacheSentinel) {
412 : Handle<FixedArray> keys;
413 : // Try initializing the enum cache and return own properties.
414 63308 : if (GetOwnKeysWithUninitializedEnumCache().ToHandle(&keys)) {
415 28936 : if (FLAG_trace_for_in_enumerate) {
416 : PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n",
417 0 : keys->length());
418 : }
419 : is_receiver_simple_enum_ =
420 28936 : object->map()->EnumLength() != kInvalidEnumCacheSentinel;
421 : return keys;
422 : }
423 : }
424 : // The properties-only case failed because there were probably elements on the
425 : // receiver.
426 1476895 : return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion);
427 : }
428 :
429 : MaybeHandle<FixedArray>
430 31654 : FastKeyAccumulator::GetOwnKeysWithUninitializedEnumCache() {
431 : Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
432 : // Uninitalized enum cache
433 : Map* map = object->map();
434 31654 : if (object->elements()->length() != 0) {
435 : // Assume that there are elements.
436 : return MaybeHandle<FixedArray>();
437 : }
438 : int number_of_own_descriptors = map->NumberOfOwnDescriptors();
439 28936 : if (number_of_own_descriptors == 0) {
440 : map->SetEnumLength(0);
441 2374 : return isolate_->factory()->empty_fixed_array();
442 : }
443 : // We have no elements but possibly enumerable property keys, hence we can
444 : // directly initialize the enum cache.
445 26562 : Handle<FixedArray> keys = GetFastEnumPropertyKeys(isolate_, object);
446 26562 : if (is_for_in_) return keys;
447 : // Do not leak the enum cache as it might end up as an elements backing store.
448 15301 : return isolate_->factory()->CopyFixedArray(keys);
449 : }
450 :
451 628059 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
452 : GetKeysConversion keys_conversion) {
453 628059 : KeyAccumulator accumulator(isolate_, mode_, filter_);
454 628059 : accumulator.set_is_for_in(is_for_in_);
455 : accumulator.set_last_non_empty_prototype(last_non_empty_prototype_);
456 :
457 628059 : MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_),
458 : MaybeHandle<FixedArray>());
459 627317 : return accumulator.GetKeys(keys_conversion);
460 : }
461 :
462 : namespace {
463 :
464 : enum IndexedOrNamed { kIndexed, kNamed };
465 :
466 : // Returns |true| on success, |nothing| on exception.
467 : template <class Callback, IndexedOrNamed type>
468 324 : Maybe<bool> CollectInterceptorKeysInternal(Handle<JSReceiver> receiver,
469 : Handle<JSObject> object,
470 : Handle<InterceptorInfo> interceptor,
471 324 : KeyAccumulator* accumulator) {
472 324 : Isolate* isolate = accumulator->isolate();
473 : PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
474 : *object, Object::DONT_THROW);
475 : Handle<JSObject> result;
476 324 : if (!interceptor->enumerator()->IsUndefined(isolate)) {
477 : Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator());
478 : const char* log_tag = type == kIndexed ? "interceptor-indexed-enum"
479 : : "interceptor-named-enum";
480 324 : LOG(isolate, ApiObjectAccess(log_tag, *object));
481 324 : result = args.Call(enum_fun);
482 : }
483 324 : RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
484 296 : if (result.is_null()) return Just(true);
485 218 : accumulator->AddKeys(
486 : result, type == kIndexed ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
487 : return Just(true);
488 : }
489 :
490 : template <class Callback, IndexedOrNamed type>
491 30465759 : Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver,
492 : Handle<JSObject> object,
493 30466027 : KeyAccumulator* accumulator) {
494 : Isolate* isolate = accumulator->isolate();
495 : if (type == kIndexed) {
496 15169890 : if (!object->HasIndexedInterceptor()) return Just(true);
497 : } else {
498 15295869 : if (!object->HasNamedInterceptor()) return Just(true);
499 : }
500 : Handle<InterceptorInfo> interceptor(type == kIndexed
501 : ? object->GetIndexedInterceptor()
502 : : object->GetNamedInterceptor(),
503 268 : isolate);
504 268 : if ((accumulator->filter() & ONLY_ALL_CAN_READ) &&
505 : !interceptor->all_can_read()) {
506 : return Just(true);
507 : }
508 : return CollectInterceptorKeysInternal<Callback, type>(
509 268 : receiver, object, interceptor, accumulator);
510 : }
511 :
512 : } // namespace
513 :
514 15306571 : Maybe<bool> KeyAccumulator::CollectOwnElementIndices(
515 : Handle<JSReceiver> receiver, Handle<JSObject> object) {
516 15306571 : if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true);
517 :
518 15169890 : ElementsAccessor* accessor = object->GetElementsAccessor();
519 15169890 : accessor->CollectElementIndices(object, this);
520 :
521 : return CollectInterceptorKeys<v8::IndexedPropertyEnumeratorCallback,
522 15169890 : kIndexed>(receiver, object, this);
523 : }
524 :
525 : namespace {
526 :
527 : template <bool skip_symbols>
528 594019 : int CollectOwnPropertyNamesInternal(Handle<JSObject> object,
529 4615384 : KeyAccumulator* keys,
530 : Handle<DescriptorArray> descs,
531 : int start_index, int limit) {
532 : int first_skipped = -1;
533 : PropertyFilter filter = keys->filter();
534 : KeyCollectionMode mode = keys->mode();
535 4089208 : for (int i = start_index; i < limit; i++) {
536 : bool is_shadowing_key = false;
537 4089208 : PropertyDetails details = descs->GetDetails(i);
538 :
539 4089208 : if ((details.attributes() & filter) != 0) {
540 175 : if (mode == KeyCollectionMode::kIncludePrototypes) {
541 : is_shadowing_key = true;
542 : } else {
543 : continue;
544 : }
545 : }
546 :
547 4089201 : if (filter & ONLY_ALL_CAN_READ) {
548 14 : if (details.kind() != kAccessor) continue;
549 : Object* accessors = descs->GetValue(i);
550 7 : if (!accessors->IsAccessorInfo()) continue;
551 7 : if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
552 : }
553 :
554 : Name* key = descs->GetKey(i);
555 4089194 : if (skip_symbols == key->IsSymbol()) {
556 67829 : if (first_skipped == -1) first_skipped = i;
557 : continue;
558 : }
559 4021365 : if (key->FilterKey(keys->filter())) continue;
560 :
561 3697595 : if (is_shadowing_key) {
562 168 : keys->AddShadowingKey(key);
563 : } else {
564 3697427 : keys->AddKey(key, DO_NOT_CONVERT);
565 : }
566 : }
567 594019 : return first_skipped;
568 : }
569 :
570 : template <class T>
571 98422 : Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
572 : KeyCollectionMode mode,
573 : KeyAccumulator* accumulator,
574 : Handle<JSObject> object,
575 : T* raw_dictionary) {
576 : Handle<T> dictionary(raw_dictionary, isolate);
577 : int length = dictionary->NumberOfEnumElements();
578 98422 : if (length == 0) {
579 : return isolate->factory()->empty_fixed_array();
580 : }
581 97481 : Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
582 97481 : T::CopyEnumKeysTo(dictionary, storage, mode, accumulator);
583 97481 : return storage;
584 : }
585 : } // namespace
586 :
587 15295870 : Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
588 : Handle<JSObject> object) {
589 15295870 : if (filter_ == ENUMERABLE_STRINGS) {
590 : Handle<FixedArray> enum_keys;
591 14647997 : if (object->HasFastProperties()) {
592 14561171 : enum_keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate_, object);
593 : // If the number of properties equals the length of enumerable properties
594 : // we do not have to filter out non-enumerable ones
595 : Map* map = object->map();
596 : int nof_descriptors = map->NumberOfOwnDescriptors();
597 14561171 : if (enum_keys->length() != nof_descriptors) {
598 : Handle<DescriptorArray> descs =
599 1335454 : Handle<DescriptorArray>(map->instance_descriptors(), isolate_);
600 17356731 : for (int i = 0; i < nof_descriptors; i++) {
601 16021277 : PropertyDetails details = descs->GetDetails(i);
602 16026339 : if (!details.IsDontEnum()) continue;
603 : Object* key = descs->GetKey(i);
604 16016215 : this->AddShadowingKey(key);
605 : }
606 : }
607 86826 : } else if (object->IsJSGlobalObject()) {
608 : enum_keys = GetOwnEnumPropertyDictionaryKeys(
609 28891 : isolate_, mode_, this, object, object->global_dictionary());
610 : } else {
611 : enum_keys = GetOwnEnumPropertyDictionaryKeys(
612 57935 : isolate_, mode_, this, object, object->property_dictionary());
613 : }
614 14647997 : AddKeys(enum_keys, DO_NOT_CONVERT);
615 : } else {
616 647873 : if (object->HasFastProperties()) {
617 : int limit = object->map()->NumberOfOwnDescriptors();
618 : Handle<DescriptorArray> descs(object->map()->instance_descriptors(),
619 581596 : isolate_);
620 : // First collect the strings,
621 : int first_symbol =
622 581596 : CollectOwnPropertyNamesInternal<true>(object, this, descs, 0, limit);
623 : // then the symbols.
624 581596 : if (first_symbol != -1) {
625 : CollectOwnPropertyNamesInternal<false>(object, this, descs,
626 12423 : first_symbol, limit);
627 : }
628 66277 : } else if (object->IsJSGlobalObject()) {
629 : GlobalDictionary::CollectKeysTo(
630 113362 : handle(object->global_dictionary(), isolate_), this);
631 : } else {
632 : NameDictionary::CollectKeysTo(
633 19192 : handle(object->property_dictionary(), isolate_), this);
634 : }
635 : }
636 : // Add the property keys from the interceptor.
637 : return CollectInterceptorKeys<v8::GenericNamedPropertyEnumeratorCallback,
638 15295870 : kNamed>(receiver, object, this);
639 : }
640 :
641 28 : Maybe<bool> KeyAccumulator::CollectAccessCheckInterceptorKeys(
642 : Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
643 : Handle<JSObject> object) {
644 56 : MAYBE_RETURN(
645 : (CollectInterceptorKeysInternal<v8::IndexedPropertyEnumeratorCallback,
646 : kIndexed>(
647 : receiver, object,
648 : handle(
649 : InterceptorInfo::cast(access_check_info->indexed_interceptor()),
650 : isolate_),
651 : this)),
652 : Nothing<bool>());
653 56 : MAYBE_RETURN(
654 : (CollectInterceptorKeysInternal<
655 : v8::GenericNamedPropertyEnumeratorCallback, kNamed>(
656 : receiver, object,
657 : handle(InterceptorInfo::cast(access_check_info->named_interceptor()),
658 : isolate_),
659 : this)),
660 : Nothing<bool>());
661 : return Just(true);
662 : }
663 :
664 : // Returns |true| on success, |false| if prototype walking should be stopped,
665 : // |nothing| if an exception was thrown.
666 15295693 : Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
667 : Handle<JSObject> object) {
668 : // Check access rights if required.
669 15295815 : if (object->IsAccessCheckNeeded() &&
670 244 : !isolate_->MayAccess(handle(isolate_->context()), object)) {
671 : // The cross-origin spec says that [[Enumerate]] shall return an empty
672 : // iterator when it doesn't have access...
673 88 : if (mode_ == KeyCollectionMode::kIncludePrototypes) {
674 : return Just(false);
675 : }
676 : // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
677 : DCHECK(KeyCollectionMode::kOwnOnly == mode_);
678 : Handle<AccessCheckInfo> access_check_info;
679 : {
680 : DisallowHeapAllocation no_gc;
681 68 : AccessCheckInfo* maybe_info = AccessCheckInfo::Get(isolate_, object);
682 68 : if (maybe_info) access_check_info = handle(maybe_info, isolate_);
683 : }
684 : // We always have both kinds of interceptors or none.
685 122 : if (!access_check_info.is_null() &&
686 : access_check_info->named_interceptor()) {
687 28 : MAYBE_RETURN(CollectAccessCheckInterceptorKeys(access_check_info,
688 : receiver, object),
689 : Nothing<bool>());
690 : return Just(false);
691 : }
692 40 : filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ);
693 : }
694 15295645 : MAYBE_RETURN(CollectOwnElementIndices(receiver, object), Nothing<bool>());
695 15295630 : MAYBE_RETURN(CollectOwnPropertyNames(receiver, object), Nothing<bool>());
696 : return Just(true);
697 : }
698 :
699 : // static
700 14579987 : Handle<FixedArray> KeyAccumulator::GetOwnEnumPropertyKeys(
701 : Isolate* isolate, Handle<JSObject> object) {
702 14579987 : if (object->HasFastProperties()) {
703 14568391 : return GetFastEnumPropertyKeys(isolate, object);
704 11596 : } else if (object->IsJSGlobalObject()) {
705 : return GetOwnEnumPropertyDictionaryKeys(
706 : isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
707 30 : object->global_dictionary());
708 : } else {
709 : return GetOwnEnumPropertyDictionaryKeys(
710 : isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
711 11566 : object->property_dictionary());
712 : }
713 : }
714 :
715 : namespace {
716 :
717 : struct NameComparator {
718 : bool operator()(uint32_t hash1, uint32_t hash2, const Handle<Name>& key1,
719 : const Handle<Name>& key2) const {
720 820 : return Name::Equals(key1, key2);
721 : }
722 : };
723 :
724 : } // namespace
725 :
726 : // ES6 9.5.12
727 : // Returns |true| on success, |nothing| in case of exception.
728 1403565 : Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver,
729 : Handle<JSProxy> proxy) {
730 2807398 : STACK_CHECK(isolate_, Nothing<bool>());
731 : // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
732 : Handle<Object> handler(proxy->handler(), isolate_);
733 : // 2. If handler is null, throw a TypeError exception.
734 : // 3. Assert: Type(handler) is Object.
735 1403565 : if (proxy->IsRevoked()) {
736 : isolate_->Throw(*isolate_->factory()->NewTypeError(
737 84 : MessageTemplate::kProxyRevoked, isolate_->factory()->ownKeys_string()));
738 : return Nothing<bool>();
739 : }
740 : // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
741 1403537 : Handle<JSReceiver> target(proxy->target(), isolate_);
742 : // 5. Let trap be ? GetMethod(handler, "ownKeys").
743 : Handle<Object> trap;
744 4210611 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
745 : isolate_, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
746 : isolate_->factory()->ownKeys_string()),
747 : Nothing<bool>());
748 : // 6. If trap is undefined, then
749 2806962 : if (trap->IsUndefined(isolate_)) {
750 : // 6a. Return target.[[OwnPropertyKeys]]().
751 1401504 : return CollectOwnJSProxyTargetKeys(proxy, target);
752 : }
753 : // 7. Let trapResultArray be Call(trap, handler, «target»).
754 : Handle<Object> trap_result_array;
755 : Handle<Object> args[] = {target};
756 3954 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
757 : isolate_, trap_result_array,
758 : Execution::Call(isolate_, trap, handler, arraysize(args), args),
759 : Nothing<bool>());
760 : // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray,
761 : // «String, Symbol»).
762 : Handle<FixedArray> trap_result;
763 3422 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
764 : isolate_, trap_result,
765 : Object::CreateListFromArrayLike(isolate_, trap_result_array,
766 : ElementTypes::kStringAndSymbol),
767 : Nothing<bool>());
768 : // 9. Let extensibleTarget be ? IsExtensible(target).
769 1529 : Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target);
770 1529 : MAYBE_RETURN(maybe_extensible, Nothing<bool>());
771 : bool extensible_target = maybe_extensible.FromJust();
772 : // 10. Let targetKeys be ? target.[[OwnPropertyKeys]]().
773 : Handle<FixedArray> target_keys;
774 3058 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, target_keys,
775 : JSReceiver::OwnPropertyKeys(target),
776 : Nothing<bool>());
777 : // 11. (Assert)
778 : // 12. Let targetConfigurableKeys be an empty List.
779 : // To save memory, we're re-using target_keys and will modify it in-place.
780 : Handle<FixedArray> target_configurable_keys = target_keys;
781 : // 13. Let targetNonconfigurableKeys be an empty List.
782 : Handle<FixedArray> target_nonconfigurable_keys =
783 1529 : isolate_->factory()->NewFixedArray(target_keys->length());
784 : int nonconfigurable_keys_length = 0;
785 : // 14. Repeat, for each element key of targetKeys:
786 5362 : for (int i = 0; i < target_keys->length(); ++i) {
787 : // 14a. Let desc be ? target.[[GetOwnProperty]](key).
788 : PropertyDescriptor desc;
789 : Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor(
790 2304 : isolate_, target, handle(target_keys->get(i), isolate_), &desc);
791 1152 : MAYBE_RETURN(found, Nothing<bool>());
792 : // 14b. If desc is not undefined and desc.[[Configurable]] is false, then
793 2304 : if (found.FromJust() && !desc.configurable()) {
794 : // 14b i. Append key as an element of targetNonconfigurableKeys.
795 : target_nonconfigurable_keys->set(nonconfigurable_keys_length,
796 423 : target_keys->get(i));
797 423 : nonconfigurable_keys_length++;
798 : // The key was moved, null it out in the original list.
799 : target_keys->set(i, Smi::kZero);
800 : } else {
801 : // 14c. Else,
802 : // 14c i. Append key as an element of targetConfigurableKeys.
803 : // (No-op, just keep it in |target_keys|.)
804 : }
805 : }
806 : // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty,
807 : // then:
808 1529 : if (extensible_target && nonconfigurable_keys_length == 0) {
809 : // 15a. Return trapResult.
810 1261 : return AddKeysFromJSProxy(proxy, trap_result);
811 : }
812 : // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult.
813 536 : Zone set_zone(isolate_->allocator(), ZONE_NAME);
814 : ZoneAllocationPolicy alloc(&set_zone);
815 : const int kPresent = 1;
816 : const int kGone = 0;
817 : base::TemplateHashMapImpl<Handle<Name>, int, NameComparator,
818 : ZoneAllocationPolicy>
819 : unchecked_result_keys(ZoneHashMap::kDefaultHashMapCapacity,
820 : NameComparator(), alloc);
821 : int unchecked_result_keys_size = 0;
822 2448 : for (int i = 0; i < trap_result->length(); ++i) {
823 956 : Handle<Name> key(Name::cast(trap_result->get(i)), isolate_);
824 : auto entry = unchecked_result_keys.LookupOrInsert(key, key->Hash(), alloc);
825 956 : if (entry->value != kPresent) {
826 774 : entry->value = kPresent;
827 774 : unchecked_result_keys_size++;
828 : }
829 : }
830 : // 17. Repeat, for each key that is an element of targetNonconfigurableKeys:
831 409 : for (int i = 0; i < nonconfigurable_keys_length; ++i) {
832 : Object* raw_key = target_nonconfigurable_keys->get(i);
833 423 : Handle<Name> key(Name::cast(raw_key), isolate_);
834 : // 17a. If key is not an element of uncheckedResultKeys, throw a
835 : // TypeError exception.
836 : auto found = unchecked_result_keys.Lookup(key, key->Hash());
837 423 : if (found == nullptr || found->value == kGone) {
838 : isolate_->Throw(*isolate_->factory()->NewTypeError(
839 28 : MessageTemplate::kProxyOwnKeysMissing, key));
840 14 : return Nothing<bool>();
841 : }
842 : // 17b. Remove key from uncheckedResultKeys.
843 409 : found->value = kGone;
844 409 : unchecked_result_keys_size--;
845 : }
846 : // 18. If extensibleTarget is true, return trapResult.
847 254 : if (extensible_target) {
848 99 : return AddKeysFromJSProxy(proxy, trap_result);
849 : }
850 : // 19. Repeat, for each key that is an element of targetConfigurableKeys:
851 1055 : for (int i = 0; i < target_configurable_keys->length(); ++i) {
852 : Object* raw_key = target_configurable_keys->get(i);
853 745 : if (raw_key->IsSmi()) continue; // Zapped entry, was nonconfigurable.
854 183 : Handle<Name> key(Name::cast(raw_key), isolate_);
855 : // 19a. If key is not an element of uncheckedResultKeys, throw a
856 : // TypeError exception.
857 : auto found = unchecked_result_keys.Lookup(key, key->Hash());
858 183 : if (found == nullptr || found->value == kGone) {
859 : isolate_->Throw(*isolate_->factory()->NewTypeError(
860 28 : MessageTemplate::kProxyOwnKeysMissing, key));
861 14 : return Nothing<bool>();
862 : }
863 : // 19b. Remove key from uncheckedResultKeys.
864 169 : found->value = kGone;
865 169 : unchecked_result_keys_size--;
866 : }
867 : // 20. If uncheckedResultKeys is not empty, throw a TypeError exception.
868 141 : if (unchecked_result_keys_size != 0) {
869 : DCHECK_GT(unchecked_result_keys_size, 0);
870 : isolate_->Throw(*isolate_->factory()->NewTypeError(
871 28 : MessageTemplate::kProxyOwnKeysNonExtensible));
872 : return Nothing<bool>();
873 : }
874 : // 21. Return trapResult.
875 127 : return AddKeysFromJSProxy(proxy, trap_result);
876 : }
877 :
878 1401504 : Maybe<bool> KeyAccumulator::CollectOwnJSProxyTargetKeys(
879 : Handle<JSProxy> proxy, Handle<JSReceiver> target) {
880 : // TODO(cbruni): avoid creating another KeyAccumulator
881 : Handle<FixedArray> keys;
882 2803008 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
883 : isolate_, keys,
884 : KeyAccumulator::GetKeys(target, KeyCollectionMode::kOwnOnly, filter_,
885 : GetKeysConversion::kConvertToString, is_for_in_),
886 : Nothing<bool>());
887 1401504 : Maybe<bool> result = AddKeysFromJSProxy(proxy, keys);
888 1401504 : return result;
889 : }
890 :
891 : } // namespace internal
892 : } // namespace v8
|