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-inl.h"
8 : #include "src/elements-inl.h"
9 : #include "src/handles-inl.h"
10 : #include "src/heap/factory.h"
11 : #include "src/identity-map.h"
12 : #include "src/isolate-inl.h"
13 : #include "src/objects-inl.h"
14 : #include "src/objects/api-callbacks.h"
15 : #include "src/objects/hash-table-inl.h"
16 : #include "src/objects/module-inl.h"
17 : #include "src/property-descriptor.h"
18 : #include "src/prototype.h"
19 :
20 : namespace v8 {
21 : namespace internal {
22 :
23 : namespace {
24 :
25 : static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
26 : int len = array->length();
27 : for (int i = 0; i < len; i++) {
28 : Object e = array->get(i);
29 : if (!(e->IsName() || e->IsNumber())) return false;
30 : }
31 : return true;
32 : }
33 :
34 : } // namespace
35 :
36 : // static
37 1332406 : MaybeHandle<FixedArray> KeyAccumulator::GetKeys(
38 : Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
39 : GetKeysConversion keys_conversion, bool is_for_in, bool skip_indices) {
40 : Isolate* isolate = object->GetIsolate();
41 : FastKeyAccumulator accumulator(isolate, object, mode, filter, is_for_in,
42 : skip_indices);
43 1332406 : return accumulator.GetKeys(keys_conversion);
44 : }
45 :
46 1431845 : Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
47 1182342 : if (keys_.is_null()) {
48 931126 : return isolate_->factory()->empty_fixed_array();
49 : }
50 495086 : if (mode_ == KeyCollectionMode::kOwnOnly &&
51 243870 : keys_->map() == ReadOnlyRoots(isolate_).fixed_array_map()) {
52 1713 : return Handle<FixedArray>::cast(keys_);
53 : }
54 : USE(ContainsOnlyValidKeys);
55 : Handle<FixedArray> result =
56 249503 : OrderedHashSet::ConvertToKeysArray(isolate(), keys(), convert);
57 : DCHECK(ContainsOnlyValidKeys(result));
58 249503 : return result;
59 : }
60 :
61 0 : Handle<OrderedHashSet> KeyAccumulator::keys() {
62 10241282 : return Handle<OrderedHashSet>::cast(keys_);
63 : }
64 :
65 3937117 : void KeyAccumulator::AddKey(Object key, AddKeyConversion convert) {
66 7874234 : AddKey(handle(key, isolate_), convert);
67 3937117 : }
68 :
69 19992945 : void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
70 20002332 : if (key->IsSymbol()) {
71 55116 : if (filter_ & SKIP_SYMBOLS) return;
72 91326 : if (Handle<Symbol>::cast(key)->is_private()) return;
73 9955437 : } else if (filter_ & SKIP_STRINGS) {
74 : return;
75 : }
76 10001094 : if (IsShadowed(key)) return;
77 9991779 : if (keys_.is_null()) {
78 249536 : keys_ = OrderedHashSet::Allocate(isolate_, 16);
79 : }
80 : uint32_t index;
81 19988367 : if (convert == CONVERT_TO_ARRAY_INDEX && key->IsString() &&
82 9994201 : Handle<String>::cast(key)->AsArrayIndex(&index)) {
83 392 : key = isolate_->factory()->NewNumberFromUint(index);
84 : }
85 9991779 : Handle<OrderedHashSet> new_set = OrderedHashSet::Add(isolate(), keys(), key);
86 9991779 : if (*new_set != *keys_) {
87 : // The keys_ Set is converted directly to a FixedArray in GetKeys which can
88 : // be left-trimmer. Hence the previous Set should not keep a pointer to the
89 : // new one.
90 178473 : keys_->set(OrderedHashSet::NextTableIndex(), Smi::kZero);
91 178473 : keys_ = new_set;
92 : }
93 : }
94 :
95 994194 : void KeyAccumulator::AddKeys(Handle<FixedArray> array,
96 : AddKeyConversion convert) {
97 : int add_length = array->length();
98 3540524 : for (int i = 0; i < add_length; i++) {
99 2546330 : Handle<Object> current(array->get(i), isolate_);
100 2546330 : AddKey(current, convert);
101 : }
102 994194 : }
103 :
104 309 : void KeyAccumulator::AddKeys(Handle<JSObject> array_like,
105 : AddKeyConversion convert) {
106 : DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements());
107 309 : ElementsAccessor* accessor = array_like->GetElementsAccessor();
108 309 : accessor->AddElementsToKeyAccumulator(array_like, this, convert);
109 309 : }
110 :
111 3016 : MaybeHandle<FixedArray> FilterProxyKeys(KeyAccumulator* accumulator,
112 : Handle<JSProxy> owner,
113 : Handle<FixedArray> keys,
114 : PropertyFilter filter) {
115 1832 : if (filter == ALL_PROPERTIES) {
116 : // Nothing to do.
117 648 : return keys;
118 : }
119 : Isolate* isolate = accumulator->isolate();
120 : int store_position = 0;
121 9518 : for (int i = 0; i < keys->length(); ++i) {
122 : Handle<Name> key(Name::cast(keys->get(i)), isolate);
123 3647 : if (key->FilterKey(filter)) continue; // Skip this key.
124 3356 : if (filter & ONLY_ENUMERABLE) {
125 : PropertyDescriptor desc;
126 : Maybe<bool> found =
127 1709 : JSProxy::GetOwnPropertyDescriptor(isolate, owner, key, &desc);
128 1709 : MAYBE_RETURN(found, MaybeHandle<FixedArray>());
129 1880 : if (!found.FromJust()) continue;
130 1520 : if (!desc.enumerable()) {
131 126 : accumulator->AddShadowingKey(key);
132 126 : continue;
133 : }
134 : }
135 : // Keep this key.
136 3041 : if (store_position != i) {
137 450 : keys->set(store_position, *key);
138 : }
139 3041 : store_position++;
140 : }
141 1112 : return FixedArray::ShrinkOrEmpty(isolate, keys, store_position);
142 : }
143 :
144 : // Returns "nothing" in case of exception, "true" on success.
145 923927 : Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy,
146 : Handle<FixedArray> keys) {
147 : // Postpone the enumerable check for for-in to the ForInFilter step.
148 923927 : if (!is_for_in_) {
149 3664 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
150 : isolate_, keys, FilterProxyKeys(this, proxy, keys, filter_),
151 : Nothing<bool>());
152 1760 : if (mode_ == KeyCollectionMode::kOwnOnly) {
153 : // If we collect only the keys from a JSProxy do not sort or deduplicate.
154 1713 : keys_ = keys;
155 : return Just(true);
156 : }
157 : }
158 922142 : AddKeys(keys, is_for_in_ ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
159 : return Just(true);
160 : }
161 :
162 1195368 : Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver,
163 : Handle<JSReceiver> object) {
164 : // Proxies have no hidden prototype and we should not trigger the
165 : // [[GetPrototypeOf]] trap on the last iteration when using
166 : // AdvanceFollowingProxies.
167 3570260 : if (mode_ == KeyCollectionMode::kOwnOnly && object->IsJSProxy()) {
168 19833 : MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)),
169 : Nothing<bool>());
170 : return Just(true);
171 : }
172 :
173 1175535 : PrototypeIterator::WhereToEnd end = mode_ == KeyCollectionMode::kOwnOnly
174 : ? PrototypeIterator::END_AT_NON_HIDDEN
175 1175535 : : PrototypeIterator::END_AT_NULL;
176 4495592 : for (PrototypeIterator iter(isolate_, object, kStartAtReceiver, end);
177 3320057 : !iter.IsAtEnd();) {
178 : // Start the shadow checks only after the first prototype has added
179 : // shadowing keys.
180 2146084 : if (HasShadowingKeys()) skip_shadow_check_ = false;
181 : Handle<JSReceiver> current =
182 : PrototypeIterator::GetCurrent<JSReceiver>(iter);
183 : Maybe<bool> result = Just(false); // Dummy initialization.
184 4292168 : if (current->IsJSProxy()) {
185 922196 : result = CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(current));
186 : } else {
187 : DCHECK(current->IsJSObject());
188 1223888 : result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current));
189 : }
190 2146084 : MAYBE_RETURN(result, Nothing<bool>());
191 2145937 : if (!result.FromJust()) break; // |false| means "stop iterating".
192 : // Iterate through proxies but ignore access checks for the ALL_CAN_READ
193 : // case on API objects for OWN_ONLY keys handled in CollectOwnKeys.
194 2145900 : if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
195 : return Nothing<bool>();
196 : }
197 2148858 : if (!last_non_empty_prototype_.is_null() &&
198 : *last_non_empty_prototype_ == *current) {
199 : break;
200 : }
201 : }
202 : return Just(true);
203 : }
204 :
205 0 : bool KeyAccumulator::HasShadowingKeys() { return !shadowing_keys_.is_null(); }
206 :
207 10001094 : bool KeyAccumulator::IsShadowed(Handle<Object> key) {
208 10001094 : if (!HasShadowingKeys() || skip_shadow_check_) return false;
209 19854 : return shadowing_keys_->Has(isolate_, key);
210 : }
211 :
212 109317 : void KeyAccumulator::AddShadowingKey(Object key) {
213 218634 : if (mode_ == KeyCollectionMode::kOwnOnly) return;
214 211352 : AddShadowingKey(handle(key, isolate_));
215 : }
216 211478 : void KeyAccumulator::AddShadowingKey(Handle<Object> key) {
217 211604 : if (mode_ == KeyCollectionMode::kOwnOnly) return;
218 105676 : if (shadowing_keys_.is_null()) {
219 6588 : shadowing_keys_ = ObjectHashSet::New(isolate_, 16);
220 : }
221 105676 : shadowing_keys_ = ObjectHashSet::Add(isolate(), shadowing_keys_, key);
222 : }
223 :
224 : namespace {
225 :
226 4968 : void TrySettingEmptyEnumCache(JSReceiver object) {
227 4968 : Map map = object->map();
228 : DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength());
229 5944 : if (!map->OnlyHasSimpleProperties()) return;
230 4334 : if (map->IsJSProxyMap()) return;
231 4334 : if (map->NumberOfEnumerableProperties() > 0) return;
232 : DCHECK(object->IsJSObject());
233 3992 : map->SetEnumLength(0);
234 : }
235 :
236 101636 : bool CheckAndInitalizeEmptyEnumCache(JSReceiver object) {
237 101636 : if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) {
238 4968 : TrySettingEmptyEnumCache(object);
239 : }
240 101636 : if (object->map()->EnumLength() != 0) return false;
241 : DCHECK(object->IsJSObject());
242 100192 : return !JSObject::cast(object)->HasEnumerableElements();
243 : }
244 : } // namespace
245 :
246 1386680 : void FastKeyAccumulator::Prepare() {
247 : DisallowHeapAllocation no_gc;
248 : // Directly go for the fast path for OWN_ONLY keys.
249 2773360 : if (mode_ == KeyCollectionMode::kOwnOnly) return;
250 : // Fully walk the prototype chain and find the last prototype with keys.
251 54274 : is_receiver_simple_enum_ = false;
252 54274 : has_empty_prototype_ = true;
253 : JSReceiver last_prototype;
254 210184 : for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
255 101636 : iter.Advance()) {
256 101636 : JSReceiver current = iter.GetCurrent<JSReceiver>();
257 101636 : bool has_no_properties = CheckAndInitalizeEmptyEnumCache(current);
258 201783 : if (has_no_properties) continue;
259 1489 : last_prototype = current;
260 1489 : has_empty_prototype_ = false;
261 : }
262 54274 : if (has_empty_prototype_) {
263 : is_receiver_simple_enum_ =
264 96981 : receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel &&
265 96981 : !JSObject::cast(*receiver_)->HasEnumerableElements();
266 1404 : } else if (!last_prototype.is_null()) {
267 2808 : last_non_empty_prototype_ = handle(last_prototype, isolate_);
268 : }
269 : }
270 :
271 : namespace {
272 :
273 174610 : Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
274 : Handle<FixedArray> array, int length) {
275 : DCHECK_LE(length, array->length());
276 174610 : if (array->length() == length) return array;
277 276 : return isolate->factory()->CopyFixedArrayUpTo(array, length);
278 : }
279 :
280 : // Initializes and directly returns the enume cache. Users of this function
281 : // have to make sure to never directly leak the enum cache.
282 222586 : Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
283 : Handle<JSObject> object) {
284 : Handle<Map> map(object->map(), isolate);
285 445172 : Handle<FixedArray> keys(map->instance_descriptors()->enum_cache()->keys(),
286 445172 : isolate);
287 :
288 : // Check if the {map} has a valid enum length, which implies that it
289 : // must have a valid enum cache as well.
290 : int enum_length = map->EnumLength();
291 222586 : if (enum_length != kInvalidEnumCacheSentinel) {
292 : DCHECK(map->OnlyHasSimpleProperties());
293 : DCHECK_LE(enum_length, keys->length());
294 : DCHECK_EQ(enum_length, map->NumberOfEnumerableProperties());
295 148467 : isolate->counters()->enum_cache_hits()->Increment();
296 148467 : return ReduceFixedArrayTo(isolate, keys, enum_length);
297 : }
298 :
299 : // Determine the actual number of enumerable properties of the {map}.
300 74119 : enum_length = map->NumberOfEnumerableProperties();
301 :
302 : // Check if there's already a shared enum cache on the {map}s
303 : // DescriptorArray with sufficient number of entries.
304 74119 : if (enum_length <= keys->length()) {
305 31944 : if (map->OnlyHasSimpleProperties()) map->SetEnumLength(enum_length);
306 26143 : isolate->counters()->enum_cache_hits()->Increment();
307 26143 : return ReduceFixedArrayTo(isolate, keys, enum_length);
308 : }
309 :
310 : Handle<DescriptorArray> descriptors =
311 95952 : Handle<DescriptorArray>(map->instance_descriptors(), isolate);
312 47976 : isolate->counters()->enum_cache_misses()->Increment();
313 : int nod = map->NumberOfOwnDescriptors();
314 :
315 : // Create the keys array.
316 : int index = 0;
317 : bool fields_only = true;
318 47976 : keys = isolate->factory()->NewFixedArray(enum_length);
319 351789 : for (int i = 0; i < nod; i++) {
320 : DisallowHeapAllocation no_gc;
321 303813 : PropertyDetails details = descriptors->GetDetails(i);
322 305604 : if (details.IsDontEnum()) continue;
323 302142 : Object key = descriptors->GetKey(i);
324 302142 : if (key->IsSymbol()) continue;
325 302022 : keys->set(index, key);
326 302022 : if (details.location() != kField) fields_only = false;
327 302022 : index++;
328 : }
329 : DCHECK_EQ(index, keys->length());
330 :
331 : // Optionally also create the indices array.
332 : Handle<FixedArray> indices = isolate->factory()->empty_fixed_array();
333 47976 : if (fields_only) {
334 29992 : indices = isolate->factory()->NewFixedArray(enum_length);
335 : index = 0;
336 262891 : for (int i = 0; i < nod; i++) {
337 : DisallowHeapAllocation no_gc;
338 232899 : PropertyDetails details = descriptors->GetDetails(i);
339 234320 : if (details.IsDontEnum()) continue;
340 231580 : Object key = descriptors->GetKey(i);
341 231580 : if (key->IsSymbol()) continue;
342 : DCHECK_EQ(kData, details.kind());
343 : DCHECK_EQ(kField, details.location());
344 231478 : FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
345 231478 : indices->set(index, Smi::FromInt(field_index.GetLoadByFieldIndex()));
346 231478 : index++;
347 : }
348 : DCHECK_EQ(index, indices->length());
349 : }
350 :
351 : DescriptorArray::InitializeOrChangeEnumCache(descriptors, isolate, keys,
352 47976 : indices);
353 95854 : if (map->OnlyHasSimpleProperties()) map->SetEnumLength(enum_length);
354 :
355 47976 : return keys;
356 : }
357 :
358 : template <bool fast_properties>
359 141835 : MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
360 : Handle<JSObject> object,
361 : GetKeysConversion convert,
362 : bool skip_indices) {
363 : Handle<FixedArray> keys;
364 141835 : ElementsAccessor* accessor = object->GetElementsAccessor();
365 : if (fast_properties) {
366 135486 : keys = GetFastEnumPropertyKeys(isolate, object);
367 : } else {
368 : // TODO(cbruni): preallocate big enough array to also hold elements.
369 6349 : keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate, object);
370 : }
371 :
372 : MaybeHandle<FixedArray> result;
373 141835 : if (skip_indices) {
374 : result = keys;
375 : } else {
376 141751 : result =
377 : accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
378 : }
379 :
380 141835 : if (FLAG_trace_for_in_enumerate) {
381 0 : PrintF("| strings=%d symbols=0 elements=%u || prototypes>=1 ||\n",
382 0 : keys->length(), result.ToHandleChecked()->length() - keys->length());
383 : }
384 141835 : return result;
385 : }
386 :
387 : } // namespace
388 :
389 1383840 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(
390 : GetKeysConversion keys_conversion) {
391 1383840 : if (filter_ == ENUMERABLE_STRINGS) {
392 : Handle<FixedArray> keys;
393 471406 : if (GetKeysFast(keys_conversion).ToHandle(&keys)) {
394 195169 : return keys;
395 : }
396 40534 : if (isolate_->has_pending_exception()) return MaybeHandle<FixedArray>();
397 : }
398 :
399 1188671 : return GetKeysSlow(keys_conversion);
400 : }
401 :
402 235703 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
403 : GetKeysConversion keys_conversion) {
404 235703 : bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly;
405 235703 : Map map = receiver_->map();
406 470002 : if (!own_only || map->IsCustomElementsReceiverMap()) {
407 40534 : return MaybeHandle<FixedArray>();
408 : }
409 :
410 : // From this point on we are certain to only collect own keys.
411 : DCHECK(receiver_->IsJSObject());
412 195169 : Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
413 :
414 : // Do not try to use the enum-cache for dict-mode objects.
415 195169 : if (map->is_dictionary_map()) {
416 : return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion,
417 6349 : skip_indices_);
418 : }
419 : int enum_length = receiver_->map()->EnumLength();
420 188820 : if (enum_length == kInvalidEnumCacheSentinel) {
421 : Handle<FixedArray> keys;
422 : // Try initializing the enum cache and return own properties.
423 110882 : if (GetOwnKeysWithUninitializedEnumCache().ToHandle(&keys)) {
424 53334 : if (FLAG_trace_for_in_enumerate) {
425 : PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n",
426 0 : keys->length());
427 : }
428 : is_receiver_simple_enum_ =
429 53334 : object->map()->EnumLength() != kInvalidEnumCacheSentinel;
430 53334 : return keys;
431 : }
432 : }
433 : // The properties-only case failed because there were probably elements on the
434 : // receiver.
435 : return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion,
436 135486 : skip_indices_);
437 : }
438 :
439 : MaybeHandle<FixedArray>
440 55441 : FastKeyAccumulator::GetOwnKeysWithUninitializedEnumCache() {
441 55441 : Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
442 : // Uninitalized enum cache
443 55441 : Map map = object->map();
444 110882 : if (object->elements()->length() != 0) {
445 : // Assume that there are elements.
446 2107 : return MaybeHandle<FixedArray>();
447 : }
448 : int number_of_own_descriptors = map->NumberOfOwnDescriptors();
449 53334 : if (number_of_own_descriptors == 0) {
450 4249 : map->SetEnumLength(0);
451 8498 : return isolate_->factory()->empty_fixed_array();
452 : }
453 : // We have no elements but possibly enumerable property keys, hence we can
454 : // directly initialize the enum cache.
455 49085 : Handle<FixedArray> keys = GetFastEnumPropertyKeys(isolate_, object);
456 49085 : if (is_for_in_) return keys;
457 : // Do not leak the enum cache as it might end up as an elements backing store.
458 44420 : return isolate_->factory()->CopyFixedArray(keys);
459 : }
460 :
461 1188671 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
462 : GetKeysConversion keys_conversion) {
463 1188671 : KeyAccumulator accumulator(isolate_, mode_, filter_);
464 1188671 : accumulator.set_is_for_in(is_for_in_);
465 1188671 : accumulator.set_skip_indices(skip_indices_);
466 : accumulator.set_last_non_empty_prototype(last_non_empty_prototype_);
467 :
468 1188671 : MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_),
469 : MaybeHandle<FixedArray>());
470 1170395 : return accumulator.GetKeys(keys_conversion);
471 : }
472 :
473 : namespace {
474 :
475 : enum IndexedOrNamed { kIndexed, kNamed };
476 :
477 60 : void FilterForEnumerableProperties(Handle<JSReceiver> receiver,
478 : Handle<JSObject> object,
479 : Handle<InterceptorInfo> interceptor,
480 156 : KeyAccumulator* accumulator,
481 : Handle<JSObject> result,
482 : IndexedOrNamed type) {
483 : DCHECK(result->IsJSArray() || result->HasSloppyArgumentsElements());
484 60 : ElementsAccessor* accessor = result->GetElementsAccessor();
485 :
486 180 : uint32_t length = accessor->GetCapacity(*result, result->elements());
487 282 : for (uint32_t i = 0; i < length; i++) {
488 510 : if (!accessor->HasEntry(*result, i)) continue;
489 :
490 : // args are invalid after args.Call(), create a new one in every iteration.
491 : PropertyCallbackArguments args(accumulator->isolate(), interceptor->data(),
492 468 : *receiver, *object, kDontThrow);
493 :
494 156 : Handle<Object> element = accessor->Get(result, i);
495 : Handle<Object> attributes;
496 156 : if (type == kIndexed) {
497 : uint32_t number;
498 84 : CHECK(element->ToUint32(&number));
499 84 : attributes = args.CallIndexedQuery(interceptor, number);
500 : } else {
501 144 : CHECK(element->IsName());
502 : attributes =
503 72 : args.CallNamedQuery(interceptor, Handle<Name>::cast(element));
504 : }
505 :
506 156 : if (!attributes.is_null()) {
507 : int32_t value;
508 144 : CHECK(attributes->ToInt32(&value));
509 144 : if ((value & DONT_ENUM) == 0) {
510 48 : accumulator->AddKey(element, DO_NOT_CONVERT);
511 : }
512 : }
513 : }
514 60 : }
515 :
516 : // Returns |true| on success, |nothing| on exception.
517 475 : Maybe<bool> CollectInterceptorKeysInternal(Handle<JSReceiver> receiver,
518 : Handle<JSObject> object,
519 : Handle<InterceptorInfo> interceptor,
520 844 : KeyAccumulator* accumulator,
521 : IndexedOrNamed type) {
522 : Isolate* isolate = accumulator->isolate();
523 : PropertyCallbackArguments enum_args(isolate, interceptor->data(), *receiver,
524 950 : *object, kDontThrow);
525 :
526 : Handle<JSObject> result;
527 950 : if (!interceptor->enumerator()->IsUndefined(isolate)) {
528 475 : if (type == kIndexed) {
529 235 : result = enum_args.CallIndexedEnumerator(interceptor);
530 : } else {
531 : DCHECK_EQ(type, kNamed);
532 240 : result = enum_args.CallNamedEnumerator(interceptor);
533 : }
534 : }
535 475 : RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
536 445 : if (result.is_null()) return Just(true);
537 :
538 1107 : if ((accumulator->filter() & ONLY_ENUMERABLE) &&
539 607 : !interceptor->query()->IsUndefined(isolate)) {
540 : FilterForEnumerableProperties(receiver, object, interceptor, accumulator,
541 60 : result, type);
542 : } else {
543 : accumulator->AddKeys(
544 309 : result, type == kIndexed ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
545 : }
546 : return Just(true);
547 : }
548 :
549 2451814 : Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver,
550 : Handle<JSObject> object,
551 2452249 : KeyAccumulator* accumulator,
552 : IndexedOrNamed type) {
553 : Isolate* isolate = accumulator->isolate();
554 2451814 : if (type == kIndexed) {
555 1227981 : if (!object->HasIndexedInterceptor()) return Just(true);
556 : } else {
557 1223833 : if (!object->HasNamedInterceptor()) return Just(true);
558 : }
559 : Handle<InterceptorInfo> interceptor(type == kIndexed
560 865 : ? object->GetIndexedInterceptor()
561 655 : : object->GetNamedInterceptor(),
562 1090 : isolate);
563 1305 : if ((accumulator->filter() & ONLY_ALL_CAN_READ) &&
564 435 : !interceptor->all_can_read()) {
565 : return Just(true);
566 : }
567 : return CollectInterceptorKeysInternal(receiver, object, interceptor,
568 435 : accumulator, type);
569 : }
570 :
571 : } // namespace
572 :
573 1228549 : Maybe<bool> KeyAccumulator::CollectOwnElementIndices(
574 : Handle<JSReceiver> receiver, Handle<JSObject> object) {
575 1228549 : if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true);
576 :
577 1227981 : ElementsAccessor* accessor = object->GetElementsAccessor();
578 1227981 : accessor->CollectElementIndices(object, this);
579 :
580 1227981 : return CollectInterceptorKeys(receiver, object, this, kIndexed);
581 : }
582 :
583 : namespace {
584 :
585 : template <bool skip_symbols>
586 1147384 : int CollectOwnPropertyNamesInternal(Handle<JSObject> object,
587 2186592 : KeyAccumulator* keys,
588 : Handle<DescriptorArray> descs,
589 : int start_index, int limit) {
590 : int first_skipped = -1;
591 : PropertyFilter filter = keys->filter();
592 : KeyCollectionMode mode = keys->mode();
593 1210962 : for (int i = start_index; i < limit; i++) {
594 : bool is_shadowing_key = false;
595 1210962 : PropertyDetails details = descs->GetDetails(i);
596 :
597 1210962 : if ((details.attributes() & filter) != 0) {
598 12 : if (mode == KeyCollectionMode::kIncludePrototypes) {
599 : is_shadowing_key = true;
600 : } else {
601 183821 : continue;
602 : }
603 : }
604 :
605 1210950 : if (filter & ONLY_ALL_CAN_READ) {
606 18 : if (details.kind() != kAccessor) continue;
607 6 : Object accessors = descs->GetStrongValue(i);
608 6 : if (!accessors->IsAccessorInfo()) continue;
609 6 : if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
610 : }
611 :
612 1210944 : Name key = descs->GetKey(i);
613 1210944 : if (skip_symbols == key->IsSymbol()) {
614 171736 : if (first_skipped == -1) first_skipped = i;
615 : continue;
616 : }
617 1039208 : if (key->FilterKey(keys->filter())) continue;
618 :
619 1027141 : if (is_shadowing_key) {
620 0 : keys->AddShadowingKey(key);
621 : } else {
622 1027141 : keys->AddKey(key, DO_NOT_CONVERT);
623 : }
624 : }
625 1147384 : return first_skipped;
626 : }
627 :
628 : template <class T>
629 43149 : Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
630 : KeyCollectionMode mode,
631 : KeyAccumulator* accumulator,
632 : Handle<JSObject> object,
633 : T raw_dictionary) {
634 : Handle<T> dictionary(raw_dictionary, isolate);
635 43149 : if (dictionary->NumberOfElements() == 0) {
636 : return isolate->factory()->empty_fixed_array();
637 : }
638 42875 : int length = dictionary->NumberOfEnumerableProperties();
639 42875 : Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
640 42875 : T::CopyEnumKeysTo(isolate, dictionary, storage, mode, accumulator);
641 42875 : return storage;
642 : }
643 : } // namespace
644 :
645 1223887 : Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
646 243 : Handle<JSObject> object) {
647 1223887 : if (filter_ == ENUMERABLE_STRINGS) {
648 : Handle<FixedArray> enum_keys;
649 72106 : if (object->HasFastProperties()) {
650 35504 : enum_keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate_, object);
651 : // If the number of properties equals the length of enumerable properties
652 : // we do not have to filter out non-enumerable ones
653 35504 : Map map = object->map();
654 : int nof_descriptors = map->NumberOfOwnDescriptors();
655 35504 : if (enum_keys->length() != nof_descriptors) {
656 : Handle<DescriptorArray> descs =
657 8889 : Handle<DescriptorArray>(map->instance_descriptors(), isolate_);
658 108687 : for (int i = 0; i < nof_descriptors; i++) {
659 99798 : PropertyDetails details = descs->GetDetails(i);
660 103267 : if (!details.IsDontEnum()) continue;
661 96329 : Object key = descs->GetKey(i);
662 96329 : this->AddShadowingKey(key);
663 : }
664 : }
665 73204 : } else if (object->IsJSGlobalObject()) {
666 : enum_keys = GetOwnEnumPropertyDictionaryKeys(
667 : isolate_, mode_, this, object,
668 36269 : JSGlobalObject::cast(*object)->global_dictionary());
669 : } else {
670 : enum_keys = GetOwnEnumPropertyDictionaryKeys(
671 333 : isolate_, mode_, this, object, object->property_dictionary());
672 : }
673 144212 : if (object->IsJSModuleNamespace()) {
674 : // Simulate [[GetOwnProperty]] for establishing enumerability, which
675 : // throws for uninitialized exports.
676 315 : for (int i = 0, n = enum_keys->length(); i < n; ++i) {
677 243 : Handle<String> key(String::cast(enum_keys->get(i)), isolate_);
678 243 : if (Handle<JSModuleNamespace>::cast(object)
679 729 : ->GetExport(isolate(), key)
680 486 : .is_null()) {
681 54 : return Nothing<bool>();
682 : }
683 : }
684 : }
685 72052 : AddKeys(enum_keys, DO_NOT_CONVERT);
686 : } else {
687 1151781 : if (object->HasFastProperties()) {
688 : int limit = object->map()->NumberOfOwnDescriptors();
689 : Handle<DescriptorArray> descs(object->map()->instance_descriptors(),
690 3385044 : isolate_);
691 : // First collect the strings,
692 : int first_symbol =
693 1128348 : CollectOwnPropertyNamesInternal<true>(object, this, descs, 0, limit);
694 : // then the symbols.
695 1128348 : if (first_symbol != -1) {
696 : CollectOwnPropertyNamesInternal<false>(object, this, descs,
697 19036 : first_symbol, limit);
698 : }
699 46866 : } else if (object->IsJSGlobalObject()) {
700 : GlobalDictionary::CollectKeysTo(
701 : handle(JSGlobalObject::cast(*object)->global_dictionary(), isolate_),
702 68013 : this);
703 : } else {
704 : NameDictionary::CollectKeysTo(
705 2286 : handle(object->property_dictionary(), isolate_), this);
706 : }
707 : }
708 : // Add the property keys from the interceptor.
709 1223833 : return CollectInterceptorKeys(receiver, object, this, kNamed);
710 : }
711 :
712 20 : Maybe<bool> KeyAccumulator::CollectAccessCheckInterceptorKeys(
713 : Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
714 : Handle<JSObject> object) {
715 20 : if (!skip_indices_) {
716 60 : MAYBE_RETURN((CollectInterceptorKeysInternal(
717 : receiver, object,
718 : handle(InterceptorInfo::cast(
719 : access_check_info->indexed_interceptor()),
720 : isolate_),
721 : this, kIndexed)),
722 : Nothing<bool>());
723 : }
724 60 : MAYBE_RETURN(
725 : (CollectInterceptorKeysInternal(
726 : receiver, object,
727 : handle(InterceptorInfo::cast(access_check_info->named_interceptor()),
728 : isolate_),
729 : this, kNamed)),
730 : Nothing<bool>());
731 : return Just(true);
732 : }
733 :
734 : // Returns |true| on success, |false| if prototype walking should be stopped,
735 : // |nothing| if an exception was thrown.
736 1223888 : Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
737 : Handle<JSObject> object) {
738 : // Check access rights if required.
739 2447870 : if (object->IsAccessCheckNeeded() &&
740 188 : !isolate_->MayAccess(handle(isolate_->context(), isolate_), object)) {
741 : // The cross-origin spec says that [[Enumerate]] shall return an empty
742 : // iterator when it doesn't have access...
743 69 : if (mode_ == KeyCollectionMode::kIncludePrototypes) {
744 : return Just(false);
745 : }
746 : // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
747 : DCHECK_EQ(KeyCollectionMode::kOwnOnly, mode_);
748 : Handle<AccessCheckInfo> access_check_info;
749 : {
750 : DisallowHeapAllocation no_gc;
751 52 : AccessCheckInfo maybe_info = AccessCheckInfo::Get(isolate_, object);
752 52 : if (!maybe_info.is_null()) {
753 42 : access_check_info = handle(maybe_info, isolate_);
754 : }
755 : }
756 : // We always have both kinds of interceptors or none.
757 146 : if (!access_check_info.is_null() &&
758 94 : access_check_info->named_interceptor() != Object()) {
759 20 : MAYBE_RETURN(CollectAccessCheckInterceptorKeys(access_check_info,
760 : receiver, object),
761 : Nothing<bool>());
762 : return Just(false);
763 : }
764 32 : filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ);
765 : }
766 1223851 : MAYBE_RETURN(CollectOwnElementIndices(receiver, object), Nothing<bool>());
767 1223839 : MAYBE_RETURN(CollectOwnPropertyNames(receiver, object), Nothing<bool>());
768 : return Just(true);
769 : }
770 :
771 : // static
772 44562 : Handle<FixedArray> KeyAccumulator::GetOwnEnumPropertyKeys(
773 : Isolate* isolate, Handle<JSObject> object) {
774 44562 : if (object->HasFastProperties()) {
775 38015 : return GetFastEnumPropertyKeys(isolate, object);
776 13094 : } else if (object->IsJSGlobalObject()) {
777 : return GetOwnEnumPropertyDictionaryKeys(
778 : isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
779 18 : JSGlobalObject::cast(*object)->global_dictionary());
780 : } else {
781 : return GetOwnEnumPropertyDictionaryKeys(
782 : isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
783 6529 : object->property_dictionary());
784 : }
785 : }
786 :
787 : namespace {
788 :
789 : class NameComparator {
790 : public:
791 : explicit NameComparator(Isolate* isolate) : isolate_(isolate) {}
792 :
793 : bool operator()(uint32_t hash1, uint32_t hash2, const Handle<Name>& key1,
794 : const Handle<Name>& key2) const {
795 606 : return Name::Equals(isolate_, key1, key2);
796 : }
797 :
798 : private:
799 : Isolate* isolate_;
800 : };
801 :
802 : } // namespace
803 :
804 : // ES6 9.5.12
805 : // Returns |true| on success, |nothing| in case of exception.
806 942029 : Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver,
807 : Handle<JSProxy> proxy) {
808 1884265 : STACK_CHECK(isolate_, Nothing<bool>());
809 : // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
810 2826060 : Handle<Object> handler(proxy->handler(), isolate_);
811 : // 2. If handler is null, throw a TypeError exception.
812 : // 3. Assert: Type(handler) is Object.
813 1884040 : if (proxy->IsRevoked()) {
814 : isolate_->Throw(*isolate_->factory()->NewTypeError(
815 54 : MessageTemplate::kProxyRevoked, isolate_->factory()->ownKeys_string()));
816 : return Nothing<bool>();
817 : }
818 : // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
819 2826006 : Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate_);
820 : // 5. Let trap be ? GetMethod(handler, "ownKeys").
821 : Handle<Object> trap;
822 2826006 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
823 : isolate_, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
824 : isolate_->factory()->ownKeys_string()),
825 : Nothing<bool>());
826 : // 6. If trap is undefined, then
827 2825898 : if (trap->IsUndefined(isolate_)) {
828 : // 6a. Return target.[[OwnPropertyKeys]]().
829 934709 : return CollectOwnJSProxyTargetKeys(proxy, target);
830 : }
831 : // 7. Let trapResultArray be Call(trap, handler, «target»).
832 : Handle<Object> trap_result_array;
833 : Handle<Object> args[] = {target};
834 14514 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
835 : isolate_, trap_result_array,
836 : Execution::Call(isolate_, trap, handler, arraysize(args), args),
837 : Nothing<bool>());
838 : // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray,
839 : // «String, Symbol»).
840 : Handle<FixedArray> trap_result;
841 2322 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
842 : isolate_, trap_result,
843 : Object::CreateListFromArrayLike(isolate_, trap_result_array,
844 : ElementTypes::kStringAndSymbol),
845 : Nothing<bool>());
846 : // 9. Let extensibleTarget be ? IsExtensible(target).
847 1044 : Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target);
848 1044 : MAYBE_RETURN(maybe_extensible, Nothing<bool>());
849 : bool extensible_target = maybe_extensible.FromJust();
850 : // 10. Let targetKeys be ? target.[[OwnPropertyKeys]]().
851 : Handle<FixedArray> target_keys;
852 2088 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, target_keys,
853 : JSReceiver::OwnPropertyKeys(target),
854 : Nothing<bool>());
855 : // 11. (Assert)
856 : // 12. Let targetConfigurableKeys be an empty List.
857 : // To save memory, we're re-using target_keys and will modify it in-place.
858 : Handle<FixedArray> target_configurable_keys = target_keys;
859 : // 13. Let targetNonconfigurableKeys be an empty List.
860 : Handle<FixedArray> target_nonconfigurable_keys =
861 1044 : isolate_->factory()->NewFixedArray(target_keys->length());
862 : int nonconfigurable_keys_length = 0;
863 : // 14. Repeat, for each element key of targetKeys:
864 3852 : for (int i = 0; i < target_keys->length(); ++i) {
865 : // 14a. Let desc be ? target.[[GetOwnProperty]](key).
866 : PropertyDescriptor desc;
867 : Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor(
868 1764 : isolate_, target, handle(target_keys->get(i), isolate_), &desc);
869 882 : MAYBE_RETURN(found, Nothing<bool>());
870 : // 14b. If desc is not undefined and desc.[[Configurable]] is false, then
871 1764 : if (found.FromJust() && !desc.configurable()) {
872 : // 14b i. Append key as an element of targetNonconfigurableKeys.
873 : target_nonconfigurable_keys->set(nonconfigurable_keys_length,
874 306 : target_keys->get(i));
875 306 : nonconfigurable_keys_length++;
876 : // The key was moved, null it out in the original list.
877 306 : target_keys->set(i, Smi::kZero);
878 : } else {
879 : // 14c. Else,
880 : // 14c i. Append key as an element of targetConfigurableKeys.
881 : // (No-op, just keep it in |target_keys|.)
882 : }
883 : }
884 : // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty,
885 : // then:
886 1044 : if (extensible_target && nonconfigurable_keys_length == 0) {
887 : // 15a. Return trapResult.
888 837 : return AddKeysFromJSProxy(proxy, trap_result);
889 : }
890 : // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult.
891 414 : Zone set_zone(isolate_->allocator(), ZONE_NAME);
892 : ZoneAllocationPolicy alloc(&set_zone);
893 : const int kPresent = 1;
894 : const int kGone = 0;
895 : base::TemplateHashMapImpl<Handle<Name>, int, NameComparator,
896 : ZoneAllocationPolicy>
897 : unchecked_result_keys(ZoneHashMap::kDefaultHashMapCapacity,
898 207 : NameComparator(isolate_), alloc);
899 : int unchecked_result_keys_size = 0;
900 2034 : for (int i = 0; i < trap_result->length(); ++i) {
901 810 : Handle<Name> key(Name::cast(trap_result->get(i)), isolate_);
902 1620 : auto entry = unchecked_result_keys.LookupOrInsert(key, key->Hash(), alloc);
903 810 : if (entry->value != kPresent) {
904 639 : entry->value = kPresent;
905 639 : unchecked_result_keys_size++;
906 : }
907 : }
908 : // 17. Repeat, for each key that is an element of targetNonconfigurableKeys:
909 297 : for (int i = 0; i < nonconfigurable_keys_length; ++i) {
910 : Object raw_key = target_nonconfigurable_keys->get(i);
911 306 : Handle<Name> key(Name::cast(raw_key), isolate_);
912 : // 17a. If key is not an element of uncheckedResultKeys, throw a
913 : // TypeError exception.
914 612 : auto found = unchecked_result_keys.Lookup(key, key->Hash());
915 306 : if (found == nullptr || found->value == kGone) {
916 : isolate_->Throw(*isolate_->factory()->NewTypeError(
917 18 : MessageTemplate::kProxyOwnKeysMissing, key));
918 9 : return Nothing<bool>();
919 : }
920 : // 17b. Remove key from uncheckedResultKeys.
921 297 : found->value = kGone;
922 297 : unchecked_result_keys_size--;
923 : }
924 : // 18. If extensibleTarget is true, return trapResult.
925 198 : if (extensible_target) {
926 99 : return AddKeysFromJSProxy(proxy, trap_result);
927 : }
928 : // 19. Repeat, for each key that is an element of targetConfigurableKeys:
929 675 : for (int i = 0; i < target_configurable_keys->length(); ++i) {
930 297 : Object raw_key = target_configurable_keys->get(i);
931 477 : if (raw_key->IsSmi()) continue; // Zapped entry, was nonconfigurable.
932 117 : Handle<Name> key(Name::cast(raw_key), isolate_);
933 : // 19a. If key is not an element of uncheckedResultKeys, throw a
934 : // TypeError exception.
935 234 : auto found = unchecked_result_keys.Lookup(key, key->Hash());
936 117 : if (found == nullptr || found->value == kGone) {
937 : isolate_->Throw(*isolate_->factory()->NewTypeError(
938 18 : MessageTemplate::kProxyOwnKeysMissing, key));
939 9 : return Nothing<bool>();
940 : }
941 : // 19b. Remove key from uncheckedResultKeys.
942 108 : found->value = kGone;
943 108 : unchecked_result_keys_size--;
944 : }
945 : // 20. If uncheckedResultKeys is not empty, throw a TypeError exception.
946 90 : if (unchecked_result_keys_size != 0) {
947 : DCHECK_GT(unchecked_result_keys_size, 0);
948 : isolate_->Throw(*isolate_->factory()->NewTypeError(
949 18 : MessageTemplate::kProxyOwnKeysNonExtensible));
950 : return Nothing<bool>();
951 : }
952 : // 21. Return trapResult.
953 81 : return AddKeysFromJSProxy(proxy, trap_result);
954 : }
955 :
956 934709 : Maybe<bool> KeyAccumulator::CollectOwnJSProxyTargetKeys(
957 : Handle<JSProxy> proxy, Handle<JSReceiver> target) {
958 : // TODO(cbruni): avoid creating another KeyAccumulator
959 : Handle<FixedArray> keys;
960 1869418 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
961 : isolate_, keys,
962 : KeyAccumulator::GetKeys(
963 : target, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES,
964 : GetKeysConversion::kConvertToString, is_for_in_, skip_indices_),
965 : Nothing<bool>());
966 922910 : Maybe<bool> result = AddKeysFromJSProxy(proxy, keys);
967 922910 : return result;
968 : }
969 :
970 : } // namespace internal
971 183867 : } // namespace v8
|