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