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 1417246 : 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 1417246 : return accumulator.GetKeys(keys_conversion);
46 : }
47 :
48 1262730 : Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
49 1262730 : if (keys_.is_null()) {
50 1010940 : return isolate_->factory()->empty_fixed_array();
51 : }
52 496286 : if (mode_ == KeyCollectionMode::kOwnOnly &&
53 244496 : keys_->map() == ReadOnlyRoots(isolate_).fixed_array_map()) {
54 : return Handle<FixedArray>::cast(keys_);
55 : }
56 : USE(ContainsOnlyValidKeys);
57 : Handle<FixedArray> result =
58 250126 : OrderedHashSet::ConvertToKeysArray(isolate(), keys(), convert);
59 : DCHECK(ContainsOnlyValidKeys(result));
60 250126 : return result;
61 : }
62 :
63 0 : Handle<OrderedHashSet> KeyAccumulator::keys() {
64 0 : return Handle<OrderedHashSet>::cast(keys_);
65 : }
66 :
67 3948985 : void KeyAccumulator::AddKey(Object key, AddKeyConversion convert) {
68 7897970 : AddKey(handle(key, isolate_), convert);
69 3948985 : }
70 :
71 10050549 : void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
72 10050549 : if (filter_ == PRIVATE_NAMES_ONLY) {
73 9473 : if (!key->IsSymbol()) return;
74 95 : if (!Symbol::cast(*key)->is_private_name()) return;
75 10050454 : } else if (key->IsSymbol()) {
76 45717 : if (filter_ & SKIP_SYMBOLS) return;
77 :
78 45660 : if (Symbol::cast(*key)->is_private()) return;
79 10004737 : } else if (filter_ & SKIP_STRINGS) {
80 : return;
81 : }
82 :
83 10050486 : if (IsShadowed(key)) return;
84 10041171 : if (keys_.is_null()) {
85 250159 : keys_ = OrderedHashSet::Allocate(isolate_, 16);
86 : }
87 : uint32_t index;
88 20085244 : if (convert == CONVERT_TO_ARRAY_INDEX && key->IsString() &&
89 10042328 : Handle<String>::cast(key)->AsArrayIndex(&index)) {
90 374 : key = isolate_->factory()->NewNumberFromUint(index);
91 : }
92 10041171 : Handle<OrderedHashSet> new_set = OrderedHashSet::Add(isolate(), keys(), key);
93 10041171 : 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 195600 : keys_->set(OrderedHashSet::NextTableIndex(), Smi::kZero);
98 195600 : 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 1279555 : 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 2551232 : if (mode_ == KeyCollectionMode::kOwnOnly && object->IsJSProxy()) {
175 48632 : MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)),
176 : Nothing<bool>());
177 : return Just(true);
178 : }
179 :
180 1255239 : PrototypeIterator::WhereToEnd end = mode_ == KeyCollectionMode::kOwnOnly
181 : ? PrototypeIterator::END_AT_NON_HIDDEN
182 1255239 : : PrototypeIterator::END_AT_NULL;
183 4752565 : 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 2243694 : if (HasShadowingKeys()) skip_shadow_check_ = false;
188 : Handle<JSReceiver> current =
189 : PrototypeIterator::GetCurrent<JSReceiver>(iter);
190 : Maybe<bool> result = Just(false); // Dummy initialization.
191 2243694 : if (current->IsJSProxy()) {
192 922196 : result = CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(current));
193 : } else {
194 : DCHECK(current->IsJSObject());
195 1321498 : result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current));
196 : }
197 2243694 : MAYBE_RETURN(result, Nothing<bool>());
198 2243538 : 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 2243501 : if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
202 : return Nothing<bool>();
203 : }
204 2246531 : 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 10050486 : bool KeyAccumulator::IsShadowed(Handle<Object> key) {
215 10050486 : 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 4666 : void TrySettingEmptyEnumCache(JSReceiver object) {
234 4666 : Map map = object->map();
235 : DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength());
236 5678 : if (!map->OnlyHasSimpleProperties()) return;
237 3996 : if (map->IsJSProxyMap()) return;
238 3996 : if (map->NumberOfEnumerableProperties() > 0) return;
239 : DCHECK(object->IsJSObject());
240 3654 : map->SetEnumLength(0);
241 : }
242 :
243 101540 : bool CheckAndInitalizeEmptyEnumCache(JSReceiver object) {
244 101540 : if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) {
245 4666 : TrySettingEmptyEnumCache(object);
246 : }
247 101540 : if (object->map()->EnumLength() != 0) return false;
248 : DCHECK(object->IsJSObject());
249 100060 : return !JSObject::cast(object)->HasEnumerableElements();
250 : }
251 : } // namespace
252 :
253 1471474 : void FastKeyAccumulator::Prepare() {
254 : DisallowHeapAllocation no_gc;
255 : // Directly go for the fast path for OWN_ONLY keys.
256 1471474 : if (mode_ == KeyCollectionMode::kOwnOnly) return;
257 : // Fully walk the prototype chain and find the last prototype with keys.
258 54228 : is_receiver_simple_enum_ = false;
259 54228 : has_empty_prototype_ = true;
260 : JSReceiver last_prototype;
261 311536 : for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
262 101540 : iter.Advance()) {
263 : JSReceiver current = iter.GetCurrent<JSReceiver>();
264 101540 : bool has_no_properties = CheckAndInitalizeEmptyEnumCache(current);
265 101540 : if (has_no_properties) continue;
266 : last_prototype = current;
267 1525 : has_empty_prototype_ = false;
268 : }
269 54228 : if (has_empty_prototype_) {
270 : is_receiver_simple_enum_ =
271 96836 : receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel &&
272 96836 : !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 174901 : Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
281 : Handle<FixedArray> array, int length) {
282 : DCHECK_LE(length, array->length());
283 174901 : if (array->length() == length) return array;
284 271 : 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 223094 : 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 223094 : if (enum_length != kInvalidEnumCacheSentinel) {
299 : DCHECK(map->OnlyHasSimpleProperties());
300 : DCHECK_LE(enum_length, keys->length());
301 : DCHECK_EQ(enum_length, map->NumberOfEnumerableProperties());
302 148591 : isolate->counters()->enum_cache_hits()->Increment();
303 148591 : return ReduceFixedArrayTo(isolate, keys, enum_length);
304 : }
305 :
306 : // Determine the actual number of enumerable properties of the {map}.
307 74503 : 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 74503 : if (enum_length <= keys->length()) {
312 32206 : if (map->OnlyHasSimpleProperties()) map->SetEnumLength(enum_length);
313 26310 : isolate->counters()->enum_cache_hits()->Increment();
314 26310 : return ReduceFixedArrayTo(isolate, keys, enum_length);
315 : }
316 :
317 : Handle<DescriptorArray> descriptors =
318 : Handle<DescriptorArray>(map->instance_descriptors(), isolate);
319 48193 : 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 48193 : keys = isolate->factory()->NewFixedArray(enum_length);
326 677093 : for (int i = 0; i < nod; i++) {
327 : DisallowHeapAllocation no_gc;
328 314450 : PropertyDetails details = descriptors->GetDetails(i);
329 314450 : if (details.IsDontEnum()) continue;
330 303735 : Object key = descriptors->GetKey(i);
331 303735 : if (key->IsSymbol()) continue;
332 303606 : keys->set(index, key);
333 303606 : if (details.location() != kField) fields_only = false;
334 303606 : 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 48193 : if (fields_only) {
341 48055 : indices = isolate->factory()->NewFixedArray(enum_length);
342 : index = 0;
343 675773 : for (int i = 0; i < nod; i++) {
344 : DisallowHeapAllocation no_gc;
345 313859 : PropertyDetails details = descriptors->GetDetails(i);
346 324545 : if (details.IsDontEnum()) continue;
347 303302 : Object key = descriptors->GetKey(i);
348 303302 : if (key->IsSymbol()) continue;
349 : DCHECK_EQ(kData, details.kind());
350 : DCHECK_EQ(kField, details.location());
351 303173 : FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
352 : indices->set(index, Smi::FromInt(field_index.GetLoadByFieldIndex()));
353 303173 : index++;
354 : }
355 : DCHECK_EQ(index, indices->length());
356 : }
357 :
358 : DescriptorArray::InitializeOrChangeEnumCache(descriptors, isolate, keys,
359 48193 : indices);
360 96288 : if (map->OnlyHasSimpleProperties()) map->SetEnumLength(enum_length);
361 :
362 48193 : return keys;
363 : }
364 :
365 : template <bool fast_properties>
366 142034 : MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
367 : Handle<JSObject> object,
368 : GetKeysConversion convert,
369 : bool skip_indices) {
370 : Handle<FixedArray> keys;
371 142034 : ElementsAccessor* accessor = object->GetElementsAccessor();
372 : if (fast_properties) {
373 135649 : 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 142034 : if (skip_indices) {
381 : result = keys;
382 : } else {
383 141930 : result =
384 : accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
385 : }
386 :
387 142034 : 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 142034 : return result;
392 : }
393 :
394 : } // namespace
395 :
396 1468625 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(
397 : GetKeysConversion keys_conversion) {
398 1468625 : if (filter_ == ENUMERABLE_STRINGS) {
399 : Handle<FixedArray> keys;
400 472544 : if (GetKeysFast(keys_conversion).ToHandle(&keys)) {
401 195688 : return keys;
402 : }
403 81168 : if (isolate_->has_pending_exception()) return MaybeHandle<FixedArray>();
404 : }
405 :
406 1272937 : return GetKeysSlow(keys_conversion);
407 : }
408 :
409 236272 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
410 : GetKeysConversion keys_conversion) {
411 236272 : bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly;
412 : Map map = receiver_->map();
413 471104 : 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 195688 : 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 189303 : if (enum_length == kInvalidEnumCacheSentinel) {
428 : Handle<FixedArray> keys;
429 : // Try initializing the enum cache and return own properties.
430 111588 : if (GetOwnKeysWithUninitializedEnumCache().ToHandle(&keys)) {
431 53654 : 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 53654 : object->map()->EnumLength() != kInvalidEnumCacheSentinel;
437 53654 : 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 135649 : skip_indices_);
444 : }
445 :
446 : MaybeHandle<FixedArray>
447 55794 : FastKeyAccumulator::GetOwnKeysWithUninitializedEnumCache() {
448 : Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
449 : // Uninitalized enum cache
450 55794 : Map map = object->map();
451 55794 : if (object->elements()->length() != 0) {
452 : // Assume that there are elements.
453 2140 : return MaybeHandle<FixedArray>();
454 : }
455 : int number_of_own_descriptors = map->NumberOfOwnDescriptors();
456 53654 : if (number_of_own_descriptors == 0) {
457 4252 : map->SetEnumLength(0);
458 8504 : return isolate_->factory()->empty_fixed_array();
459 : }
460 : // We have no elements but possibly enumerable property keys, hence we can
461 : // directly initialize the enum cache.
462 49402 : Handle<FixedArray> keys = GetFastEnumPropertyKeys(isolate_, object);
463 49402 : if (is_for_in_) return keys;
464 : // Do not leak the enum cache as it might end up as an elements backing store.
465 44749 : return isolate_->factory()->CopyFixedArray(keys);
466 : }
467 :
468 1272937 : MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
469 : GetKeysConversion keys_conversion) {
470 1272937 : KeyAccumulator accumulator(isolate_, mode_, filter_);
471 1272937 : accumulator.set_is_for_in(is_for_in_);
472 1272937 : accumulator.set_skip_indices(skip_indices_);
473 : accumulator.set_last_non_empty_prototype(last_non_empty_prototype_);
474 :
475 2545874 : MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_),
476 : MaybeHandle<FixedArray>());
477 1250145 : return accumulator.GetKeys(keys_conversion);
478 : }
479 :
480 : namespace {
481 :
482 : enum IndexedOrNamed { kIndexed, kNamed };
483 :
484 60 : void FilterForEnumerableProperties(Handle<JSReceiver> receiver,
485 : Handle<JSObject> object,
486 : Handle<InterceptorInfo> interceptor,
487 : KeyAccumulator* accumulator,
488 : Handle<JSObject> result,
489 : IndexedOrNamed type) {
490 : DCHECK(result->IsJSArray() || result->HasSloppyArgumentsElements());
491 60 : ElementsAccessor* accessor = result->GetElementsAccessor();
492 :
493 120 : uint32_t length = accessor->GetCapacity(*result, result->elements());
494 504 : for (uint32_t i = 0; i < length; i++) {
495 510 : if (!accessor->HasEntry(*result, i)) continue;
496 :
497 : // args are invalid after args.Call(), create a new one in every iteration.
498 : PropertyCallbackArguments args(accumulator->isolate(), interceptor->data(),
499 312 : *receiver, *object, Just(kDontThrow));
500 :
501 156 : Handle<Object> element = accessor->Get(result, i);
502 : Handle<Object> attributes;
503 156 : if (type == kIndexed) {
504 : uint32_t number;
505 84 : CHECK(element->ToUint32(&number));
506 84 : attributes = args.CallIndexedQuery(interceptor, number);
507 : } else {
508 72 : CHECK(element->IsName());
509 : attributes =
510 72 : args.CallNamedQuery(interceptor, Handle<Name>::cast(element));
511 : }
512 :
513 156 : if (!attributes.is_null()) {
514 : int32_t value;
515 144 : CHECK(attributes->ToInt32(&value));
516 144 : if ((value & DONT_ENUM) == 0) {
517 48 : accumulator->AddKey(element, DO_NOT_CONVERT);
518 : }
519 : }
520 : }
521 60 : }
522 :
523 : // Returns |true| on success, |nothing| on exception.
524 475 : Maybe<bool> CollectInterceptorKeysInternal(Handle<JSReceiver> receiver,
525 : Handle<JSObject> object,
526 : Handle<InterceptorInfo> interceptor,
527 : KeyAccumulator* accumulator,
528 : IndexedOrNamed type) {
529 : Isolate* isolate = accumulator->isolate();
530 : PropertyCallbackArguments enum_args(isolate, interceptor->data(), *receiver,
531 950 : *object, Just(kDontThrow));
532 :
533 : Handle<JSObject> result;
534 475 : if (!interceptor->enumerator()->IsUndefined(isolate)) {
535 475 : if (type == kIndexed) {
536 235 : result = enum_args.CallIndexedEnumerator(interceptor);
537 : } else {
538 : DCHECK_EQ(type, kNamed);
539 240 : result = enum_args.CallNamedEnumerator(interceptor);
540 : }
541 : }
542 475 : RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
543 445 : if (result.is_null()) return Just(true);
544 :
545 488 : if ((accumulator->filter() & ONLY_ENUMERABLE) &&
546 : !interceptor->query()->IsUndefined(isolate)) {
547 : FilterForEnumerableProperties(receiver, object, interceptor, accumulator,
548 60 : result, type);
549 : } else {
550 309 : accumulator->AddKeys(
551 309 : result, type == kIndexed ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
552 : }
553 : return Just(true);
554 : }
555 :
556 2453449 : Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver,
557 : Handle<JSObject> object,
558 : KeyAccumulator* accumulator,
559 : IndexedOrNamed type) {
560 : Isolate* isolate = accumulator->isolate();
561 2453449 : if (type == kIndexed) {
562 1229144 : if (!object->HasIndexedInterceptor()) return Just(true);
563 : } else {
564 1224305 : if (!object->HasNamedInterceptor()) return Just(true);
565 : }
566 : Handle<InterceptorInfo> interceptor(type == kIndexed
567 865 : ? object->GetIndexedInterceptor()
568 655 : : object->GetNamedInterceptor(),
569 1090 : isolate);
570 435 : if ((accumulator->filter() & ONLY_ALL_CAN_READ) &&
571 : !interceptor->all_can_read()) {
572 : return Just(true);
573 : }
574 : return CollectInterceptorKeysInternal(receiver, object, interceptor,
575 435 : accumulator, type);
576 : }
577 :
578 : } // namespace
579 :
580 1229732 : Maybe<bool> KeyAccumulator::CollectOwnElementIndices(
581 : Handle<JSReceiver> receiver, Handle<JSObject> object) {
582 1229732 : if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true);
583 :
584 1229144 : ElementsAccessor* accessor = object->GetElementsAccessor();
585 1229144 : accessor->CollectElementIndices(object, this);
586 :
587 1229144 : return CollectInterceptorKeys(receiver, object, this, kIndexed);
588 : }
589 :
590 : namespace {
591 :
592 : template <bool skip_symbols>
593 1227065 : int CollectOwnPropertyNamesInternal(Handle<JSObject> object,
594 : KeyAccumulator* keys,
595 : Handle<DescriptorArray> descs,
596 : int start_index, int limit) {
597 : int first_skipped = -1;
598 : PropertyFilter filter = keys->filter();
599 : KeyCollectionMode mode = keys->mode();
600 4036421 : for (int i = start_index; i < limit; i++) {
601 : bool is_shadowing_key = false;
602 1404678 : PropertyDetails details = descs->GetDetails(i);
603 :
604 1404678 : if ((details.attributes() & filter) != 0) {
605 12 : if (mode == KeyCollectionMode::kIncludePrototypes) {
606 : is_shadowing_key = true;
607 : } else {
608 375848 : continue;
609 : }
610 : }
611 :
612 1404666 : if (filter & ONLY_ALL_CAN_READ) {
613 18 : if (details.kind() != kAccessor) continue;
614 6 : Object accessors = descs->GetStrongValue(i);
615 6 : if (!accessors->IsAccessorInfo()) continue;
616 6 : if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
617 : }
618 :
619 1404660 : Name key = descs->GetKey(i);
620 1404660 : if (skip_symbols == key->IsSymbol()) {
621 363208 : if (first_skipped == -1) first_skipped = i;
622 : continue;
623 : }
624 1041452 : if (key->FilterKey(keys->filter())) continue;
625 :
626 1028830 : if (is_shadowing_key) {
627 0 : keys->AddShadowingKey(key);
628 : } else {
629 1028830 : keys->AddKey(key, DO_NOT_CONVERT);
630 : }
631 : }
632 1227065 : return first_skipped;
633 : }
634 :
635 : template <class T>
636 43191 : Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
637 : KeyCollectionMode mode,
638 : KeyAccumulator* accumulator,
639 : Handle<JSObject> object,
640 : T raw_dictionary) {
641 : Handle<T> dictionary(raw_dictionary, isolate);
642 43191 : if (dictionary->NumberOfElements() == 0) {
643 : return isolate->factory()->empty_fixed_array();
644 : }
645 42917 : int length = dictionary->NumberOfEnumerableProperties();
646 42917 : Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
647 42917 : T::CopyEnumKeysTo(isolate, dictionary, storage, mode, accumulator);
648 42917 : return storage;
649 : }
650 : } // namespace
651 :
652 1224359 : Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
653 : Handle<JSObject> object) {
654 1224359 : if (filter_ == ENUMERABLE_STRINGS) {
655 : Handle<FixedArray> enum_keys;
656 72110 : if (object->HasFastProperties()) {
657 35512 : enum_keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate_, object);
658 : // If the number of properties equals the length of enumerable properties
659 : // we do not have to filter out non-enumerable ones
660 : Map map = object->map();
661 : int nof_descriptors = map->NumberOfOwnDescriptors();
662 35512 : if (enum_keys->length() != nof_descriptors) {
663 : Handle<DescriptorArray> descs =
664 8972 : Handle<DescriptorArray>(map->instance_descriptors(), isolate_);
665 230270 : for (int i = 0; i < nof_descriptors; i++) {
666 110649 : PropertyDetails details = descs->GetDetails(i);
667 114208 : if (!details.IsDontEnum()) continue;
668 107090 : Object key = descs->GetKey(i);
669 107090 : this->AddShadowingKey(key);
670 : }
671 : }
672 36598 : } else if (object->IsJSGlobalObject()) {
673 : enum_keys = GetOwnEnumPropertyDictionaryKeys(
674 : isolate_, mode_, this, object,
675 36269 : JSGlobalObject::cast(*object)->global_dictionary());
676 : } else {
677 : enum_keys = GetOwnEnumPropertyDictionaryKeys(
678 329 : isolate_, mode_, this, object, object->property_dictionary());
679 : }
680 72110 : if (object->IsJSModuleNamespace()) {
681 : // Simulate [[GetOwnProperty]] for establishing enumerability, which
682 : // throws for uninitialized exports.
683 504 : for (int i = 0, n = enum_keys->length(); i < n; ++i) {
684 243 : Handle<String> key(String::cast(enum_keys->get(i)), isolate_);
685 243 : if (Handle<JSModuleNamespace>::cast(object)
686 486 : ->GetExport(isolate(), key)
687 : .is_null()) {
688 54 : return Nothing<bool>();
689 : }
690 : }
691 : }
692 72056 : AddKeys(enum_keys, DO_NOT_CONVERT);
693 : } else {
694 1152249 : if (object->HasFastProperties()) {
695 : int limit = object->map()->NumberOfOwnDescriptors();
696 : Handle<DescriptorArray> descs(object->map()->instance_descriptors(),
697 1128808 : isolate_);
698 : // First collect the strings,
699 : int first_symbol =
700 1128808 : CollectOwnPropertyNamesInternal<true>(object, this, descs, 0, limit);
701 : // then the symbols.
702 1128808 : if (first_symbol != -1) {
703 : CollectOwnPropertyNamesInternal<false>(object, this, descs,
704 19048 : first_symbol, limit);
705 : }
706 23441 : } else if (object->IsJSGlobalObject()) {
707 45346 : GlobalDictionary::CollectKeysTo(
708 45346 : handle(JSGlobalObject::cast(*object)->global_dictionary(), isolate_),
709 22673 : this);
710 : } else {
711 1536 : NameDictionary::CollectKeysTo(
712 1536 : handle(object->property_dictionary(), isolate_), this);
713 : }
714 : }
715 : // Add the property keys from the interceptor.
716 1224305 : return CollectInterceptorKeys(receiver, object, this, kNamed);
717 : }
718 :
719 97141 : void KeyAccumulator::CollectPrivateNames(Handle<JSReceiver> receiver,
720 : Handle<JSObject> object) {
721 97141 : if (object->HasFastProperties()) {
722 : int limit = object->map()->NumberOfOwnDescriptors();
723 : Handle<DescriptorArray> descs(object->map()->instance_descriptors(),
724 79209 : isolate_);
725 79209 : CollectOwnPropertyNamesInternal<false>(object, this, descs, 0, limit);
726 17932 : } else if (object->IsJSGlobalObject()) {
727 35786 : GlobalDictionary::CollectKeysTo(
728 35786 : handle(JSGlobalObject::cast(*object)->global_dictionary(), isolate_),
729 17893 : this);
730 : } else {
731 78 : NameDictionary::CollectKeysTo(
732 78 : handle(object->property_dictionary(), isolate_), this);
733 : }
734 97141 : }
735 :
736 20 : Maybe<bool> KeyAccumulator::CollectAccessCheckInterceptorKeys(
737 : Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
738 : Handle<JSObject> object) {
739 20 : if (!skip_indices_) {
740 60 : MAYBE_RETURN((CollectInterceptorKeysInternal(
741 : receiver, object,
742 : handle(InterceptorInfo::cast(
743 : access_check_info->indexed_interceptor()),
744 : isolate_),
745 : this, kIndexed)),
746 : Nothing<bool>());
747 : }
748 60 : MAYBE_RETURN(
749 : (CollectInterceptorKeysInternal(
750 : receiver, object,
751 : handle(InterceptorInfo::cast(access_check_info->named_interceptor()),
752 : isolate_),
753 : this, kNamed)),
754 : Nothing<bool>());
755 : return Just(true);
756 : }
757 :
758 : // Returns |true| on success, |false| if prototype walking should be stopped,
759 : // |nothing| if an exception was thrown.
760 1321498 : Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
761 : Handle<JSObject> object) {
762 : // Check access rights if required.
763 1321592 : if (object->IsAccessCheckNeeded() &&
764 188 : !isolate_->MayAccess(handle(isolate_->context(), isolate_), object)) {
765 : // The cross-origin spec says that [[Enumerate]] shall return an empty
766 : // iterator when it doesn't have access...
767 69 : if (mode_ == KeyCollectionMode::kIncludePrototypes) {
768 : return Just(false);
769 : }
770 : // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
771 : DCHECK_EQ(KeyCollectionMode::kOwnOnly, mode_);
772 : Handle<AccessCheckInfo> access_check_info;
773 : {
774 : DisallowHeapAllocation no_gc;
775 52 : AccessCheckInfo maybe_info = AccessCheckInfo::Get(isolate_, object);
776 52 : if (!maybe_info.is_null()) {
777 42 : access_check_info = handle(maybe_info, isolate_);
778 : }
779 : }
780 : // We always have both kinds of interceptors or none.
781 94 : if (!access_check_info.is_null() &&
782 : access_check_info->named_interceptor() != Object()) {
783 40 : MAYBE_RETURN(CollectAccessCheckInterceptorKeys(access_check_info,
784 : receiver, object),
785 : Nothing<bool>());
786 : return Just(false);
787 : }
788 32 : filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ);
789 : }
790 1321461 : if (filter_ & PRIVATE_NAMES_ONLY) {
791 97141 : CollectPrivateNames(receiver, object);
792 : return Just(true);
793 : }
794 :
795 2448640 : MAYBE_RETURN(CollectOwnElementIndices(receiver, object), Nothing<bool>());
796 2448616 : MAYBE_RETURN(CollectOwnPropertyNames(receiver, object), Nothing<bool>());
797 : return Just(true);
798 : }
799 :
800 : // static
801 44636 : Handle<FixedArray> KeyAccumulator::GetOwnEnumPropertyKeys(
802 : Isolate* isolate, Handle<JSObject> object) {
803 44636 : if (object->HasFastProperties()) {
804 38043 : return GetFastEnumPropertyKeys(isolate, object);
805 6593 : } else if (object->IsJSGlobalObject()) {
806 : return GetOwnEnumPropertyDictionaryKeys(
807 : isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
808 18 : JSGlobalObject::cast(*object)->global_dictionary());
809 : } else {
810 : return GetOwnEnumPropertyDictionaryKeys(
811 : isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
812 6575 : object->property_dictionary());
813 : }
814 : }
815 :
816 : namespace {
817 :
818 : class NameComparator {
819 : public:
820 : explicit NameComparator(Isolate* isolate) : isolate_(isolate) {}
821 :
822 : bool operator()(uint32_t hash1, uint32_t hash2, const Handle<Name>& key1,
823 : const Handle<Name>& key2) const {
824 806 : return Name::Equals(isolate_, key1, key2);
825 : }
826 :
827 : private:
828 : Isolate* isolate_;
829 : };
830 :
831 : } // namespace
832 :
833 : // ES6 #sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys
834 : // Returns |true| on success, |nothing| in case of exception.
835 946512 : Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver,
836 : Handle<JSProxy> proxy) {
837 1893024 : STACK_CHECK(isolate_, Nothing<bool>());
838 946502 : if (filter_ == PRIVATE_NAMES_ONLY) {
839 50 : NameDictionary::CollectKeysTo(
840 50 : handle(proxy->property_dictionary(), isolate_), this);
841 : return Just(true);
842 : }
843 :
844 : // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
845 946477 : Handle<Object> handler(proxy->handler(), isolate_);
846 : // 2. If handler is null, throw a TypeError exception.
847 : // 3. Assert: Type(handler) is Object.
848 946477 : if (proxy->IsRevoked()) {
849 54 : isolate_->Throw(*isolate_->factory()->NewTypeError(
850 54 : MessageTemplate::kProxyRevoked, isolate_->factory()->ownKeys_string()));
851 : return Nothing<bool>();
852 : }
853 : // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
854 946459 : Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate_);
855 : // 5. Let trap be ? GetMethod(handler, "ownKeys").
856 : Handle<Object> trap;
857 2839377 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
858 : isolate_, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
859 : isolate_->factory()->ownKeys_string()),
860 : Nothing<bool>());
861 : // 6. If trap is undefined, then
862 1892846 : if (trap->IsUndefined(isolate_)) {
863 : // 6a. Return target.[[OwnPropertyKeys]]().
864 938233 : return CollectOwnJSProxyTargetKeys(proxy, target);
865 : }
866 : // 7. Let trapResultArray be Call(trap, handler, «target»).
867 : Handle<Object> trap_result_array;
868 : Handle<Object> args[] = {target};
869 16380 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
870 : isolate_, trap_result_array,
871 : Execution::Call(isolate_, trap, handler, arraysize(args), args),
872 : Nothing<bool>());
873 : // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray,
874 : // «String, Symbol»).
875 : Handle<FixedArray> trap_result;
876 2340 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
877 : isolate_, trap_result,
878 : Object::CreateListFromArrayLike(isolate_, trap_result_array,
879 : ElementTypes::kStringAndSymbol),
880 : Nothing<bool>());
881 : // 9. If trapResult contains any duplicate entries, throw a TypeError
882 : // exception. Combine with step 18
883 : // 18. Let uncheckedResultKeys be a new List which is a copy of trapResult.
884 2106 : Zone set_zone(isolate_->allocator(), ZONE_NAME);
885 : ZoneAllocationPolicy alloc(&set_zone);
886 : const int kPresent = 1;
887 : const int kGone = 0;
888 : base::TemplateHashMapImpl<Handle<Name>, int, NameComparator,
889 : ZoneAllocationPolicy>
890 : unchecked_result_keys(ZoneHashMap::kDefaultHashMapCapacity,
891 1053 : NameComparator(isolate_), alloc);
892 : int unchecked_result_keys_size = 0;
893 6525 : for (int i = 0; i < trap_result->length(); ++i) {
894 2817 : Handle<Name> key(Name::cast(trap_result->get(i)), isolate_);
895 5634 : auto entry = unchecked_result_keys.LookupOrInsert(key, key->Hash(), alloc);
896 2817 : if (entry->value != kPresent) {
897 2736 : entry->value = kPresent;
898 2736 : unchecked_result_keys_size++;
899 : } else {
900 : // found dupes, throw exception
901 243 : isolate_->Throw(*isolate_->factory()->NewTypeError(
902 162 : MessageTemplate::kProxyOwnKeysDuplicateEntries));
903 81 : return Nothing<bool>();
904 : }
905 : }
906 : // 10. Let extensibleTarget be ? IsExtensible(target).
907 972 : Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target);
908 972 : MAYBE_RETURN(maybe_extensible, Nothing<bool>());
909 : bool extensible_target = maybe_extensible.FromJust();
910 : // 11. Let targetKeys be ? target.[[OwnPropertyKeys]]().
911 : Handle<FixedArray> target_keys;
912 1944 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, target_keys,
913 : JSReceiver::OwnPropertyKeys(target),
914 : Nothing<bool>());
915 : // 12, 13. (Assert)
916 : // 14. Let targetConfigurableKeys be an empty List.
917 : // To save memory, we're re-using target_keys and will modify it in-place.
918 : Handle<FixedArray> target_configurable_keys = target_keys;
919 : // 15. Let targetNonconfigurableKeys be an empty List.
920 : Handle<FixedArray> target_nonconfigurable_keys =
921 972 : isolate_->factory()->NewFixedArray(target_keys->length());
922 : int nonconfigurable_keys_length = 0;
923 : // 16. Repeat, for each element key of targetKeys:
924 2340 : for (int i = 0; i < target_keys->length(); ++i) {
925 : // 16a. Let desc be ? target.[[GetOwnProperty]](key).
926 : PropertyDescriptor desc;
927 : Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor(
928 1368 : isolate_, target, handle(target_keys->get(i), isolate_), &desc);
929 684 : MAYBE_RETURN(found, Nothing<bool>());
930 : // 16b. If desc is not undefined and desc.[[Configurable]] is false, then
931 1368 : if (found.FromJust() && !desc.configurable()) {
932 : // 16b i. Append key as an element of targetNonconfigurableKeys.
933 486 : target_nonconfigurable_keys->set(nonconfigurable_keys_length,
934 243 : target_keys->get(i));
935 243 : nonconfigurable_keys_length++;
936 : // The key was moved, null it out in the original list.
937 243 : target_keys->set(i, Smi::kZero);
938 : } else {
939 : // 16c. Else,
940 : // 16c i. Append key as an element of targetConfigurableKeys.
941 : // (No-op, just keep it in |target_keys|.)
942 : }
943 : }
944 : // 17. If extensibleTarget is true and targetNonconfigurableKeys is empty,
945 : // then:
946 972 : if (extensible_target && nonconfigurable_keys_length == 0) {
947 : // 17a. Return trapResult.
948 828 : return AddKeysFromJSProxy(proxy, trap_result);
949 : }
950 : // 18. (Done in step 9)
951 : // 19. Repeat, for each key that is an element of targetNonconfigurableKeys:
952 612 : for (int i = 0; i < nonconfigurable_keys_length; ++i) {
953 : Object raw_key = target_nonconfigurable_keys->get(i);
954 243 : Handle<Name> key(Name::cast(raw_key), isolate_);
955 : // 19a. If key is not an element of uncheckedResultKeys, throw a
956 : // TypeError exception.
957 486 : auto found = unchecked_result_keys.Lookup(key, key->Hash());
958 243 : if (found == nullptr || found->value == kGone) {
959 27 : isolate_->Throw(*isolate_->factory()->NewTypeError(
960 18 : MessageTemplate::kProxyOwnKeysMissing, key));
961 9 : return Nothing<bool>();
962 : }
963 : // 19b. Remove key from uncheckedResultKeys.
964 234 : found->value = kGone;
965 234 : unchecked_result_keys_size--;
966 : }
967 : // 20. If extensibleTarget is true, return trapResult.
968 135 : if (extensible_target) {
969 54 : return AddKeysFromJSProxy(proxy, trap_result);
970 : }
971 : // 21. Repeat, for each key that is an element of targetConfigurableKeys:
972 621 : for (int i = 0; i < target_configurable_keys->length(); ++i) {
973 : Object raw_key = target_configurable_keys->get(i);
974 441 : if (raw_key->IsSmi()) continue; // Zapped entry, was nonconfigurable.
975 99 : Handle<Name> key(Name::cast(raw_key), isolate_);
976 : // 21a. If key is not an element of uncheckedResultKeys, throw a
977 : // TypeError exception.
978 198 : auto found = unchecked_result_keys.Lookup(key, key->Hash());
979 99 : if (found == nullptr || found->value == kGone) {
980 0 : isolate_->Throw(*isolate_->factory()->NewTypeError(
981 0 : MessageTemplate::kProxyOwnKeysMissing, key));
982 0 : return Nothing<bool>();
983 : }
984 : // 21b. Remove key from uncheckedResultKeys.
985 99 : found->value = kGone;
986 99 : unchecked_result_keys_size--;
987 : }
988 : // 22. If uncheckedResultKeys is not empty, throw a TypeError exception.
989 81 : if (unchecked_result_keys_size != 0) {
990 : DCHECK_GT(unchecked_result_keys_size, 0);
991 27 : isolate_->Throw(*isolate_->factory()->NewTypeError(
992 18 : MessageTemplate::kProxyOwnKeysNonExtensible));
993 : return Nothing<bool>();
994 : }
995 : // 23. Return trapResult.
996 72 : return AddKeysFromJSProxy(proxy, trap_result);
997 : }
998 :
999 938233 : Maybe<bool> KeyAccumulator::CollectOwnJSProxyTargetKeys(
1000 : Handle<JSProxy> proxy, Handle<JSReceiver> target) {
1001 : // TODO(cbruni): avoid creating another KeyAccumulator
1002 : Handle<FixedArray> keys;
1003 1876466 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
1004 : isolate_, keys,
1005 : KeyAccumulator::GetKeys(
1006 : target, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES,
1007 : GetKeysConversion::kConvertToString, is_for_in_, skip_indices_),
1008 : Nothing<bool>());
1009 922915 : Maybe<bool> result = AddKeysFromJSProxy(proxy, keys);
1010 922915 : return result;
1011 : }
1012 :
1013 : } // namespace internal
1014 120216 : } // namespace v8
|