• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/objects/lookup.h"
6 
7 #include "src/common/globals.h"
8 #include "src/deoptimizer/deoptimizer.h"
9 #include "src/execution/isolate-inl.h"
10 #include "src/execution/protectors-inl.h"
11 #include "src/init/bootstrapper.h"
12 #include "src/logging/counters.h"
13 #include "src/objects/arguments-inl.h"
14 #include "src/objects/elements.h"
15 #include "src/objects/field-type.h"
16 #include "src/objects/hash-table-inl.h"
17 #include "src/objects/heap-number-inl.h"
18 #include "src/objects/js-struct-inl.h"
19 #include "src/objects/map-updater.h"
20 #include "src/objects/ordered-hash-table.h"
21 #include "src/objects/property-details.h"
22 #include "src/objects/struct-inl.h"
23 
24 #if V8_ENABLE_WEBASSEMBLY
25 #include "src/wasm/wasm-objects-inl.h"
26 #endif  // V8_ENABLE_WEBASSEMBLY
27 
28 namespace v8 {
29 namespace internal {
30 
PropertyKey(Isolate * isolate,Handle<Object> key,bool * success)31 PropertyKey::PropertyKey(Isolate* isolate, Handle<Object> key, bool* success) {
32   if (key->ToIntegerIndex(&index_)) {
33     *success = true;
34     return;
35   }
36   *success = Object::ToName(isolate, key).ToHandle(&name_);
37   if (!*success) {
38     DCHECK(isolate->has_pending_exception());
39     index_ = LookupIterator::kInvalidIndex;
40     return;
41   }
42   if (!name_->AsIntegerIndex(&index_)) {
43     // {AsIntegerIndex} may modify {index_} before deciding to fail.
44     index_ = LookupIterator::kInvalidIndex;
45   }
46 }
47 
LookupIterator(Isolate * isolate,Handle<Object> receiver,Handle<Name> name,Handle<Map> transition_map,PropertyDetails details,bool has_property)48 LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
49                                Handle<Name> name, Handle<Map> transition_map,
50                                PropertyDetails details, bool has_property)
51     : configuration_(DEFAULT),
52       state_(TRANSITION),
53       has_property_(has_property),
54       interceptor_state_(InterceptorState::kUninitialized),
55       property_details_(details),
56       isolate_(isolate),
57       name_(name),
58       transition_(transition_map),
59       receiver_(receiver),
60       lookup_start_object_(receiver),
61       index_(kInvalidIndex) {
62   holder_ = GetRoot(isolate, lookup_start_object_);
63 }
64 
65 template <bool is_element>
Start()66 void LookupIterator::Start() {
67   // GetRoot might allocate if lookup_start_object_ is a string.
68   holder_ = GetRoot(isolate_, lookup_start_object_, index_);
69 
70   {
71     DisallowGarbageCollection no_gc;
72 
73     has_property_ = false;
74     state_ = NOT_FOUND;
75 
76     JSReceiver holder = *holder_;
77     Map map = holder.map(isolate_);
78 
79     state_ = LookupInHolder<is_element>(map, holder);
80     if (IsFound()) return;
81 
82     NextInternal<is_element>(map, holder);
83   }
84 }
85 
86 template void LookupIterator::Start<true>();
87 template void LookupIterator::Start<false>();
88 
Next()89 void LookupIterator::Next() {
90   DCHECK_NE(JSPROXY, state_);
91   DCHECK_NE(TRANSITION, state_);
92   DisallowGarbageCollection no_gc;
93   has_property_ = false;
94 
95   JSReceiver holder = *holder_;
96   Map map = holder.map(isolate_);
97 
98   if (map.IsSpecialReceiverMap()) {
99     state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
100                          : LookupInSpecialHolder<false>(map, holder);
101     if (IsFound()) return;
102   }
103 
104   IsElement() ? NextInternal<true>(map, holder)
105               : NextInternal<false>(map, holder);
106 }
107 
108 template <bool is_element>
NextInternal(Map map,JSReceiver holder)109 void LookupIterator::NextInternal(Map map, JSReceiver holder) {
110   do {
111     JSReceiver maybe_holder = NextHolder(map);
112     if (maybe_holder.is_null()) {
113       if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
114         RestartLookupForNonMaskingInterceptors<is_element>();
115         return;
116       }
117       state_ = NOT_FOUND;
118       if (holder != *holder_) holder_ = handle(holder, isolate_);
119       return;
120     }
121     holder = maybe_holder;
122     map = holder.map(isolate_);
123     state_ = LookupInHolder<is_element>(map, holder);
124   } while (!IsFound());
125 
126   holder_ = handle(holder, isolate_);
127 }
128 
129 template <bool is_element>
RestartInternal(InterceptorState interceptor_state)130 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
131   interceptor_state_ = interceptor_state;
132   property_details_ = PropertyDetails::Empty();
133   number_ = InternalIndex::NotFound();
134   Start<is_element>();
135 }
136 
137 template void LookupIterator::RestartInternal<true>(InterceptorState);
138 template void LookupIterator::RestartInternal<false>(InterceptorState);
139 
140 // static
GetRootForNonJSReceiver(Isolate * isolate,Handle<Object> lookup_start_object,size_t index)141 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
142     Isolate* isolate, Handle<Object> lookup_start_object, size_t index) {
143   // Strings are the only objects with properties (only elements) directly on
144   // the wrapper. Hence we can skip generating the wrapper for all other cases.
145   if (lookup_start_object->IsString(isolate) &&
146       index <
147           static_cast<size_t>(String::cast(*lookup_start_object).length())) {
148     // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
149     // context, ensuring that we don't leak it into JS?
150     Handle<JSFunction> constructor = isolate->string_function();
151     Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
152     Handle<JSPrimitiveWrapper>::cast(result)->set_value(*lookup_start_object);
153     return result;
154   }
155   Handle<HeapObject> root(
156       lookup_start_object->GetPrototypeChainRootMap(isolate).prototype(isolate),
157       isolate);
158   if (root->IsNull(isolate)) {
159     isolate->PushStackTraceAndDie(
160         reinterpret_cast<void*>(lookup_start_object->ptr()));
161   }
162   return Handle<JSReceiver>::cast(root);
163 }
164 
GetReceiverMap() const165 Handle<Map> LookupIterator::GetReceiverMap() const {
166   if (receiver_->IsNumber(isolate_)) return factory()->heap_number_map();
167   return handle(Handle<HeapObject>::cast(receiver_)->map(isolate_), isolate_);
168 }
169 
HasAccess() const170 bool LookupIterator::HasAccess() const {
171   // TRANSITION is true when being called from DefineNamedOwnIC.
172   DCHECK(state_ == ACCESS_CHECK || state_ == TRANSITION);
173   return isolate_->MayAccess(handle(isolate_->context(), isolate_),
174                              GetHolder<JSObject>());
175 }
176 
177 template <bool is_element>
ReloadPropertyInformation()178 void LookupIterator::ReloadPropertyInformation() {
179   state_ = BEFORE_PROPERTY;
180   interceptor_state_ = InterceptorState::kUninitialized;
181   state_ = LookupInHolder<is_element>(holder_->map(isolate_), *holder_);
182   DCHECK(IsFound() || !holder_->HasFastProperties(isolate_));
183 }
184 
185 // static
InternalUpdateProtector(Isolate * isolate,Handle<Object> receiver_generic,Handle<Name> name)186 void LookupIterator::InternalUpdateProtector(Isolate* isolate,
187                                              Handle<Object> receiver_generic,
188                                              Handle<Name> name) {
189   if (isolate->bootstrapper()->IsActive()) return;
190   if (!receiver_generic->IsHeapObject()) return;
191   Handle<HeapObject> receiver = Handle<HeapObject>::cast(receiver_generic);
192 
193   ReadOnlyRoots roots(isolate);
194   if (*name == roots.constructor_string()) {
195     // Setting the constructor property could change an instance's @@species
196     if (receiver->IsJSArray(isolate)) {
197       if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
198       isolate->CountUsage(
199           v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
200       Protectors::InvalidateArraySpeciesLookupChain(isolate);
201       return;
202     } else if (receiver->IsJSPromise(isolate)) {
203       if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
204       Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
205       return;
206     } else if (receiver->IsJSRegExp(isolate)) {
207       if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
208       Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
209       return;
210     } else if (receiver->IsJSTypedArray(isolate)) {
211       if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
212       Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
213       return;
214     }
215     if (receiver->map(isolate).is_prototype_map()) {
216       DisallowGarbageCollection no_gc;
217       // Setting the constructor of any prototype with the @@species protector
218       // (of any realm) also needs to invalidate the protector.
219       if (isolate->IsInAnyContext(*receiver,
220                                   Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
221         if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
222         isolate->CountUsage(
223             v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified);
224         Protectors::InvalidateArraySpeciesLookupChain(isolate);
225       } else if (receiver->IsJSPromisePrototype()) {
226         if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
227         Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
228       } else if (receiver->IsJSRegExpPrototype()) {
229         if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
230         Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
231       } else if (receiver->IsJSTypedArrayPrototype()) {
232         if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
233         Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
234       }
235     }
236   } else if (*name == roots.next_string()) {
237     if (receiver->IsJSArrayIterator() ||
238         receiver->IsJSArrayIteratorPrototype()) {
239       // Setting the next property of %ArrayIteratorPrototype% also needs to
240       // invalidate the array iterator protector.
241       if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return;
242       Protectors::InvalidateArrayIteratorLookupChain(isolate);
243     } else if (receiver->IsJSMapIterator() ||
244                receiver->IsJSMapIteratorPrototype()) {
245       if (!Protectors::IsMapIteratorLookupChainIntact(isolate)) return;
246       Protectors::InvalidateMapIteratorLookupChain(isolate);
247     } else if (receiver->IsJSSetIterator() ||
248                receiver->IsJSSetIteratorPrototype()) {
249       if (!Protectors::IsSetIteratorLookupChainIntact(isolate)) return;
250       Protectors::InvalidateSetIteratorLookupChain(isolate);
251     } else if (receiver->IsJSStringIterator() ||
252                receiver->IsJSStringIteratorPrototype()) {
253       // Setting the next property of %StringIteratorPrototype% invalidates the
254       // string iterator protector.
255       if (!Protectors::IsStringIteratorLookupChainIntact(isolate)) return;
256       Protectors::InvalidateStringIteratorLookupChain(isolate);
257     }
258   } else if (*name == roots.species_symbol()) {
259     // Setting the Symbol.species property of any Array, Promise or TypedArray
260     // constructor invalidates the @@species protector
261     if (receiver->IsJSArrayConstructor()) {
262       if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
263       isolate->CountUsage(
264           v8::Isolate::UseCounterFeature::kArraySpeciesModified);
265       Protectors::InvalidateArraySpeciesLookupChain(isolate);
266     } else if (receiver->IsJSPromiseConstructor()) {
267       if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
268       Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
269     } else if (receiver->IsJSRegExpConstructor()) {
270       if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
271       Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
272     } else if (receiver->IsTypedArrayConstructor()) {
273       if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
274       Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
275     }
276   } else if (*name == roots.is_concat_spreadable_symbol()) {
277     if (!Protectors::IsIsConcatSpreadableLookupChainIntact(isolate)) return;
278     Protectors::InvalidateIsConcatSpreadableLookupChain(isolate);
279   } else if (*name == roots.iterator_symbol()) {
280     if (receiver->IsJSArray(isolate)) {
281       if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return;
282       Protectors::InvalidateArrayIteratorLookupChain(isolate);
283     } else if (receiver->IsJSSet(isolate) || receiver->IsJSSetIterator() ||
284                receiver->IsJSSetIteratorPrototype() ||
285                receiver->IsJSSetPrototype()) {
286       if (Protectors::IsSetIteratorLookupChainIntact(isolate)) {
287         Protectors::InvalidateSetIteratorLookupChain(isolate);
288       }
289     } else if (receiver->IsJSMapIterator() ||
290                receiver->IsJSMapIteratorPrototype()) {
291       if (Protectors::IsMapIteratorLookupChainIntact(isolate)) {
292         Protectors::InvalidateMapIteratorLookupChain(isolate);
293       }
294     } else if (receiver->IsJSIteratorPrototype()) {
295       if (Protectors::IsMapIteratorLookupChainIntact(isolate)) {
296         Protectors::InvalidateMapIteratorLookupChain(isolate);
297       }
298       if (Protectors::IsSetIteratorLookupChainIntact(isolate)) {
299         Protectors::InvalidateSetIteratorLookupChain(isolate);
300       }
301     } else if (isolate->IsInAnyContext(
302                    *receiver, Context::INITIAL_STRING_PROTOTYPE_INDEX)) {
303       // Setting the Symbol.iterator property of String.prototype invalidates
304       // the string iterator protector. Symbol.iterator can also be set on a
305       // String wrapper, but not on a primitive string. We only support
306       // protector for primitive strings.
307       if (!Protectors::IsStringIteratorLookupChainIntact(isolate)) return;
308       Protectors::InvalidateStringIteratorLookupChain(isolate);
309     }
310   } else if (*name == roots.resolve_string()) {
311     if (!Protectors::IsPromiseResolveLookupChainIntact(isolate)) return;
312     // Setting the "resolve" property on any %Promise% intrinsic object
313     // invalidates the Promise.resolve protector.
314     if (receiver->IsJSPromiseConstructor()) {
315       Protectors::InvalidatePromiseResolveLookupChain(isolate);
316     }
317   } else if (*name == roots.then_string()) {
318     if (!Protectors::IsPromiseThenLookupChainIntact(isolate)) return;
319     // Setting the "then" property on any JSPromise instance or on the
320     // initial %PromisePrototype% invalidates the Promise#then protector.
321     // Also setting the "then" property on the initial %ObjectPrototype%
322     // invalidates the Promise#then protector, since we use this protector
323     // to guard the fast-path in AsyncGeneratorResolve, where we can skip
324     // the ResolvePromise step and go directly to FulfillPromise if we
325     // know that the Object.prototype doesn't contain a "then" method.
326     if (receiver->IsJSPromise(isolate) || receiver->IsJSObjectPrototype() ||
327         receiver->IsJSPromisePrototype()) {
328       Protectors::InvalidatePromiseThenLookupChain(isolate);
329     }
330   }
331 }
332 
PrepareForDataProperty(Handle<Object> value)333 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
334   DCHECK(state_ == DATA || state_ == ACCESSOR);
335   DCHECK(HolderIsReceiverOrHiddenPrototype());
336 #if V8_ENABLE_WEBASSEMBLY
337   DCHECK(!receiver_->IsWasmObject(isolate_));
338 #endif  // V8_ENABLE_WEBASSEMBLY
339 
340   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
341   // We are not interested in tracking constness of a JSProxy's direct
342   // properties.
343   DCHECK_IMPLIES(holder->IsJSProxy(isolate_), name()->IsPrivate(isolate_));
344   if (holder->IsJSProxy(isolate_)) return;
345 
346   if (IsElement(*holder)) {
347     Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
348     ElementsKind kind = holder_obj->GetElementsKind(isolate_);
349     ElementsKind to = value->OptimalElementsKind(isolate_);
350     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
351     to = GetMoreGeneralElementsKind(kind, to);
352 
353     if (kind != to) {
354       JSObject::TransitionElementsKind(holder_obj, to);
355     }
356 
357     // Copy the backing store if it is copy-on-write.
358     if (IsSmiOrObjectElementsKind(to) || IsSealedElementsKind(to) ||
359         IsNonextensibleElementsKind(to)) {
360       JSObject::EnsureWritableFastElements(holder_obj);
361     }
362     return;
363   }
364 
365   if (holder->IsJSGlobalObject(isolate_)) {
366     Handle<GlobalDictionary> dictionary(
367         JSGlobalObject::cast(*holder).global_dictionary(isolate_, kAcquireLoad),
368         isolate());
369     Handle<PropertyCell> cell(dictionary->CellAt(isolate_, dictionary_entry()),
370                               isolate());
371     property_details_ = cell->property_details();
372     PropertyCell::PrepareForAndSetValue(
373         isolate(), dictionary, dictionary_entry(), value, property_details_);
374     return;
375   }
376 
377   PropertyConstness new_constness = PropertyConstness::kConst;
378   if (constness() == PropertyConstness::kConst) {
379     DCHECK_EQ(PropertyKind::kData, property_details_.kind());
380     // Check that current value matches new value otherwise we should make
381     // the property mutable.
382     if (holder->HasFastProperties(isolate_)) {
383       if (!IsConstFieldValueEqualTo(*value)) {
384         new_constness = PropertyConstness::kMutable;
385       }
386     } else if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) {
387       if (!IsConstDictValueEqualTo(*value)) {
388         property_details_ =
389             property_details_.CopyWithConstness(PropertyConstness::kMutable);
390 
391         // We won't reach the map updating code after Map::Update below, because
392         // that's only for the case that the existing map is a fast mode map.
393         // Therefore, we need to perform the necessary updates to the property
394         // details and the prototype validity cell directly.
395         if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
396           SwissNameDictionary dict = holder->property_dictionary_swiss();
397           dict.DetailsAtPut(dictionary_entry(), property_details_);
398         } else {
399           NameDictionary dict = holder->property_dictionary();
400           dict.DetailsAtPut(dictionary_entry(), property_details_);
401         }
402 
403         Map old_map = holder->map(isolate_);
404         if (old_map.is_prototype_map()) {
405           JSObject::InvalidatePrototypeChains(old_map);
406         }
407       }
408       return;
409     }
410   }
411 
412   if (!holder->HasFastProperties(isolate_)) return;
413 
414   Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
415   Handle<Map> old_map(holder->map(isolate_), isolate_);
416 
417   Handle<Map> new_map = Map::Update(isolate_, old_map);
418   if (!new_map->is_dictionary_map()) {  // fast -> fast
419     new_map = Map::PrepareForDataProperty(
420         isolate(), new_map, descriptor_number(), new_constness, value);
421 
422     if (old_map.is_identical_to(new_map)) {
423       // Update the property details if the representation was None.
424       if (constness() != new_constness || representation().IsNone()) {
425         property_details_ = new_map->instance_descriptors(isolate_).GetDetails(
426             descriptor_number());
427       }
428       return;
429     }
430   }
431   // We should only get here if the new_map is different from the old map,
432   // otherwise we would have falled through to the is_identical_to check above.
433   DCHECK_NE(*old_map, *new_map);
434 
435   JSObject::MigrateToMap(isolate_, holder_obj, new_map);
436   ReloadPropertyInformation<false>();
437 
438   // If we transitioned from fast to slow and the property changed from kConst
439   // to kMutable, then this change in the constness is indicated by neither the
440   // old or the new map. We need to update the constness ourselves.
441   DCHECK(!old_map->is_dictionary_map());
442   if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && new_map->is_dictionary_map() &&
443       new_constness == PropertyConstness::kMutable) {  // fast -> slow
444     property_details_ =
445         property_details_.CopyWithConstness(PropertyConstness::kMutable);
446 
447     if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
448       SwissNameDictionary dict = holder_obj->property_dictionary_swiss();
449       dict.DetailsAtPut(dictionary_entry(), property_details_);
450     } else {
451       NameDictionary dict = holder_obj->property_dictionary();
452       dict.DetailsAtPut(dictionary_entry(), property_details_);
453     }
454 
455     DCHECK_IMPLIES(new_map->is_prototype_map(),
456                    !new_map->IsPrototypeValidityCellValid());
457   }
458 }
459 
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)460 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
461                                              PropertyAttributes attributes) {
462   DCHECK(state_ == DATA || state_ == ACCESSOR);
463   DCHECK(HolderIsReceiverOrHiddenPrototype());
464 
465   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
466 #if V8_ENABLE_WEBASSEMBLY
467   if (V8_UNLIKELY(holder->IsWasmObject())) UNREACHABLE();
468 #endif  // V8_ENABLE_WEBASSEMBLY
469 
470   // Property details can never change for private properties.
471   if (holder->IsJSProxy(isolate_)) {
472     DCHECK(name()->IsPrivate(isolate_));
473     return;
474   }
475 
476   Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
477   if (IsElement(*holder)) {
478     DCHECK(!holder_obj->HasTypedArrayOrRabGsabTypedArrayElements(isolate_));
479     DCHECK(attributes != NONE || !holder_obj->HasFastElements(isolate_));
480     Handle<FixedArrayBase> elements(holder_obj->elements(isolate_), isolate());
481     holder_obj->GetElementsAccessor(isolate_)->Reconfigure(
482         holder_obj, elements, number_, value, attributes);
483     ReloadPropertyInformation<true>();
484   } else if (holder_obj->HasFastProperties(isolate_)) {
485     Handle<Map> old_map(holder_obj->map(isolate_), isolate_);
486     // Force mutable to avoid changing constant value by reconfiguring
487     // kData -> kAccessor -> kData.
488     Handle<Map> new_map = MapUpdater::ReconfigureExistingProperty(
489         isolate_, old_map, descriptor_number(), i::PropertyKind::kData,
490         attributes, PropertyConstness::kMutable);
491     if (!new_map->is_dictionary_map()) {
492       // Make sure that the data property has a compatible representation.
493       // TODO(leszeks): Do this as part of ReconfigureExistingProperty.
494       new_map =
495           Map::PrepareForDataProperty(isolate(), new_map, descriptor_number(),
496                                       PropertyConstness::kMutable, value);
497     }
498     JSObject::MigrateToMap(isolate_, holder_obj, new_map);
499     ReloadPropertyInformation<false>();
500   }
501 
502   if (!IsElement(*holder) && !holder_obj->HasFastProperties(isolate_)) {
503     if (holder_obj->map(isolate_).is_prototype_map() &&
504         (((property_details_.attributes() & READ_ONLY) == 0 &&
505           (attributes & READ_ONLY) != 0) ||
506          (property_details_.attributes() & DONT_ENUM) !=
507              (attributes & DONT_ENUM))) {
508       // Invalidate prototype validity cell when a property is reconfigured
509       // from writable to read-only as this may invalidate transitioning store
510       // IC handlers.
511       // Invalidate prototype validity cell when a property changes
512       // enumerability to clear the prototype chain enum cache.
513       JSObject::InvalidatePrototypeChains(holder->map(isolate_));
514     }
515     if (holder_obj->IsJSGlobalObject(isolate_)) {
516       PropertyDetails details(PropertyKind::kData, attributes,
517                               PropertyCellType::kMutable);
518       Handle<GlobalDictionary> dictionary(
519           JSGlobalObject::cast(*holder_obj)
520               .global_dictionary(isolate_, kAcquireLoad),
521           isolate());
522 
523       Handle<PropertyCell> cell = PropertyCell::PrepareForAndSetValue(
524           isolate(), dictionary, dictionary_entry(), value, details);
525       property_details_ = cell->property_details();
526       DCHECK_EQ(cell->value(), *value);
527     } else {
528       PropertyDetails details(PropertyKind::kData, attributes,
529                               PropertyConstness::kMutable);
530       if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
531         Handle<SwissNameDictionary> dictionary(
532             holder_obj->property_dictionary_swiss(isolate_), isolate());
533         dictionary->ValueAtPut(dictionary_entry(), *value);
534         dictionary->DetailsAtPut(dictionary_entry(), details);
535         DCHECK_EQ(details.AsSmi(),
536                   dictionary->DetailsAt(dictionary_entry()).AsSmi());
537         property_details_ = details;
538       } else {
539         Handle<NameDictionary> dictionary(
540             holder_obj->property_dictionary(isolate_), isolate());
541         PropertyDetails original_details =
542             dictionary->DetailsAt(dictionary_entry());
543         int enumeration_index = original_details.dictionary_index();
544         DCHECK_GT(enumeration_index, 0);
545         details = details.set_index(enumeration_index);
546         dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
547         property_details_ = details;
548       }
549     }
550     state_ = DATA;
551   }
552 
553   WriteDataValue(value, true);
554 
555 #if VERIFY_HEAP
556   if (FLAG_verify_heap) {
557     holder->HeapObjectVerify(isolate());
558   }
559 #endif
560 }
561 
562 // Can only be called when the receiver is a JSObject, or when the name is a
563 // private field, otherwise JSProxy has to be handled via a trap.
564 // Adding properties to primitive values is not observable.
PrepareTransitionToDataProperty(Handle<JSReceiver> receiver,Handle<Object> value,PropertyAttributes attributes,StoreOrigin store_origin)565 void LookupIterator::PrepareTransitionToDataProperty(
566     Handle<JSReceiver> receiver, Handle<Object> value,
567     PropertyAttributes attributes, StoreOrigin store_origin) {
568   DCHECK_IMPLIES(receiver->IsJSProxy(isolate_), name()->IsPrivate(isolate_));
569   DCHECK_IMPLIES(!receiver.is_identical_to(GetStoreTarget<JSReceiver>()),
570                  name()->IsPrivateName());
571   if (state_ == TRANSITION) return;
572 
573   if (!IsElement() && name()->IsPrivate(isolate_)) {
574     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
575   }
576 
577   DCHECK(state_ != LookupIterator::ACCESSOR ||
578          (GetAccessors()->IsAccessorInfo(isolate_) &&
579           AccessorInfo::cast(*GetAccessors()).is_special_data_property()));
580   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
581   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
582 
583   Handle<Map> map(receiver->map(isolate_), isolate_);
584 
585   // Dictionary maps can always have additional data properties.
586   if (map->is_dictionary_map()) {
587     state_ = TRANSITION;
588     if (map->IsJSGlobalObjectMap()) {
589       DCHECK(!value->IsTheHole(isolate_));
590       // Don't set enumeration index (it will be set during value store).
591       property_details_ =
592           PropertyDetails(PropertyKind::kData, attributes,
593                           PropertyCell::InitialType(isolate_, *value));
594       transition_ = isolate_->factory()->NewPropertyCell(
595           name(), property_details_, value);
596       has_property_ = true;
597     } else {
598       // Don't set enumeration index (it will be set during value store).
599       property_details_ =
600           PropertyDetails(PropertyKind::kData, attributes,
601                           PropertyDetails::kConstIfDictConstnessTracking);
602       transition_ = map;
603     }
604     return;
605   }
606 
607   Handle<Map> transition =
608       Map::TransitionToDataProperty(isolate_, map, name_, value, attributes,
609                                     PropertyConstness::kConst, store_origin);
610   state_ = TRANSITION;
611   transition_ = transition;
612 
613   if (transition->is_dictionary_map()) {
614     DCHECK(!transition->IsJSGlobalObjectMap());
615     // Don't set enumeration index (it will be set during value store).
616     property_details_ =
617         PropertyDetails(PropertyKind::kData, attributes,
618                         PropertyDetails::kConstIfDictConstnessTracking);
619   } else {
620     property_details_ = transition->GetLastDescriptorDetails(isolate_);
621     has_property_ = true;
622   }
623 }
624 
ApplyTransitionToDataProperty(Handle<JSReceiver> receiver)625 void LookupIterator::ApplyTransitionToDataProperty(
626     Handle<JSReceiver> receiver) {
627   DCHECK_EQ(TRANSITION, state_);
628 
629   DCHECK_IMPLIES(!receiver.is_identical_to(GetStoreTarget<JSReceiver>()),
630                  name()->IsPrivateName());
631   holder_ = receiver;
632   if (receiver->IsJSGlobalObject(isolate_)) {
633     JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
634 
635     // Install a property cell.
636     Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
637     DCHECK(!global->HasFastProperties());
638     Handle<GlobalDictionary> dictionary(
639         global->global_dictionary(isolate_, kAcquireLoad), isolate_);
640 
641     dictionary =
642         GlobalDictionary::Add(isolate_, dictionary, name(), transition_cell(),
643                               property_details_, &number_);
644     global->set_global_dictionary(*dictionary, kReleaseStore);
645 
646     // Reload details containing proper enumeration index value.
647     property_details_ = transition_cell()->property_details();
648     has_property_ = true;
649     state_ = DATA;
650     return;
651   }
652   Handle<Map> transition = transition_map();
653   bool simple_transition =
654       transition->GetBackPointer(isolate_) == receiver->map(isolate_);
655 
656   if (configuration_ == DEFAULT && !transition->is_dictionary_map() &&
657       !transition->IsPrototypeValidityCellValid()) {
658     // Only LookupIterator instances with DEFAULT (full prototype chain)
659     // configuration can produce valid transition handler maps.
660     Handle<Object> validity_cell =
661         Map::GetOrCreatePrototypeChainValidityCell(transition, isolate());
662     transition->set_prototype_validity_cell(*validity_cell);
663   }
664 
665   if (!receiver->IsJSProxy(isolate_)) {
666     JSObject::MigrateToMap(isolate_, Handle<JSObject>::cast(receiver),
667                            transition);
668   }
669 
670   if (simple_transition) {
671     number_ = transition->LastAdded();
672     property_details_ = transition->GetLastDescriptorDetails(isolate_);
673     state_ = DATA;
674   } else if (receiver->map(isolate_).is_dictionary_map()) {
675     if (receiver->map(isolate_).is_prototype_map() &&
676         receiver->IsJSObject(isolate_)) {
677       JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
678     }
679     if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
680       Handle<SwissNameDictionary> dictionary(
681           receiver->property_dictionary_swiss(isolate_), isolate_);
682 
683       dictionary =
684           SwissNameDictionary::Add(isolate(), dictionary, name(),
685                                    isolate_->factory()->uninitialized_value(),
686                                    property_details_, &number_);
687       receiver->SetProperties(*dictionary);
688     } else {
689       Handle<NameDictionary> dictionary(receiver->property_dictionary(isolate_),
690                                         isolate_);
691 
692       dictionary =
693           NameDictionary::Add(isolate(), dictionary, name(),
694                               isolate_->factory()->uninitialized_value(),
695                               property_details_, &number_);
696       receiver->SetProperties(*dictionary);
697       // Reload details containing proper enumeration index value.
698       property_details_ = dictionary->DetailsAt(number_);
699     }
700     has_property_ = true;
701     state_ = DATA;
702 
703   } else {
704     ReloadPropertyInformation<false>();
705   }
706 }
707 
Delete()708 void LookupIterator::Delete() {
709   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
710   if (IsElement(*holder)) {
711     Handle<JSObject> object = Handle<JSObject>::cast(holder);
712     ElementsAccessor* accessor = object->GetElementsAccessor(isolate_);
713     accessor->Delete(object, number_);
714   } else {
715     DCHECK(!name()->IsPrivateName(isolate_));
716     bool is_prototype_map = holder->map(isolate_).is_prototype_map();
717     RCS_SCOPE(isolate_,
718               is_prototype_map
719                   ? RuntimeCallCounterId::kPrototypeObject_DeleteProperty
720                   : RuntimeCallCounterId::kObject_DeleteProperty);
721 
722     PropertyNormalizationMode mode =
723         is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
724 
725     if (holder->HasFastProperties(isolate_)) {
726       JSObject::NormalizeProperties(isolate_, Handle<JSObject>::cast(holder),
727                                     mode, 0, "DeletingProperty");
728       ReloadPropertyInformation<false>();
729     }
730     JSReceiver::DeleteNormalizedProperty(holder, dictionary_entry());
731     if (holder->IsJSObject(isolate_)) {
732       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
733     }
734   }
735   state_ = NOT_FOUND;
736 }
737 
TransitionToAccessorProperty(Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)738 void LookupIterator::TransitionToAccessorProperty(
739     Handle<Object> getter, Handle<Object> setter,
740     PropertyAttributes attributes) {
741   DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
742   // Can only be called when the receiver is a JSObject. JSProxy has to be
743   // handled via a trap. Adding properties to primitive values is not
744   // observable.
745   Handle<JSObject> receiver = GetStoreTarget<JSObject>();
746   if (!IsElement() && name()->IsPrivate(isolate_)) {
747     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
748   }
749 
750   if (!IsElement(*receiver) && !receiver->map(isolate_).is_dictionary_map()) {
751     Handle<Map> old_map(receiver->map(isolate_), isolate_);
752 
753     if (!holder_.is_identical_to(receiver)) {
754       holder_ = receiver;
755       state_ = NOT_FOUND;
756     } else if (state_ == INTERCEPTOR) {
757       LookupInRegularHolder<false>(*old_map, *holder_);
758     }
759     // The case of IsFound() && number_.is_not_found() can occur for
760     // interceptors.
761     DCHECK_IMPLIES(!IsFound(), number_.is_not_found());
762 
763     Handle<Map> new_map = Map::TransitionToAccessorProperty(
764         isolate_, old_map, name_, number_, getter, setter, attributes);
765     bool simple_transition =
766         new_map->GetBackPointer(isolate_) == receiver->map(isolate_);
767     JSObject::MigrateToMap(isolate_, receiver, new_map);
768 
769     if (simple_transition) {
770       number_ = new_map->LastAdded();
771       property_details_ = new_map->GetLastDescriptorDetails(isolate_);
772       state_ = ACCESSOR;
773       return;
774     }
775 
776     ReloadPropertyInformation<false>();
777     if (!new_map->is_dictionary_map()) return;
778   }
779 
780   Handle<AccessorPair> pair;
781   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair(isolate_)) {
782     pair = Handle<AccessorPair>::cast(GetAccessors());
783     // If the component and attributes are identical, nothing has to be done.
784     if (pair->Equals(*getter, *setter)) {
785       if (property_details().attributes() == attributes) {
786         if (!IsElement(*receiver)) JSObject::ReoptimizeIfPrototype(receiver);
787         return;
788       }
789     } else {
790       pair = AccessorPair::Copy(isolate(), pair);
791       pair->SetComponents(*getter, *setter);
792     }
793   } else {
794     pair = factory()->NewAccessorPair();
795     pair->SetComponents(*getter, *setter);
796   }
797 
798   TransitionToAccessorPair(pair, attributes);
799 
800 #if VERIFY_HEAP
801   if (FLAG_verify_heap) {
802     receiver->JSObjectVerify(isolate());
803   }
804 #endif
805 }
806 
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)807 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
808                                               PropertyAttributes attributes) {
809   Handle<JSObject> receiver = GetStoreTarget<JSObject>();
810   holder_ = receiver;
811 
812   PropertyDetails details(PropertyKind::kAccessor, attributes,
813                           PropertyCellType::kMutable);
814 
815   if (IsElement(*receiver)) {
816     // TODO(verwaest): Move code into the element accessor.
817     isolate_->CountUsage(v8::Isolate::kIndexAccessor);
818     Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(receiver);
819 
820     dictionary = NumberDictionary::Set(isolate_, dictionary, array_index(),
821                                        pair, receiver, details);
822     receiver->RequireSlowElements(*dictionary);
823 
824     if (receiver->HasSlowArgumentsElements(isolate_)) {
825       SloppyArgumentsElements parameter_map =
826           SloppyArgumentsElements::cast(receiver->elements(isolate_));
827       uint32_t length = parameter_map.length();
828       if (number_.is_found() && number_.as_uint32() < length) {
829         parameter_map.set_mapped_entries(
830             number_.as_int(), ReadOnlyRoots(isolate_).the_hole_value());
831       }
832       parameter_map.set_arguments(*dictionary);
833     } else {
834       receiver->set_elements(*dictionary);
835     }
836 
837     ReloadPropertyInformation<true>();
838   } else {
839     PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES;
840     if (receiver->map(isolate_).is_prototype_map()) {
841       JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
842       mode = KEEP_INOBJECT_PROPERTIES;
843     }
844 
845     // Normalize object to make this operation simple.
846     JSObject::NormalizeProperties(isolate_, receiver, mode, 0,
847                                   "TransitionToAccessorPair");
848 
849     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
850     JSObject::ReoptimizeIfPrototype(receiver);
851 
852     ReloadPropertyInformation<false>();
853   }
854 }
855 
HolderIsReceiver() const856 bool LookupIterator::HolderIsReceiver() const {
857   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
858   // Optimization that only works if configuration_ is not mutable.
859   if (!check_prototype_chain()) return true;
860   return *receiver_ == *holder_;
861 }
862 
HolderIsReceiverOrHiddenPrototype() const863 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
864   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
865   // Optimization that only works if configuration_ is not mutable.
866   if (!check_prototype_chain()) return true;
867   if (*receiver_ == *holder_) return true;
868   if (!receiver_->IsJSGlobalProxy(isolate_)) return false;
869   return Handle<JSGlobalProxy>::cast(receiver_)->map(isolate_).prototype(
870              isolate_) == *holder_;
871 }
872 
FetchValue(AllocationPolicy allocation_policy) const873 Handle<Object> LookupIterator::FetchValue(
874     AllocationPolicy allocation_policy) const {
875   Object result;
876   if (IsElement(*holder_)) {
877 #if V8_ENABLE_WEBASSEMBLY
878     if (V8_UNLIKELY(holder_->IsWasmObject(isolate_))) {
879       if (holder_->IsWasmStruct()) {
880         // WasmStructs don't have elements.
881         return isolate_->factory()->undefined_value();
882       }
883       Handle<WasmArray> holder = GetHolder<WasmArray>();
884       return WasmArray::GetElement(isolate_, holder, number_.as_uint32());
885     }
886 #endif  // V8_ENABLE_WEBASSEMBLY
887     DCHECK(holder_->IsJSObject(isolate_));
888     Handle<JSObject> holder = GetHolder<JSObject>();
889     ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
890     return accessor->Get(holder, number_);
891   } else if (holder_->IsJSGlobalObject(isolate_)) {
892     Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
893     result = holder->global_dictionary(isolate_, kAcquireLoad)
894                  .ValueAt(isolate_, dictionary_entry());
895   } else if (!holder_->HasFastProperties(isolate_)) {
896     if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
897       result = holder_->property_dictionary_swiss(isolate_).ValueAt(
898           dictionary_entry());
899     } else {
900       result = holder_->property_dictionary(isolate_).ValueAt(
901           isolate_, dictionary_entry());
902     }
903   } else if (property_details_.location() == PropertyLocation::kField) {
904     DCHECK_EQ(PropertyKind::kData, property_details_.kind());
905 #if V8_ENABLE_WEBASSEMBLY
906     if (V8_UNLIKELY(holder_->IsWasmObject(isolate_))) {
907       if (allocation_policy == AllocationPolicy::kAllocationDisallowed) {
908         // TODO(ishell): consider taking field type into account and relaxing
909         // this a bit.
910         return isolate_->factory()->undefined_value();
911       }
912       if (holder_->IsWasmArray(isolate_)) {
913         // WasmArrays don't have other named properties besides "length".
914         DCHECK_EQ(*name_, ReadOnlyRoots(isolate_).length_string());
915         Handle<WasmArray> holder = GetHolder<WasmArray>();
916         uint32_t length = holder->length();
917         return isolate_->factory()->NewNumberFromUint(length);
918       }
919       Handle<WasmStruct> holder = GetHolder<WasmStruct>();
920       return WasmStruct::GetField(isolate_, holder,
921                                   property_details_.field_index());
922     }
923 #endif  // V8_ENABLE_WEBASSEMBLY
924 
925     DCHECK(holder_->IsJSObject(isolate_));
926     Handle<JSObject> holder = GetHolder<JSObject>();
927     FieldIndex field_index =
928         FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
929     if (allocation_policy == AllocationPolicy::kAllocationDisallowed &&
930         field_index.is_inobject() && field_index.is_double()) {
931       return isolate_->factory()->undefined_value();
932     }
933     return JSObject::FastPropertyAt(
934         isolate_, holder, property_details_.representation(), field_index);
935   } else {
936     result =
937         holder_->map(isolate_).instance_descriptors(isolate_).GetStrongValue(
938             isolate_, descriptor_number());
939   }
940   return handle(result, isolate_);
941 }
942 
IsConstFieldValueEqualTo(Object value) const943 bool LookupIterator::IsConstFieldValueEqualTo(Object value) const {
944   DCHECK(!IsElement(*holder_));
945   DCHECK(holder_->HasFastProperties(isolate_));
946   DCHECK_EQ(PropertyLocation::kField, property_details_.location());
947   DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
948   if (value.IsUninitialized(isolate())) {
949     // Storing uninitialized value means that we are preparing for a computed
950     // property value in an object literal. The initializing store will follow
951     // and it will properly update constness based on the actual value.
952     return true;
953   }
954   Handle<JSObject> holder = GetHolder<JSObject>();
955   FieldIndex field_index =
956       FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
957   if (property_details_.representation().IsDouble()) {
958     if (!value.IsNumber(isolate_)) return false;
959     uint64_t bits;
960     Object current_value = holder->RawFastPropertyAt(isolate_, field_index);
961     DCHECK(current_value.IsHeapNumber(isolate_));
962     bits = HeapNumber::cast(current_value).value_as_bits(kRelaxedLoad);
963     // Use bit representation of double to check for hole double, since
964     // manipulating the signaling NaN used for the hole in C++, e.g. with
965     // bit_cast or value(), will change its value on ia32 (the x87 stack is
966     // used to return values and stores to the stack silently clear the
967     // signalling bit).
968     if (bits == kHoleNanInt64) {
969       // Uninitialized double field.
970       return true;
971     }
972     return Object::SameNumberValue(bit_cast<double>(bits), value.Number());
973   } else {
974     Object current_value = holder->RawFastPropertyAt(isolate_, field_index);
975     if (current_value.IsUninitialized(isolate()) || current_value == value) {
976       return true;
977     }
978     return current_value.IsNumber(isolate_) && value.IsNumber(isolate_) &&
979            Object::SameNumberValue(current_value.Number(), value.Number());
980   }
981 }
982 
IsConstDictValueEqualTo(Object value) const983 bool LookupIterator::IsConstDictValueEqualTo(Object value) const {
984   DCHECK(!IsElement(*holder_));
985   DCHECK(!holder_->HasFastProperties(isolate_));
986   DCHECK(!holder_->IsJSGlobalObject());
987   DCHECK(!holder_->IsJSProxy());
988   DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
989 
990   DisallowHeapAllocation no_gc;
991 
992   if (value.IsUninitialized(isolate())) {
993     // Storing uninitialized value means that we are preparing for a computed
994     // property value in an object literal. The initializing store will follow
995     // and it will properly update constness based on the actual value.
996     return true;
997   }
998   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
999   Object current_value;
1000   if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
1001     SwissNameDictionary dict = holder->property_dictionary_swiss();
1002     current_value = dict.ValueAt(dictionary_entry());
1003   } else {
1004     NameDictionary dict = holder->property_dictionary();
1005     current_value = dict.ValueAt(dictionary_entry());
1006   }
1007 
1008   if (current_value.IsUninitialized(isolate()) || current_value == value) {
1009     return true;
1010   }
1011   return current_value.IsNumber(isolate_) && value.IsNumber(isolate_) &&
1012          Object::SameNumberValue(current_value.Number(), value.Number());
1013 }
1014 
GetFieldDescriptorIndex() const1015 int LookupIterator::GetFieldDescriptorIndex() const {
1016   DCHECK(has_property_);
1017   DCHECK(holder_->HasFastProperties());
1018   DCHECK_EQ(PropertyLocation::kField, property_details_.location());
1019   DCHECK_EQ(PropertyKind::kData, property_details_.kind());
1020   // TODO(jkummerow): Propagate InternalIndex further.
1021   return descriptor_number().as_int();
1022 }
1023 
GetAccessorIndex() const1024 int LookupIterator::GetAccessorIndex() const {
1025   DCHECK(has_property_);
1026   DCHECK(holder_->HasFastProperties(isolate_));
1027   DCHECK_EQ(PropertyLocation::kDescriptor, property_details_.location());
1028   DCHECK_EQ(PropertyKind::kAccessor, property_details_.kind());
1029   return descriptor_number().as_int();
1030 }
1031 
GetFieldIndex() const1032 FieldIndex LookupIterator::GetFieldIndex() const {
1033   DCHECK(has_property_);
1034   DCHECK(holder_->HasFastProperties(isolate_));
1035   DCHECK_EQ(PropertyLocation::kField, property_details_.location());
1036   DCHECK(!IsElement(*holder_));
1037   return FieldIndex::ForDescriptor(holder_->map(isolate_), descriptor_number());
1038 }
1039 
GetPropertyCell() const1040 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
1041   DCHECK(!IsElement(*holder_));
1042   Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
1043   return handle(holder->global_dictionary(isolate_, kAcquireLoad)
1044                     .CellAt(isolate_, dictionary_entry()),
1045                 isolate_);
1046 }
1047 
GetAccessors() const1048 Handle<Object> LookupIterator::GetAccessors() const {
1049   DCHECK_EQ(ACCESSOR, state_);
1050   return FetchValue();
1051 }
1052 
GetDataValue(AllocationPolicy allocation_policy) const1053 Handle<Object> LookupIterator::GetDataValue(
1054     AllocationPolicy allocation_policy) const {
1055   DCHECK_EQ(DATA, state_);
1056   Handle<Object> value = FetchValue(allocation_policy);
1057   return value;
1058 }
1059 
GetDataValue(SeqCstAccessTag tag) const1060 Handle<Object> LookupIterator::GetDataValue(SeqCstAccessTag tag) const {
1061   DCHECK_EQ(DATA, state_);
1062   DCHECK_EQ(PropertyLocation::kField, property_details_.location());
1063   DCHECK_EQ(PropertyKind::kData, property_details_.kind());
1064   // Currently only shared structs support sequentially consistent access.
1065   Handle<JSSharedStruct> holder = GetHolder<JSSharedStruct>();
1066   FieldIndex field_index =
1067       FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
1068   return JSObject::FastPropertyAt(
1069       isolate_, holder, property_details_.representation(), field_index, tag);
1070 }
1071 
WriteDataValue(Handle<Object> value,bool initializing_store)1072 void LookupIterator::WriteDataValue(Handle<Object> value,
1073                                     bool initializing_store) {
1074   DCHECK_EQ(DATA, state_);
1075 #if V8_ENABLE_WEBASSEMBLY
1076   // WriteDataValueToWasmObject() must be used instead for writing to
1077   // WasmObjects.
1078   DCHECK(!holder_->IsWasmObject(isolate_));
1079 #endif  // V8_ENABLE_WEBASSEMBLY
1080   DCHECK_IMPLIES(holder_->IsJSSharedStruct(), value->IsShared());
1081 
1082   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
1083   if (IsElement(*holder)) {
1084     Handle<JSObject> object = Handle<JSObject>::cast(holder);
1085     ElementsAccessor* accessor = object->GetElementsAccessor(isolate_);
1086     accessor->Set(object, number_, *value);
1087   } else if (holder->HasFastProperties(isolate_)) {
1088     DCHECK(holder->IsJSObject(isolate_));
1089     if (property_details_.location() == PropertyLocation::kField) {
1090       // Check that in case of VariableMode::kConst field the existing value is
1091       // equal to |value|.
1092       DCHECK_IMPLIES(!initializing_store && property_details_.constness() ==
1093                                                 PropertyConstness::kConst,
1094                      IsConstFieldValueEqualTo(*value));
1095       JSObject::cast(*holder).WriteToField(descriptor_number(),
1096                                            property_details_, *value);
1097     } else {
1098       DCHECK_EQ(PropertyLocation::kDescriptor, property_details_.location());
1099       DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
1100     }
1101   } else if (holder->IsJSGlobalObject(isolate_)) {
1102     // PropertyCell::PrepareForAndSetValue already wrote the value into the
1103     // cell.
1104 #ifdef DEBUG
1105     GlobalDictionary dictionary =
1106         JSGlobalObject::cast(*holder).global_dictionary(isolate_, kAcquireLoad);
1107     PropertyCell cell = dictionary.CellAt(isolate_, dictionary_entry());
1108     DCHECK(cell.value() == *value ||
1109            (cell.value().IsString() && value->IsString() &&
1110             String::cast(cell.value()).Equals(String::cast(*value))));
1111 #endif  // DEBUG
1112   } else {
1113     DCHECK_IMPLIES(holder->IsJSProxy(isolate_), name()->IsPrivate(isolate_));
1114     // Check similar to fast mode case above.
1115     DCHECK_IMPLIES(
1116         V8_DICT_PROPERTY_CONST_TRACKING_BOOL && !initializing_store &&
1117             property_details_.constness() == PropertyConstness::kConst,
1118         holder->IsJSProxy(isolate_) || IsConstDictValueEqualTo(*value));
1119 
1120     if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
1121       SwissNameDictionary dictionary =
1122           holder->property_dictionary_swiss(isolate_);
1123       dictionary.ValueAtPut(dictionary_entry(), *value);
1124     } else {
1125       NameDictionary dictionary = holder->property_dictionary(isolate_);
1126       dictionary.ValueAtPut(dictionary_entry(), *value);
1127     }
1128   }
1129 }
1130 
WriteDataValue(Handle<Object> value,SeqCstAccessTag tag)1131 void LookupIterator::WriteDataValue(Handle<Object> value, SeqCstAccessTag tag) {
1132   DCHECK_EQ(DATA, state_);
1133   DCHECK_EQ(PropertyLocation::kField, property_details_.location());
1134   DCHECK_EQ(PropertyKind::kData, property_details_.kind());
1135   // Currently only shared structs support sequentially consistent access.
1136   Handle<JSSharedStruct> holder = GetHolder<JSSharedStruct>();
1137   DisallowGarbageCollection no_gc;
1138   FieldIndex field_index =
1139       FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
1140   holder->FastPropertyAtPut(field_index, *value, tag);
1141 }
1142 
SwapDataValue(Handle<Object> value,SeqCstAccessTag tag)1143 Handle<Object> LookupIterator::SwapDataValue(Handle<Object> value,
1144                                              SeqCstAccessTag tag) {
1145   DCHECK_EQ(DATA, state_);
1146   DCHECK_EQ(PropertyLocation::kField, property_details_.location());
1147   DCHECK_EQ(PropertyKind::kData, property_details_.kind());
1148   // Currently only shared structs support sequentially consistent access.
1149   Handle<JSSharedStruct> holder = GetHolder<JSSharedStruct>();
1150   DisallowGarbageCollection no_gc;
1151   FieldIndex field_index =
1152       FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
1153   return handle(holder->RawFastPropertyAtSwap(field_index, *value, tag),
1154                 isolate_);
1155 }
1156 
1157 #if V8_ENABLE_WEBASSEMBLY
1158 
wasm_value_type() const1159 wasm::ValueType LookupIterator::wasm_value_type() const {
1160   DCHECK(has_property_);
1161   DCHECK(holder_->IsWasmObject(isolate_));
1162   if (holder_->IsWasmStruct(isolate_)) {
1163     wasm::StructType* wasm_struct_type = WasmStruct::cast(*holder_).type();
1164     return wasm_struct_type->field(property_details_.field_index());
1165 
1166   } else {
1167     DCHECK(holder_->IsWasmArray(isolate_));
1168     wasm::ArrayType* wasm_array_type = WasmArray::cast(*holder_).type();
1169     return wasm_array_type->element_type();
1170   }
1171 }
1172 
WriteDataValueToWasmObject(Handle<Object> value)1173 void LookupIterator::WriteDataValueToWasmObject(Handle<Object> value) {
1174   DCHECK_EQ(DATA, state_);
1175   DCHECK(holder_->IsWasmObject(isolate_));
1176   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
1177 
1178   if (IsElement(*holder)) {
1179     // TODO(ishell): consider supporting indexed access to WasmStruct fields.
1180     // TODO(v8:11804): implement stores to WasmArrays.
1181     UNIMPLEMENTED();
1182   } else {
1183     // WasmArrays don't have writable properties.
1184     DCHECK(holder->IsWasmStruct());
1185     Handle<WasmStruct> wasm_holder = GetHolder<WasmStruct>();
1186     WasmStruct::SetField(isolate_, wasm_holder, property_details_.field_index(),
1187                          value);
1188   }
1189 }
1190 
1191 #endif  // V8_ENABLE_WEBASSEMBLY
1192 
1193 template <bool is_element>
SkipInterceptor(JSObject holder)1194 bool LookupIterator::SkipInterceptor(JSObject holder) {
1195   InterceptorInfo info = GetInterceptor<is_element>(holder);
1196   if (!is_element && name_->IsSymbol(isolate_) &&
1197       !info.can_intercept_symbols()) {
1198     return true;
1199   }
1200   if (info.non_masking()) {
1201     switch (interceptor_state_) {
1202       case InterceptorState::kUninitialized:
1203         interceptor_state_ = InterceptorState::kSkipNonMasking;
1204         V8_FALLTHROUGH;
1205       case InterceptorState::kSkipNonMasking:
1206         return true;
1207       case InterceptorState::kProcessNonMasking:
1208         return false;
1209     }
1210   }
1211   return interceptor_state_ == InterceptorState::kProcessNonMasking;
1212 }
1213 
NextHolder(Map map)1214 JSReceiver LookupIterator::NextHolder(Map map) {
1215   DisallowGarbageCollection no_gc;
1216   if (map.prototype(isolate_) == ReadOnlyRoots(isolate_).null_value()) {
1217     return JSReceiver();
1218   }
1219   if (!check_prototype_chain() && !map.IsJSGlobalProxyMap()) {
1220     return JSReceiver();
1221   }
1222   return JSReceiver::cast(map.prototype(isolate_));
1223 }
1224 
NotFound(JSReceiver const holder) const1225 LookupIterator::State LookupIterator::NotFound(JSReceiver const holder) const {
1226   if (!holder.IsJSTypedArray(isolate_)) return NOT_FOUND;
1227   if (IsElement()) return INTEGER_INDEXED_EXOTIC;
1228   if (!name_->IsString(isolate_)) return NOT_FOUND;
1229   return IsSpecialIndex(String::cast(*name_)) ? INTEGER_INDEXED_EXOTIC
1230                                               : NOT_FOUND;
1231 }
1232 
1233 namespace {
1234 
1235 template <bool is_element>
HasInterceptor(Map map,size_t index)1236 bool HasInterceptor(Map map, size_t index) {
1237   if (is_element) {
1238     if (index > JSObject::kMaxElementIndex) {
1239       // There is currently no way to install interceptors on an object with
1240       // typed array elements.
1241       DCHECK(!map.has_typed_array_or_rab_gsab_typed_array_elements());
1242       return map.has_named_interceptor();
1243     }
1244     return map.has_indexed_interceptor();
1245   } else {
1246     return map.has_named_interceptor();
1247   }
1248 }
1249 
1250 }  // namespace
1251 
1252 template <bool is_element>
LookupInSpecialHolder(Map const map,JSReceiver const holder)1253 LookupIterator::State LookupIterator::LookupInSpecialHolder(
1254     Map const map, JSReceiver const holder) {
1255   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
1256   switch (state_) {
1257     case NOT_FOUND:
1258       if (map.IsJSProxyMap()) {
1259         if (is_element || !name_->IsPrivate(isolate_)) return JSPROXY;
1260       }
1261 #if V8_ENABLE_WEBASSEMBLY
1262       if (map.IsWasmObjectMap()) {
1263         return LookupInRegularHolder<is_element>(map, holder);
1264       }
1265 #endif  // V8_ENABLE_WEBASSEMBLY
1266       if (map.is_access_check_needed()) {
1267         if (is_element || !name_->IsPrivate(isolate_) ||
1268             name_->IsPrivateName(isolate_))
1269           return ACCESS_CHECK;
1270       }
1271       V8_FALLTHROUGH;
1272     case ACCESS_CHECK:
1273       if (check_interceptor() && HasInterceptor<is_element>(map, index_) &&
1274           !SkipInterceptor<is_element>(JSObject::cast(holder))) {
1275         if (is_element || !name_->IsPrivate(isolate_)) return INTERCEPTOR;
1276       }
1277       V8_FALLTHROUGH;
1278     case INTERCEPTOR:
1279       if (map.IsJSGlobalObjectMap() && !is_js_array_element(is_element)) {
1280         GlobalDictionary dict = JSGlobalObject::cast(holder).global_dictionary(
1281             isolate_, kAcquireLoad);
1282         number_ = dict.FindEntry(isolate(), name_);
1283         if (number_.is_not_found()) return NOT_FOUND;
1284         PropertyCell cell = dict.CellAt(isolate_, number_);
1285         if (cell.value(isolate_).IsTheHole(isolate_)) {
1286           return NOT_FOUND;
1287         }
1288         property_details_ = cell.property_details();
1289         has_property_ = true;
1290         switch (property_details_.kind()) {
1291           case v8::internal::PropertyKind::kData:
1292             return DATA;
1293           case v8::internal::PropertyKind::kAccessor:
1294             return ACCESSOR;
1295         }
1296       }
1297       return LookupInRegularHolder<is_element>(map, holder);
1298     case ACCESSOR:
1299     case DATA:
1300       return NOT_FOUND;
1301     case INTEGER_INDEXED_EXOTIC:
1302     case JSPROXY:
1303     case TRANSITION:
1304       UNREACHABLE();
1305   }
1306   UNREACHABLE();
1307 }
1308 
1309 template <bool is_element>
LookupInRegularHolder(Map const map,JSReceiver const holder)1310 LookupIterator::State LookupIterator::LookupInRegularHolder(
1311     Map const map, JSReceiver const holder) {
1312   DisallowGarbageCollection no_gc;
1313   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
1314     return NOT_FOUND;
1315   }
1316 
1317   if (is_element && IsElement(holder)) {
1318 #if V8_ENABLE_WEBASSEMBLY
1319     if (V8_UNLIKELY(holder.IsWasmObject(isolate_))) {
1320       // TODO(ishell): consider supporting indexed access to WasmStruct fields.
1321       if (holder.IsWasmArray(isolate_)) {
1322         WasmArray wasm_array = WasmArray::cast(holder);
1323         number_ = index_ < wasm_array.length() ? InternalIndex(index_)
1324                                                : InternalIndex::NotFound();
1325         wasm::ArrayType* wasm_array_type = wasm_array.type();
1326         property_details_ =
1327             PropertyDetails(PropertyKind::kData,
1328                             wasm_array_type->mutability() ? SEALED : FROZEN,
1329                             PropertyCellType::kNoCell);
1330 
1331       } else {
1332         DCHECK(holder.IsWasmStruct(isolate_));
1333         DCHECK(number_.is_not_found());
1334       }
1335     } else  // NOLINT(readability/braces)
1336 #endif      // V8_ENABLE_WEBASSEMBLY
1337     {
1338       JSObject js_object = JSObject::cast(holder);
1339       ElementsAccessor* accessor = js_object.GetElementsAccessor(isolate_);
1340       FixedArrayBase backing_store = js_object.elements(isolate_);
1341       number_ = accessor->GetEntryForIndex(isolate_, js_object, backing_store,
1342                                            index_);
1343       if (number_.is_not_found()) {
1344         return holder.IsJSTypedArray(isolate_) ? INTEGER_INDEXED_EXOTIC
1345                                                : NOT_FOUND;
1346       }
1347       property_details_ = accessor->GetDetails(js_object, number_);
1348       if (map.has_frozen_elements()) {
1349         property_details_ = property_details_.CopyAddAttributes(FROZEN);
1350       } else if (map.has_sealed_elements()) {
1351         property_details_ = property_details_.CopyAddAttributes(SEALED);
1352       }
1353     }
1354   } else if (!map.is_dictionary_map()) {
1355     DescriptorArray descriptors = map.instance_descriptors(isolate_);
1356     number_ = descriptors.SearchWithCache(isolate_, *name_, map);
1357     if (number_.is_not_found()) return NotFound(holder);
1358     property_details_ = descriptors.GetDetails(number_);
1359   } else {
1360     DCHECK_IMPLIES(holder.IsJSProxy(isolate_), name()->IsPrivate(isolate_));
1361     if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
1362       SwissNameDictionary dict = holder.property_dictionary_swiss(isolate_);
1363       number_ = dict.FindEntry(isolate(), *name_);
1364       if (number_.is_not_found()) return NotFound(holder);
1365       property_details_ = dict.DetailsAt(number_);
1366     } else {
1367       NameDictionary dict = holder.property_dictionary(isolate_);
1368       number_ = dict.FindEntry(isolate(), name_);
1369       if (number_.is_not_found()) return NotFound(holder);
1370       property_details_ = dict.DetailsAt(number_);
1371     }
1372   }
1373   has_property_ = true;
1374   switch (property_details_.kind()) {
1375     case v8::internal::PropertyKind::kData:
1376       return DATA;
1377     case v8::internal::PropertyKind::kAccessor:
1378       return ACCESSOR;
1379   }
1380 
1381   UNREACHABLE();
1382 }
1383 
GetInterceptorForFailedAccessCheck() const1384 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
1385     const {
1386   DCHECK_EQ(ACCESS_CHECK, state_);
1387   DisallowGarbageCollection no_gc;
1388   AccessCheckInfo access_check_info =
1389       AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
1390   if (!access_check_info.is_null()) {
1391     // There is currently no way to create objects with typed array elements
1392     // and access checks.
1393     DCHECK(!holder_->map().has_typed_array_or_rab_gsab_typed_array_elements());
1394     Object interceptor = is_js_array_element(IsElement())
1395                              ? access_check_info.indexed_interceptor()
1396                              : access_check_info.named_interceptor();
1397     if (interceptor != Object()) {
1398       return handle(InterceptorInfo::cast(interceptor), isolate_);
1399     }
1400   }
1401   return Handle<InterceptorInfo>();
1402 }
1403 
TryLookupCachedProperty(Handle<AccessorPair> accessor)1404 bool LookupIterator::TryLookupCachedProperty(Handle<AccessorPair> accessor) {
1405   DCHECK_EQ(state(), LookupIterator::ACCESSOR);
1406   return LookupCachedProperty(accessor);
1407 }
1408 
TryLookupCachedProperty()1409 bool LookupIterator::TryLookupCachedProperty() {
1410   if (state() != LookupIterator::ACCESSOR) return false;
1411 
1412   Handle<Object> accessor_pair = GetAccessors();
1413   return accessor_pair->IsAccessorPair(isolate_) &&
1414          LookupCachedProperty(Handle<AccessorPair>::cast(accessor_pair));
1415 }
1416 
LookupCachedProperty(Handle<AccessorPair> accessor_pair)1417 bool LookupIterator::LookupCachedProperty(Handle<AccessorPair> accessor_pair) {
1418   DCHECK_EQ(state(), LookupIterator::ACCESSOR);
1419   DCHECK(GetAccessors()->IsAccessorPair(isolate_));
1420 
1421   base::Optional<Name> maybe_name =
1422       FunctionTemplateInfo::TryGetCachedPropertyName(
1423           isolate(), accessor_pair->getter(isolate_));
1424   if (!maybe_name.has_value()) return false;
1425 
1426   // We have found a cached property! Modify the iterator accordingly.
1427   name_ = handle(maybe_name.value(), isolate_);
1428   Restart();
1429   CHECK_EQ(state(), LookupIterator::DATA);
1430   return true;
1431 }
1432 
1433 // static
TryGetOwnCowElement(Isolate * isolate,FixedArray array_elements,ElementsKind elements_kind,int array_length,size_t index)1434 base::Optional<Object> ConcurrentLookupIterator::TryGetOwnCowElement(
1435     Isolate* isolate, FixedArray array_elements, ElementsKind elements_kind,
1436     int array_length, size_t index) {
1437   DisallowGarbageCollection no_gc;
1438 
1439   CHECK_EQ(array_elements.map(), ReadOnlyRoots(isolate).fixed_cow_array_map());
1440   DCHECK(IsFastElementsKind(elements_kind) &&
1441          IsSmiOrObjectElementsKind(elements_kind));
1442   USE(elements_kind);
1443   DCHECK_GE(array_length, 0);
1444 
1445   //  ________________________________________
1446   // ( Check against both JSArray::length and )
1447   // ( FixedArray::length.                    )
1448   //  ----------------------------------------
1449   //         o   ^__^
1450   //          o  (oo)\_______
1451   //             (__)\       )\/\
1452   //                 ||----w |
1453   //                 ||     ||
1454   // The former is the source of truth, but due to concurrent reads it may not
1455   // match the given `array_elements`.
1456   if (index >= static_cast<size_t>(array_length)) return {};
1457   if (index >= static_cast<size_t>(array_elements.length())) return {};
1458 
1459   Object result = array_elements.get(isolate, static_cast<int>(index));
1460 
1461   //  ______________________________________
1462   // ( Filter out holes irrespective of the )
1463   // ( elements kind.                       )
1464   //  --------------------------------------
1465   //         o   ^__^
1466   //          o  (..)\_______
1467   //             (__)\       )\/\
1468   //                 ||----w |
1469   //                 ||     ||
1470   // The elements kind may not be consistent with the given elements backing
1471   // store.
1472   if (result == ReadOnlyRoots(isolate).the_hole_value()) return {};
1473 
1474   return result;
1475 }
1476 
1477 // static
1478 ConcurrentLookupIterator::Result
TryGetOwnConstantElement(Object * result_out,Isolate * isolate,LocalIsolate * local_isolate,JSObject holder,FixedArrayBase elements,ElementsKind elements_kind,size_t index)1479 ConcurrentLookupIterator::TryGetOwnConstantElement(
1480     Object* result_out, Isolate* isolate, LocalIsolate* local_isolate,
1481     JSObject holder, FixedArrayBase elements, ElementsKind elements_kind,
1482     size_t index) {
1483   DisallowGarbageCollection no_gc;
1484 
1485   DCHECK_LE(index, JSObject::kMaxElementIndex);
1486 
1487   // Own 'constant' elements (PropertyAttributes READ_ONLY|DONT_DELETE) occur in
1488   // three main cases:
1489   //
1490   // 1. Frozen elements: guaranteed constant.
1491   // 2. Dictionary elements: may be constant.
1492   // 3. String wrapper elements: guaranteed constant.
1493 
1494   // Interesting field reads below:
1495   //
1496   // - elements.length (immutable on FixedArrays).
1497   // - elements[i] (immutable if constant; be careful around dictionaries).
1498   // - holder.AsJSPrimitiveWrapper.value.AsString.length (immutable).
1499   // - holder.AsJSPrimitiveWrapper.value.AsString[i] (immutable).
1500   // - single_character_string_cache()->get().
1501 
1502   if (IsFrozenElementsKind(elements_kind)) {
1503     if (!elements.IsFixedArray()) return kGaveUp;
1504     FixedArray elements_fixed_array = FixedArray::cast(elements);
1505     if (index >= static_cast<uint32_t>(elements_fixed_array.length())) {
1506       return kGaveUp;
1507     }
1508     Object result = elements_fixed_array.get(isolate, static_cast<int>(index));
1509     if (IsHoleyElementsKindForRead(elements_kind) &&
1510         result == ReadOnlyRoots(isolate).the_hole_value()) {
1511       return kNotPresent;
1512     }
1513     *result_out = result;
1514     return kPresent;
1515   } else if (IsDictionaryElementsKind(elements_kind)) {
1516     if (!elements.IsNumberDictionary()) return kGaveUp;
1517     // TODO(jgruber, v8:7790): Add support. Dictionary elements require racy
1518     // NumberDictionary lookups. This should be okay in general (slot iteration
1519     // depends only on the dict's capacity), but 1. we'd need to update
1520     // NumberDictionary methods to do atomic reads, and 2. the dictionary
1521     // elements case isn't very important for callers of this function.
1522     return kGaveUp;
1523   } else if (IsStringWrapperElementsKind(elements_kind)) {
1524     // In this case we don't care about the actual `elements`. All in-bounds
1525     // reads are redirected to the wrapped String.
1526 
1527     JSPrimitiveWrapper js_value = JSPrimitiveWrapper::cast(holder);
1528     String wrapped_string = String::cast(js_value.value());
1529     return ConcurrentLookupIterator::TryGetOwnChar(
1530         static_cast<String*>(result_out), isolate, local_isolate,
1531         wrapped_string, index);
1532   } else {
1533     DCHECK(!IsFrozenElementsKind(elements_kind));
1534     DCHECK(!IsDictionaryElementsKind(elements_kind));
1535     DCHECK(!IsStringWrapperElementsKind(elements_kind));
1536     return kGaveUp;
1537   }
1538 
1539   UNREACHABLE();
1540 }
1541 
1542 // static
TryGetOwnChar(String * result_out,Isolate * isolate,LocalIsolate * local_isolate,String string,size_t index)1543 ConcurrentLookupIterator::Result ConcurrentLookupIterator::TryGetOwnChar(
1544     String* result_out, Isolate* isolate, LocalIsolate* local_isolate,
1545     String string, size_t index) {
1546   DisallowGarbageCollection no_gc;
1547   // The access guard below protects string accesses related to internalized
1548   // strings.
1549   // TODO(jgruber): Support other string kinds.
1550   Map string_map = string.map(isolate, kAcquireLoad);
1551   InstanceType type = string_map.instance_type();
1552   if (!(InstanceTypeChecker::IsInternalizedString(type)) ||
1553       InstanceTypeChecker::IsThinString(type)) {
1554     return kGaveUp;
1555   }
1556 
1557   const uint32_t length = static_cast<uint32_t>(string.length());
1558   if (index >= length) return kGaveUp;
1559 
1560   uint16_t charcode;
1561   {
1562     SharedStringAccessGuardIfNeeded access_guard(local_isolate);
1563     charcode = string.Get(static_cast<int>(index), PtrComprCageBase(isolate),
1564                           access_guard);
1565   }
1566 
1567   if (charcode > unibrow::Latin1::kMaxChar) return kGaveUp;
1568 
1569   Object value = isolate->factory()->single_character_string_cache()->get(
1570       charcode, kRelaxedLoad);
1571   if (value == ReadOnlyRoots(isolate).undefined_value()) return kGaveUp;
1572 
1573   *result_out = String::cast(value);
1574   return kPresent;
1575 }
1576 
1577 // static
TryGetPropertyCell(Isolate * isolate,LocalIsolate * local_isolate,Handle<JSGlobalObject> holder,Handle<Name> name)1578 base::Optional<PropertyCell> ConcurrentLookupIterator::TryGetPropertyCell(
1579     Isolate* isolate, LocalIsolate* local_isolate,
1580     Handle<JSGlobalObject> holder, Handle<Name> name) {
1581   DisallowGarbageCollection no_gc;
1582 
1583   Map holder_map = holder->map();
1584   if (holder_map.is_access_check_needed()) return {};
1585   if (holder_map.has_named_interceptor()) return {};
1586 
1587   GlobalDictionary dict = holder->global_dictionary(kAcquireLoad);
1588   base::Optional<PropertyCell> cell =
1589       dict.TryFindPropertyCellForConcurrentLookupIterator(isolate, name,
1590                                                           kRelaxedLoad);
1591   if (!cell.has_value()) return {};
1592 
1593   if (cell->property_details(kAcquireLoad).kind() == PropertyKind::kAccessor) {
1594     Object maybe_accessor_pair = cell->value(kAcquireLoad);
1595     if (!maybe_accessor_pair.IsAccessorPair()) return {};
1596 
1597     base::Optional<Name> maybe_cached_property_name =
1598         FunctionTemplateInfo::TryGetCachedPropertyName(
1599             isolate, AccessorPair::cast(maybe_accessor_pair)
1600                          .getter(isolate, kAcquireLoad));
1601     if (!maybe_cached_property_name.has_value()) return {};
1602 
1603     cell = dict.TryFindPropertyCellForConcurrentLookupIterator(
1604         isolate, handle(*maybe_cached_property_name, local_isolate),
1605         kRelaxedLoad);
1606     if (!cell.has_value()) return {};
1607     if (cell->property_details(kAcquireLoad).kind() != PropertyKind::kData)
1608       return {};
1609   }
1610 
1611   DCHECK(cell.has_value());
1612   DCHECK_EQ(cell->property_details(kAcquireLoad).kind(), PropertyKind::kData);
1613   return cell;
1614 }
1615 
1616 }  // namespace internal
1617 }  // namespace v8
1618