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