v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
lookup.cc
Go to the documentation of this file.
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
6
7#include <optional>
8
26
27namespace v8::internal {
28
29template <bool is_element>
31 // GetRoot might allocate if lookup_start_object_ is a string.
34 if (!maybe_holder.ToHandle(&holder_)) {
35 // This is an attempt to perform an own property lookup on a non-JSReceiver
36 // that doesn't have any properties.
37 DCHECK(!IsJSReceiver(*lookup_start_object_));
39 has_property_ = false;
41 return;
42 }
43
44 {
46
47 has_property_ = false;
49
51 Tagged<Map> map = holder->map(isolate_);
52
54 if (IsFound()) return;
55
56 NextInternal<is_element>(map, holder);
57 }
58}
59
60template void LookupIterator::Start<true>();
61template void LookupIterator::Start<false>();
62
68 has_property_ = false;
69
71 Tagged<Map> map = holder->map(isolate_);
72
73 if (IsSpecialReceiverMap(map)) {
75 : LookupInSpecialHolder<false>(map, holder);
76 if (IsFound()) return;
77 }
78
79 IsElement() ? NextInternal<true>(map, holder)
80 : NextInternal<false>(map, holder);
81}
82
83template <bool is_element>
85 do {
86 Tagged<JSReceiver> maybe_holder = NextHolder(map);
87 if (maybe_holder.is_null()) {
90 return;
91 }
93 if (holder != *holder_) holder_ = direct_handle(holder, isolate_);
94 return;
95 }
96 holder = maybe_holder;
97 map = holder->map(isolate_);
99 } while (!IsFound());
100
101 holder_ = direct_handle(holder, isolate_);
102}
103
104template <bool is_element>
111
112template void LookupIterator::RestartInternal<true>(InterceptorState);
113template void LookupIterator::RestartInternal<false>(InterceptorState);
114
116 DCHECK(IsJSTypedArray(*holder_, isolate_));
118
119 if (!IsElement(*holder_)) {
120 // This happens when the index is not an allowed index.
121 return;
122 }
123
125 ElementsAccessor* accessor = js_object->GetElementsAccessor(isolate_);
126 Tagged<FixedArrayBase> backing_store = js_object->elements(isolate_);
127 number_ =
128 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
129
130 if (number_.is_not_found()) {
131 // The state is already TYPED_ARRAY_INDEX_NOT_FOUND.
132 return;
133 }
134 property_details_ = accessor->GetDetails(js_object, number_);
135#ifdef DEBUG
136 Tagged<Map> map = holder_->map(isolate_);
137 DCHECK(!map->has_frozen_elements());
138 DCHECK(!map->has_sealed_elements());
139#endif // DEBUG
140 has_property_ = true;
142 state_ = DATA;
143}
144
145// static
147 Isolate* isolate, DirectHandle<JSPrimitive> lookup_start_object,
148 size_t index, Configuration configuration) {
149 // Strings are the only non-JSReceiver objects with properties (only elements
150 // and 'length') directly on the wrapper. Hence we can skip generating
151 // the wrapper for all other cases.
152 bool own_property_lookup = (configuration & kPrototypeChain) == 0;
153 if (IsString(*lookup_start_object, isolate)) {
154 if (own_property_lookup ||
155 index <
156 static_cast<size_t>(Cast<String>(*lookup_start_object)->length())) {
157 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the
158 // native context, ensuring that we don't leak it into JS?
159 DirectHandle<JSFunction> constructor = isolate->string_function();
161 isolate->factory()->NewJSObject(constructor);
163 return result;
164 }
165 } else if (own_property_lookup) {
166 // Signal that the lookup will not find anything.
167 return {};
168 }
171 ->prototype(isolate),
172 isolate);
173 if (IsNull(*root, isolate)) {
174 isolate->PushStackTraceAndDie(
175 reinterpret_cast<void*>((*lookup_start_object).ptr()));
176 }
177 return Cast<JSReceiver>(root);
178}
179
184
186 // TRANSITION is true when being called from DefineNamedOwnIC.
189}
190
191template <bool is_element>
198
199// static
201 Isolate* isolate, DirectHandle<JSAny> receiver_generic,
202 DirectHandle<Name> name) {
203 if (isolate->bootstrapper()->IsActive()) return;
204 if (!IsJSObject(*receiver_generic)) return;
205 auto receiver = Cast<JSObject>(receiver_generic);
206
207 ReadOnlyRoots roots(isolate);
208 if (*name == roots.constructor_string()) {
209 // Setting the constructor property could change an instance's @@species
210 if (IsJSArray(*receiver, isolate)) {
211 if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
212 isolate->CountUsage(
214 Protectors::InvalidateArraySpeciesLookupChain(isolate);
215 return;
216 } else if (IsJSPromise(*receiver, isolate)) {
217 if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
218 Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
219 return;
220 } else if (IsJSRegExp(*receiver, isolate)) {
221 if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
222 Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
223 return;
224 } else if (IsJSTypedArray(*receiver, isolate)) {
225 if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
226 Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
227 return;
228 }
229 if (receiver->map(isolate)->is_prototype_map()) {
231 // Setting the constructor of any prototype with the @@species protector
232 // (of any realm) also needs to invalidate the protector.
233 if (isolate->IsInCreationContext(
235 Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
236 if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
237 isolate->CountUsage(
239 Protectors::InvalidateArraySpeciesLookupChain(isolate);
240 } else if (IsJSPromisePrototype(*receiver)) {
241 if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
242 Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
243 } else if (IsJSRegExpPrototype(*receiver)) {
244 if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
245 Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
246 } else if (IsJSTypedArrayPrototype(*receiver)) {
247 if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
248 Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
249 }
250 }
251 } else if (*name == roots.next_string()) {
252 if (IsJSArrayIterator(*receiver) || IsJSArrayIteratorPrototype(*receiver)) {
253 // Setting the next property of %ArrayIteratorPrototype% also needs to
254 // invalidate the array iterator protector.
255 if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return;
256 Protectors::InvalidateArrayIteratorLookupChain(isolate);
257 } else if (IsJSMapIterator(*receiver) ||
258 IsJSMapIteratorPrototype(*receiver)) {
259 if (!Protectors::IsMapIteratorLookupChainIntact(isolate)) return;
260 Protectors::InvalidateMapIteratorLookupChain(isolate);
261 } else if (IsJSSetIterator(*receiver) ||
262 IsJSSetIteratorPrototype(*receiver)) {
263 if (!Protectors::IsSetIteratorLookupChainIntact(isolate)) return;
264 Protectors::InvalidateSetIteratorLookupChain(isolate);
265 } else if (IsJSStringIterator(*receiver) ||
266 IsJSStringIteratorPrototype(*receiver)) {
267 // Setting the next property of %StringIteratorPrototype% invalidates the
268 // string iterator protector.
269 if (!Protectors::IsStringIteratorLookupChainIntact(isolate)) return;
270 Protectors::InvalidateStringIteratorLookupChain(isolate);
271 }
272 } else if (*name == roots.species_symbol()) {
273 // Setting the Symbol.species property of any Array, Promise or TypedArray
274 // constructor invalidates the @@species protector
275 if (IsJSArrayConstructor(*receiver)) {
276 if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
277 isolate->CountUsage(
279 Protectors::InvalidateArraySpeciesLookupChain(isolate);
280 } else if (IsJSPromiseConstructor(*receiver)) {
281 if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
282 Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
283 } else if (IsJSRegExpConstructor(*receiver)) {
284 if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
285 Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
286 } else if (IsTypedArrayConstructor(*receiver)) {
287 if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
288 Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
289 }
290 } else if (*name == roots.is_concat_spreadable_symbol()) {
291 if (!Protectors::IsIsConcatSpreadableLookupChainIntact(isolate)) return;
292 Protectors::InvalidateIsConcatSpreadableLookupChain(isolate);
293 } else if (*name == roots.iterator_symbol()) {
294 if (IsJSArray(*receiver, isolate)) {
295 if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return;
296 Protectors::InvalidateArrayIteratorLookupChain(isolate);
297 } else if (IsJSSet(*receiver, isolate) || IsJSSetIterator(*receiver) ||
298 IsJSSetIteratorPrototype(*receiver) ||
299 IsJSSetPrototype(*receiver)) {
300 if (Protectors::IsSetIteratorLookupChainIntact(isolate)) {
301 Protectors::InvalidateSetIteratorLookupChain(isolate);
302 }
303 } else if (IsJSMapIterator(*receiver) ||
304 IsJSMapIteratorPrototype(*receiver)) {
305 if (Protectors::IsMapIteratorLookupChainIntact(isolate)) {
306 Protectors::InvalidateMapIteratorLookupChain(isolate);
307 }
308 } else if (IsJSIteratorPrototype(*receiver)) {
309 if (Protectors::IsMapIteratorLookupChainIntact(isolate)) {
310 Protectors::InvalidateMapIteratorLookupChain(isolate);
311 }
312 if (Protectors::IsSetIteratorLookupChainIntact(isolate)) {
313 Protectors::InvalidateSetIteratorLookupChain(isolate);
314 }
315 } else if (isolate->IsInCreationContext(
316 *receiver, Context::INITIAL_STRING_PROTOTYPE_INDEX)) {
317 // Setting the Symbol.iterator property of String.prototype invalidates
318 // the string iterator protector. Symbol.iterator can also be set on a
319 // String wrapper, but not on a primitive string. We only support
320 // protector for primitive strings.
321 if (!Protectors::IsStringIteratorLookupChainIntact(isolate)) return;
322 Protectors::InvalidateStringIteratorLookupChain(isolate);
323 }
324 } else if (*name == roots.resolve_string()) {
325 if (!Protectors::IsPromiseResolveLookupChainIntact(isolate)) return;
326 // Setting the "resolve" property on any %Promise% intrinsic object
327 // invalidates the Promise.resolve protector.
328 if (IsJSPromiseConstructor(*receiver)) {
329 Protectors::InvalidatePromiseResolveLookupChain(isolate);
330 }
331 } else if (*name == roots.then_string()) {
332 if (!Protectors::IsPromiseThenLookupChainIntact(isolate)) return;
333 // Setting the "then" property on any JSPromise instance or on the
334 // initial %PromisePrototype% invalidates the Promise#then protector.
335 // Also setting the "then" property on the initial %ObjectPrototype%
336 // invalidates the Promise#then protector, since we use this protector
337 // to guard the fast-path in AsyncGeneratorResolve, where we can skip
338 // the ResolvePromise step and go directly to FulfillPromise if we
339 // know that the Object.prototype doesn't contain a "then" method.
340 if (IsJSPromise(*receiver, isolate) || IsJSObjectPrototype(*receiver) ||
341 IsJSPromisePrototype(*receiver)) {
342 Protectors::InvalidatePromiseThenLookupChain(isolate);
343 }
344 } else if (*name == roots.match_all_symbol() ||
345 *name == roots.replace_symbol() || *name == roots.split_symbol()) {
346 if (!Protectors::IsNumberStringNotRegexpLikeIntact(isolate)) return;
347 // We need to protect the prototype chains of `Number.prototype` and
348 // `String.prototype`: that `Symbol.{matchAll|replace|split}` is not added
349 // as a property on any object on these prototype chains. We detect
350 // `Number.prototype` and `String.prototype` by checking for a prototype
351 // that is a JSPrimitiveWrapper. This is a safe approximation. Using
352 // JSPrimitiveWrapper as prototype should be sufficiently rare.
353 if (receiver->map()->is_prototype_map() &&
354 (IsJSPrimitiveWrapper(*receiver) || IsJSObjectPrototype(*receiver))) {
355 Protectors::InvalidateNumberStringNotRegexpLike(isolate);
356 }
357 } else if (*name == roots.to_primitive_symbol()) {
358 if (!Protectors::IsStringWrapperToPrimitiveIntact(isolate)) return;
359 if (isolate->IsInCreationContext(*receiver,
360 Context::INITIAL_STRING_PROTOTYPE_INDEX) ||
361 isolate->IsInCreationContext(*receiver,
362 Context::INITIAL_OBJECT_PROTOTYPE_INDEX) ||
363 IsStringWrapper(*receiver)) {
364 Protectors::InvalidateStringWrapperToPrimitive(isolate);
365 }
366 } else if (*name == roots.valueOf_string()) {
367 if (!Protectors::IsStringWrapperToPrimitiveIntact(isolate)) return;
368 if (isolate->IsInCreationContext(*receiver,
369 Context::INITIAL_STRING_PROTOTYPE_INDEX) ||
370 IsStringWrapper(*receiver)) {
371 Protectors::InvalidateStringWrapperToPrimitive(isolate);
372 }
373 } else if (*name == roots.length_string()) {
374 if (!Protectors::IsTypedArrayLengthLookupChainIntact(isolate)) return;
375 if (IsJSTypedArray(*receiver) || IsJSTypedArrayPrototype(*receiver) ||
376 isolate->IsInCreationContext(*receiver,
377 Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
378 Protectors::InvalidateTypedArrayLengthLookupChain(isolate);
379 }
380 }
381}
382
384 DCHECK(state_ == DATA || state_ == ACCESSOR);
387
389 // We are not interested in tracking constness of a JSProxy's direct
390 // properties.
391 DCHECK_IMPLIES(IsJSProxy(*holder, isolate_), name()->IsPrivate());
392 if (IsJSProxy(*holder, isolate_)) return;
393
394 if (IsElement(*holder)) {
395 DirectHandle<JSObject> holder_obj = Cast<JSObject>(holder);
396 ElementsKind kind = holder_obj->GetElementsKind(isolate_);
400
401 if (kind != to) {
402 JSObject::TransitionElementsKind(holder_obj, to);
403 }
404
405 // Copy the backing store if it is copy-on-write.
409 }
410 return;
411 }
412
413 if (IsJSGlobalObject(*holder, isolate_)) {
415 Cast<JSGlobalObject>(*holder)->global_dictionary(isolate_,
417 isolate());
419 dictionary->CellAt(isolate_, dictionary_entry()), isolate());
420 property_details_ = cell->property_details();
422 isolate(), dictionary, dictionary_entry(), value, property_details_);
423 return;
424 }
425
429 // Check that current value matches new value otherwise we should make
430 // the property mutable.
431 if (holder->HasFastProperties(isolate_)) {
432 if (!CanStayConst(*value)) new_constness = PropertyConstness::kMutable;
434 if (!DictCanStayConst(*value)) {
437
438 // We won't reach the map updating code after Map::Update below, because
439 // that's only for the case that the existing map is a fast mode map.
440 // Therefore, we need to perform the necessary updates to the property
441 // details and the prototype validity cell directly.
444 holder->property_dictionary_swiss();
445 dict->DetailsAtPut(dictionary_entry(), property_details_);
446 } else {
447 Tagged<NameDictionary> dict = holder->property_dictionary();
448 dict->DetailsAtPut(dictionary_entry(), property_details_);
449 }
450
451 Tagged<Map> old_map = holder->map(isolate_);
452 if (old_map->is_prototype_map()) {
454 }
455 }
456 return;
457 }
458 }
459
460 if (!holder->HasFastProperties(isolate_)) return;
461
462 auto holder_obj = Cast<JSObject>(holder);
463 Handle<Map> old_map(holder->map(isolate_), isolate_);
464
465 DirectHandle<Map> new_map = Map::Update(isolate_, old_map);
466 if (!new_map->is_dictionary_map()) { // fast -> fast
468 isolate(), new_map, descriptor_number(), new_constness, value);
469
470 if (old_map.is_identical_to(new_map)) {
471 // Update the property details if the representation was None.
472 if (constness() != new_constness || representation().IsNone()) {
473 property_details_ = new_map->instance_descriptors(isolate_)->GetDetails(
475 }
476 return;
477 }
478 }
479 // We should only get here if the new_map is different from the old map,
480 // otherwise we would have fallen through to the is_identical_to check above.
481 DCHECK_NE(*old_map, *new_map);
482
483 JSObject::MigrateToMap(isolate_, holder_obj, new_map);
485
486 // If we transitioned from fast to slow and the property changed from kConst
487 // to kMutable, then this change in the constness is indicated by neither the
488 // old or the new map. We need to update the constness ourselves.
489 DCHECK(!old_map->is_dictionary_map());
490 if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && new_map->is_dictionary_map() &&
491 new_constness == PropertyConstness::kMutable) { // fast -> slow
494
497 holder_obj->property_dictionary_swiss();
498 dict->DetailsAtPut(dictionary_entry(), property_details_);
499 } else {
500 Tagged<NameDictionary> dict = holder_obj->property_dictionary();
501 dict->DetailsAtPut(dictionary_entry(), property_details_);
502 }
503
504 DCHECK_IMPLIES(new_map->is_prototype_map(),
505 !new_map->IsPrototypeValidityCellValid());
506 }
507}
508
510 PropertyAttributes attributes) {
511 DCHECK(state_ == DATA || state_ == ACCESSOR);
513
515 if (V8_UNLIKELY(IsWasmObject(*holder))) UNREACHABLE();
516
517 // Property details can never change for private properties.
518 if (IsJSProxy(*holder, isolate_)) {
519 DCHECK(name()->IsPrivate());
520 return;
521 }
522
523 DirectHandle<JSObject> holder_obj = Cast<JSObject>(holder);
524 if (IsElement(*holder)) {
525 DCHECK(!holder_obj->HasTypedArrayOrRabGsabTypedArrayElements(isolate_));
526 DCHECK(attributes != NONE || !holder_obj->HasFastElements(isolate_));
527 DirectHandle<FixedArrayBase> elements(holder_obj->elements(isolate_),
528 isolate());
529 holder_obj->GetElementsAccessor(isolate_)->Reconfigure(
530 holder_obj, elements, number_, value, attributes);
532 } else if (holder_obj->HasFastProperties(isolate_)) {
533 DirectHandle<Map> old_map(holder_obj->map(isolate_), isolate_);
534 // Force mutable to avoid changing constant value by reconfiguring
535 // kData -> kAccessor -> kData.
537 isolate_, old_map, descriptor_number(), i::PropertyKind::kData,
538 attributes, PropertyConstness::kMutable);
539 if (!new_map->is_dictionary_map()) {
540 // Make sure that the data property has a compatible representation.
541 // TODO(leszeks): Do this as part of ReconfigureExistingProperty.
542 new_map =
545 }
546 JSObject::MigrateToMap(isolate_, holder_obj, new_map);
548 }
549
550 if (!IsElement(*holder) && !holder_obj->HasFastProperties(isolate_)) {
551 if (holder_obj->map(isolate_)->is_prototype_map() &&
553 (attributes & READ_ONLY) != 0) ||
555 (attributes & DONT_ENUM))) {
556 // Invalidate prototype validity cell when a property is reconfigured
557 // from writable to read-only as this may invalidate transitioning store
558 // IC handlers.
559 // Invalidate prototype validity cell when a property changes
560 // enumerability to clear the prototype chain enum cache.
562 }
563 if (IsJSGlobalObject(*holder_obj, isolate_)) {
564 PropertyDetails details(PropertyKind::kData, attributes,
567 Cast<JSGlobalObject>(*holder_obj)
568 ->global_dictionary(isolate_, kAcquireLoad),
569 isolate());
570
572 isolate(), dictionary, dictionary_entry(), value, details);
573 property_details_ = cell->property_details();
574 DCHECK_EQ(cell->value(), *value);
575 } else {
576 PropertyDetails details(PropertyKind::kData, attributes,
580 holder_obj->property_dictionary_swiss(isolate_), isolate());
581 dictionary->ValueAtPut(dictionary_entry(), *value);
582 dictionary->DetailsAtPut(dictionary_entry(), details);
583 DCHECK_EQ(details.AsSmi(),
584 dictionary->DetailsAt(dictionary_entry()).AsSmi());
585 property_details_ = details;
586 } else {
588 holder_obj->property_dictionary(isolate_), isolate());
589 PropertyDetails original_details =
590 dictionary->DetailsAt(dictionary_entry());
591 int enumeration_index = original_details.dictionary_index();
592 DCHECK_GT(enumeration_index, 0);
593 details = details.set_index(enumeration_index);
594 dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
595 property_details_ = details;
596 }
597 }
598 state_ = DATA;
599 }
600
601 WriteDataValue(value, true);
602
603#if VERIFY_HEAP
604 if (v8_flags.verify_heap) {
605 holder->HeapObjectVerify(isolate());
606 }
607#endif
608}
609
610// Can only be called when the receiver is a JSObject, or when the name is a
611// private field, otherwise JSProxy has to be handled via a trap.
612// Adding properties to primitive values is not observable.
615 PropertyAttributes attributes, StoreOrigin store_origin) {
616 DCHECK_IMPLIES(IsJSProxy(*receiver, isolate_), name()->IsPrivate());
618 name()->IsPrivateName());
619 DCHECK(!IsAlwaysSharedSpaceJSObject(*receiver));
620 if (state_ == TRANSITION) return;
621
622 if (!IsElement() && name()->IsPrivate()) {
623 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
624 }
625
627 IsAccessorInfo(*GetAccessors(), isolate_));
630
632
633 // Dictionary maps can always have additional data properties.
634 if (map->is_dictionary_map()) {
636 if (IsJSGlobalObjectMap(*map)) {
637 DCHECK(!IsTheHole(*value, isolate_));
638 // Don't set enumeration index (it will be set during value store).
643 name(), property_details_, value);
644 has_property_ = true;
645 } else {
646 // Don't set enumeration index (it will be set during value store).
651 }
652 return;
653 }
654
655 DirectHandle<Map> transition =
656 Map::TransitionToDataProperty(isolate_, map, name_, value, attributes,
657 PropertyConstness::kConst, store_origin);
659 transition_ = transition;
660
661 if (transition->is_dictionary_map()) {
662 DCHECK(!IsJSGlobalObjectMap(*transition));
663 // Don't set enumeration index (it will be set during value store).
667 } else {
668 property_details_ = transition->GetLastDescriptorDetails(isolate_);
669 has_property_ = true;
670 }
671}
672
676
678 name()->IsPrivateName());
680 if (IsJSGlobalObject(*receiver, isolate_)) {
682
683 // Install a property cell.
684 auto global = Cast<JSGlobalObject>(receiver);
685 DCHECK(!global->HasFastProperties());
687 global->global_dictionary(isolate_, kAcquireLoad), isolate_);
688
689 dictionary =
690 GlobalDictionary::Add(isolate_, dictionary, name(), transition_cell(),
692 global->set_global_dictionary(*dictionary, kReleaseStore);
693
694 // Reload details containing proper enumeration index value.
695 property_details_ = transition_cell()->property_details();
696 has_property_ = true;
697 state_ = DATA;
698 return;
699 }
700 DirectHandle<Map> transition = transition_map();
701 bool simple_transition =
702 transition->GetBackPointer(isolate_) == receiver->map(isolate_);
703
704 if (configuration_ == DEFAULT && !transition->is_dictionary_map() &&
705 !transition->IsPrototypeValidityCellValid()) {
706 // Only LookupIterator instances with DEFAULT (full prototype chain)
707 // configuration can produce valid transition handler maps.
708 DirectHandle<UnionOf<Smi, Cell>> validity_cell =
710 transition->set_prototype_validity_cell(*validity_cell, kRelaxedStore);
711 }
712
713 if (!IsJSProxy(*receiver, isolate_)) {
715 }
716
717 if (simple_transition) {
718 number_ = transition->LastAdded();
719 property_details_ = transition->GetLastDescriptorDetails(isolate_);
720 state_ = DATA;
721 } else if (receiver->map(isolate_)->is_dictionary_map()) {
722 if (receiver->map(isolate_)->is_prototype_map() &&
723 IsJSObject(*receiver, isolate_)) {
725 }
728 receiver->property_dictionary_swiss(isolate_), isolate_);
729
730 dictionary =
731 SwissNameDictionary::Add(isolate(), dictionary, name(),
732 isolate_->factory()->uninitialized_value(),
734 receiver->SetProperties(*dictionary);
735 } else {
737 receiver->property_dictionary(isolate_), isolate_);
738
739 dictionary =
740 NameDictionary::Add(isolate(), dictionary, name(),
741 isolate_->factory()->uninitialized_value(),
743 receiver->SetProperties(*dictionary);
744 // TODO(pthier): Add flags to swiss dictionaries.
745 if (name()->IsInteresting(isolate())) {
746 dictionary->set_may_have_interesting_properties(true);
747 }
748 // Reload details containing proper enumeration index value.
749 property_details_ = dictionary->DetailsAt(number_);
750 }
751 has_property_ = true;
752 state_ = DATA;
753
754 } else {
756 }
757}
758
761 if (IsElement(*holder)) {
762 DirectHandle<JSObject> object = Cast<JSObject>(holder);
763 ElementsAccessor* accessor = object->GetElementsAccessor(isolate_);
764 accessor->Delete(object, number_);
765 } else {
767 bool is_prototype_map = holder->map(isolate_)->is_prototype_map();
770 ? RuntimeCallCounterId::kPrototypeObject_DeleteProperty
771 : RuntimeCallCounterId::kObject_DeleteProperty);
772
775
776 if (holder->HasFastProperties(isolate_)) {
778 "DeletingProperty");
780 }
782 if (IsJSObject(*holder, isolate_)) {
784 }
785 }
787}
788
791 PropertyAttributes attributes) {
793 // Can only be called when the receiver is a JSObject. JSProxy has to be
794 // handled via a trap. Adding properties to primitive values is not
795 // observable.
797 if (!IsElement() && name()->IsPrivate()) {
798 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
799 }
800
801 if (!IsElement(*receiver) && !receiver->map(isolate_)->is_dictionary_map()) {
803
804 if (!holder_.is_identical_to(receiver)) {
807 } else if (state_ == INTERCEPTOR) {
809 }
810 // The case of IsFound() && number_.is_not_found() can occur for
811 // interceptors.
813
815 isolate_, old_map, name_, number_, getter, setter, attributes);
816 bool simple_transition =
817 new_map->GetBackPointer(isolate_) == receiver->map(isolate_);
819
820 if (simple_transition) {
821 number_ = new_map->LastAdded();
822 property_details_ = new_map->GetLastDescriptorDetails(isolate_);
824 return;
825 }
826
828 if (!new_map->is_dictionary_map()) return;
829 }
830
832 if (state() == ACCESSOR && IsAccessorPair(*GetAccessors(), isolate_)) {
834 // If the component and attributes are identical, nothing has to be done.
835 if (pair->Equals(*getter, *setter)) {
836 if (property_details().attributes() == attributes) {
838 return;
839 }
840 } else {
841 pair = AccessorPair::Copy(isolate(), pair);
842 pair->SetComponents(*getter, *setter);
843 }
844 } else {
845 pair = factory()->NewAccessorPair();
846 pair->SetComponents(*getter, *setter);
847 }
848
849 TransitionToAccessorPair(pair, attributes);
850
851#if VERIFY_HEAP
852 if (v8_flags.verify_heap) {
853 receiver->JSObjectVerify(isolate());
854 }
855#endif
856}
857
859 PropertyAttributes attributes) {
862
863 PropertyDetails details(PropertyKind::kAccessor, attributes,
865
866 if (IsElement(*receiver)) {
867 // TODO(verwaest): Move code into the element accessor.
871
872 dictionary = NumberDictionary::Set(isolate_, dictionary, array_index(),
873 pair, receiver, details);
874 receiver->RequireSlowElements(*dictionary);
875
876 if (receiver->HasSlowArgumentsElements(isolate_)) {
877 Tagged<SloppyArgumentsElements> parameter_map =
879 uint32_t length = parameter_map->length();
880 if (number_.is_found() && number_.as_uint32() < length) {
881 parameter_map->set_mapped_entries(
882 number_.as_int(), ReadOnlyRoots(isolate_).the_hole_value());
883 }
884 parameter_map->set_arguments(*dictionary);
885 } else {
886 receiver->set_elements(*dictionary);
887 }
888
890 } else {
892 if (receiver->map(isolate_)->is_prototype_map()) {
895 }
896
897 // Normalize object to make this operation simple.
899 "TransitionToAccessorPair");
900
903
905 }
906}
907
910 // Optimization that only works if configuration_ is not mutable.
911 if (!check_prototype_chain()) return true;
912 return *receiver_ == *holder_;
913}
914
917 // Optimization that only works if configuration_ is not mutable.
918 if (!check_prototype_chain()) return true;
919 if (*receiver_ == *holder_) return true;
920 if (!IsJSGlobalProxy(*receiver_, isolate_)) return false;
921 return Cast<JSGlobalProxy>(receiver_)->map(isolate_)->prototype(isolate_) ==
922 *holder_;
923}
924
926 AllocationPolicy allocation_policy) const {
929 if (IsElement(*holder_)) {
931 ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
932 return accessor->Get(isolate_, holder, number_);
933 } else if (IsJSGlobalObject(*holder_, isolate_)) {
935 result = holder->global_dictionary(isolate_, kAcquireLoad)
936 ->ValueAt(isolate_, dictionary_entry());
937 } else if (!holder_->HasFastProperties(isolate_)) {
939 result = holder_->property_dictionary_swiss(isolate_)->ValueAt(
941 } else {
942 result = holder_->property_dictionary(isolate_)->ValueAt(
944 }
948 FieldIndex field_index =
950 if (allocation_policy == AllocationPolicy::kAllocationDisallowed &&
951 field_index.is_inobject() && field_index.is_double()) {
952 return isolate_->factory()->undefined_value();
953 }
955 isolate_, holder, property_details_.representation(), field_index);
956 } else {
957 result =
958 holder_->map(isolate_)->instance_descriptors(isolate_)->GetStrongValue(
960 }
962}
963
965 DCHECK(!holder_.is_null());
967 DCHECK(holder_->HasFastProperties(isolate_));
970 if (IsUninitialized(value, isolate())) {
971 // Storing uninitialized value means that we are preparing for a computed
972 // property value in an object literal. The initializing store will follow
973 // and it will properly update constness based on the actual value.
974 return true;
975 }
977 FieldIndex field_index =
980 if (!IsNumber(value, isolate_)) return false;
981 uint64_t bits;
982 Tagged<Object> current_value =
983 holder->RawFastPropertyAt(isolate_, field_index);
984 DCHECK(IsHeapNumber(current_value, isolate_));
985 bits = Cast<HeapNumber>(current_value)->value_as_bits();
986 // Use bit representation of double to check for hole double, since
987 // manipulating the signaling NaN used for the hole in C++, e.g. with
988 // base::bit_cast or value(), will change its value on ia32 (the x87
989 // stack is used to return values and stores to the stack silently clear the
990 // signalling bit).
991 // Only allow initializing stores to double to stay constant.
992 return bits == kHoleNanInt64;
993 }
994
995 Tagged<Object> current_value =
996 holder->RawFastPropertyAt(isolate_, field_index);
997 return IsUninitialized(current_value, isolate());
998}
999
1001 DCHECK(!holder_.is_null());
1003 DCHECK(!holder_->HasFastProperties(isolate_));
1004 DCHECK(!IsJSGlobalObject(*holder_));
1005 DCHECK(!IsJSProxy(*holder_));
1007
1009
1010 if (IsUninitialized(value, isolate())) {
1011 // Storing uninitialized value means that we are preparing for a computed
1012 // property value in an object literal. The initializing store will follow
1013 // and it will properly update constness based on the actual value.
1014 return true;
1015 }
1017 Tagged<Object> current_value;
1019 Tagged<SwissNameDictionary> dict = holder->property_dictionary_swiss();
1020 current_value = dict->ValueAt(dictionary_entry());
1021 } else {
1022 Tagged<NameDictionary> dict = holder->property_dictionary();
1023 current_value = dict->ValueAt(dictionary_entry());
1024 }
1025
1026 return IsUninitialized(current_value, isolate());
1027}
1028
1031 DCHECK(holder_->HasFastProperties());
1034 // TODO(jkummerow): Propagate InternalIndex further.
1035 return descriptor_number().as_int();
1036}
1037
1045
1054
1063
1068
1070 AllocationPolicy allocation_policy) const {
1072 return indirect_handle(FetchValue(allocation_policy), isolate_);
1073}
1074
1077 // Currently only shared structs and arrays support sequentially consistent
1078 // access.
1079 DCHECK(IsJSSharedStruct(*holder_, isolate_) ||
1080 IsJSSharedArray(*holder_, isolate_));
1082 if (IsElement(*holder)) {
1083 ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
1084 return accessor->GetAtomic(isolate_, holder, number_, kSeqCstAccess);
1085 }
1088 FieldIndex field_index =
1091 isolate_, holder, property_details_.representation(), field_index, tag);
1092}
1093
1095 bool initializing_store) {
1097 // WriteDataValueToWasmObject() must be used instead for writing to
1098 // WasmObjects.
1100 DCHECK_IMPLIES(IsJSSharedStruct(*holder_), IsShared(*value));
1101
1103 if (IsElement(*holder)) {
1104 DirectHandle<JSObject> object = Cast<JSObject>(holder);
1105 ElementsAccessor* accessor = object->GetElementsAccessor(isolate_);
1106 accessor->Set(object, number_, *value);
1107 } else if (holder->HasFastProperties(isolate_)) {
1108 DCHECK(IsJSObject(*holder, isolate_));
1110 // Check that in case of VariableMode::kConst field the existing value is
1111 // equal to |value|.
1112 DCHECK_IMPLIES(!initializing_store && property_details_.constness() ==
1114 CanStayConst(*value));
1115 Cast<JSObject>(*holder)->WriteToField(descriptor_number(),
1116 property_details_, *value);
1117 } else {
1120 }
1121 } else if (IsJSGlobalObject(*holder, isolate_)) {
1122 // PropertyCell::PrepareForAndSetValue already wrote the value into the
1123 // cell.
1124#ifdef DEBUG
1125 Tagged<GlobalDictionary> dictionary =
1126 Cast<JSGlobalObject>(*holder)->global_dictionary(isolate_,
1127 kAcquireLoad);
1129 dictionary->CellAt(isolate_, dictionary_entry());
1130 DCHECK(cell->value() == *value ||
1131 (IsString(cell->value()) && IsString(*value) &&
1132 Cast<String>(cell->value())->Equals(Cast<String>(*value))));
1133#endif // DEBUG
1134 } else {
1135 DCHECK_IMPLIES(IsJSProxy(*holder, isolate_), name()->IsPrivate());
1136 // Check similar to fast mode case above.
1138 V8_DICT_PROPERTY_CONST_TRACKING_BOOL && !initializing_store &&
1140 IsJSProxy(*holder, isolate_) || DictCanStayConst(*value));
1141
1143 Tagged<SwissNameDictionary> dictionary =
1144 holder->property_dictionary_swiss(isolate_);
1145 dictionary->ValueAtPut(dictionary_entry(), *value);
1146 } else {
1147 Tagged<NameDictionary> dictionary = holder->property_dictionary(isolate_);
1148 dictionary->ValueAtPut(dictionary_entry(), *value);
1149 }
1150 }
1151}
1152
1154 SeqCstAccessTag tag) {
1156 // Currently only shared structs and arrays support sequentially consistent
1157 // access.
1158 DCHECK(IsJSSharedStruct(*holder_, isolate_) ||
1159 IsJSSharedArray(*holder_, isolate_));
1161 if (IsElement(*holder)) {
1162 ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
1163 accessor->SetAtomic(holder, number_, *value, kSeqCstAccess);
1164 return;
1165 }
1169 FieldIndex field_index =
1171 holder->FastPropertyAtPut(field_index, *value, tag);
1172}
1173
1175 SeqCstAccessTag tag) {
1177 // Currently only shared structs and arrays support sequentially consistent
1178 // access.
1179 DCHECK(IsJSSharedStruct(*holder_, isolate_) ||
1180 IsJSSharedArray(*holder_, isolate_));
1182 if (IsElement(*holder)) {
1183 ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
1184 return accessor->SwapAtomic(isolate_, holder, number_, *value,
1186 }
1190 FieldIndex field_index =
1192 return direct_handle(holder->RawFastPropertyAtSwap(field_index, *value, tag),
1193 isolate_);
1194}
1195
1198 SeqCstAccessTag tag) {
1200 // Currently only shared structs and arrays support sequentially consistent
1201 // access.
1202 DCHECK(IsJSSharedStruct(*holder_, isolate_) ||
1203 IsJSSharedArray(*holder_, isolate_));
1206 if (IsElement(*holder)) {
1207 ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
1208 return accessor->CompareAndSwapAtomic(isolate_, holder, number_, *expected,
1209 *value, kSeqCstAccess);
1210 }
1213 FieldIndex field_index =
1215 return direct_handle(holder->RawFastPropertyAtCompareAndSwap(
1216 field_index, *expected, *value, tag),
1217 isolate_);
1218}
1219
1220template <bool is_element>
1223 if (!is_element && IsSymbol(*name_, isolate_) &&
1224 !info->can_intercept_symbols()) {
1225 return true;
1226 }
1227 if (info->non_masking()) {
1228 switch (interceptor_state_) {
1231 [[fallthrough]];
1233 return true;
1235 return false;
1236 }
1237 }
1239}
1240
1243 if (map->prototype(isolate_) == ReadOnlyRoots(isolate_).null_value()) {
1244 return JSReceiver();
1245 }
1246 if (!check_prototype_chain() && !IsJSGlobalProxyMap(map)) {
1247 return JSReceiver();
1248 }
1249 return Cast<JSReceiver>(map->prototype(isolate_));
1250}
1251
1253 Tagged<JSReceiver> const holder) const {
1254 if (!IsJSTypedArray(holder, isolate_)) return NOT_FOUND;
1256 if (!IsString(*name_, isolate_)) return NOT_FOUND;
1258 : NOT_FOUND;
1259}
1260
1261namespace {
1262
1263template <bool is_element>
1264bool HasInterceptor(Tagged<Map> map, size_t index) {
1265 if (is_element) {
1266 if (index > JSObject::kMaxElementIndex) {
1267 // There is currently no way to install interceptors on an object with
1268 // typed array elements.
1269 DCHECK(!map->has_typed_array_or_rab_gsab_typed_array_elements());
1270 return map->has_named_interceptor();
1271 }
1272 return map->has_indexed_interceptor();
1273 } else {
1274 return map->has_named_interceptor();
1275 }
1276}
1277
1278} // namespace
1279
1280template <bool is_element>
1282 Tagged<Map> const map, Tagged<JSReceiver> const holder) {
1283 static_assert(INTERCEPTOR == BEFORE_PROPERTY);
1284 switch (state_) {
1285 case NOT_FOUND:
1286 if (IsJSProxyMap(map)) {
1287 if (is_element || !name_->IsPrivate()) return JSPROXY;
1288 }
1289#if V8_ENABLE_WEBASSEMBLY
1290 if (IsWasmObjectMap(map)) return WASM_OBJECT;
1291#endif // V8_ENABLE_WEBASSEMBLY
1292 if (map->is_access_check_needed()) {
1293 if (is_element || !name_->IsPrivate() || name_->IsPrivateName())
1294 return ACCESS_CHECK;
1295 }
1296 [[fallthrough]];
1297 case ACCESS_CHECK:
1298 if (check_interceptor() && HasInterceptor<is_element>(map, index_) &&
1300 if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
1301 }
1302 [[fallthrough]];
1303 case INTERCEPTOR:
1304 if (IsJSGlobalObjectMap(map) && !is_js_array_element(is_element)) {
1306 Cast<JSGlobalObject>(holder)->global_dictionary(isolate_,
1307 kAcquireLoad);
1308 number_ = dict->FindEntry(isolate(), name_);
1309 if (number_.is_not_found()) return NOT_FOUND;
1310 Tagged<PropertyCell> cell = dict->CellAt(isolate_, number_);
1311 if (IsPropertyCellHole(cell->value(isolate_), isolate_)) {
1312 return NOT_FOUND;
1313 }
1314 property_details_ = cell->property_details();
1315 has_property_ = true;
1316 switch (property_details_.kind()) {
1318 return DATA;
1320 return ACCESSOR;
1321 }
1322 }
1323 return LookupInRegularHolder<is_element>(map, holder);
1324 case ACCESSOR:
1325 case DATA:
1326 case WASM_OBJECT:
1327 return NOT_FOUND;
1329 case JSPROXY:
1330 case TRANSITION:
1331 UNREACHABLE();
1332 }
1333 UNREACHABLE();
1334}
1335
1336template <bool is_element>
1338 Tagged<Map> const map, Tagged<JSReceiver> const holder) {
1341 return NOT_FOUND;
1342 }
1343 DCHECK(!IsWasmObject(holder, isolate_));
1344 if (is_element && IsElement(holder)) {
1345 Tagged<JSObject> js_object = Cast<JSObject>(holder);
1346 ElementsAccessor* accessor = js_object->GetElementsAccessor(isolate_);
1347 Tagged<FixedArrayBase> backing_store = js_object->elements(isolate_);
1348 number_ =
1349 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
1350 if (number_.is_not_found()) {
1351 return IsJSTypedArray(holder, isolate_) ? TYPED_ARRAY_INDEX_NOT_FOUND
1352 : NOT_FOUND;
1353 }
1354 property_details_ = accessor->GetDetails(js_object, number_);
1355 if (map->has_frozen_elements()) {
1357 } else if (map->has_sealed_elements()) {
1359 }
1360 } else if (!map->is_dictionary_map()) {
1361 Tagged<DescriptorArray> descriptors = map->instance_descriptors(isolate_);
1362 number_ = descriptors->SearchWithCache(isolate_, *name_, map);
1363 if (number_.is_not_found()) return NotFound(holder);
1364 property_details_ = descriptors->GetDetails(number_);
1365 } else {
1366 DCHECK_IMPLIES(IsJSProxy(holder, isolate_), name()->IsPrivate());
1369 holder->property_dictionary_swiss(isolate_);
1370 number_ = dict->FindEntry(isolate(), *name_);
1371 if (number_.is_not_found()) return NotFound(holder);
1372 property_details_ = dict->DetailsAt(number_);
1373 } else {
1374 Tagged<NameDictionary> dict = holder->property_dictionary(isolate_);
1375 number_ = dict->FindEntry(isolate(), name_);
1376 if (number_.is_not_found()) return NotFound(holder);
1377 property_details_ = dict->DetailsAt(number_);
1378 }
1379 }
1380 has_property_ = true;
1381 switch (property_details_.kind()) {
1383 return DATA;
1385 return ACCESSOR;
1386 }
1387
1388 UNREACHABLE();
1389}
1390
1391// This is a specialization of function LookupInRegularHolder above
1392// which is tailored to test whether an object has an internal marker
1393// property.
1394// static
1396 Isolate* isolate, Tagged<JSReceiver> const holder,
1397 DirectHandle<Symbol> const marker) {
1399 Tagged<Map> map = holder->map(isolate);
1400 if (map->is_dictionary_map()) {
1403 holder->property_dictionary_swiss(isolate);
1404 InternalIndex entry = dict->FindEntry(isolate, marker);
1405 return entry.is_found();
1406 } else {
1407 Tagged<NameDictionary> dict = holder->property_dictionary(isolate);
1408 InternalIndex entry = dict->FindEntry(isolate, marker);
1409 return entry.is_found();
1410 }
1411 } else {
1412 Tagged<DescriptorArray> descriptors = map->instance_descriptors(isolate);
1413 InternalIndex entry = descriptors->SearchWithCache(isolate, *marker, map);
1414 return entry.is_found();
1415 }
1416}
1417
1421 // Skip the interceptors for private
1422 if (IsPrivateName()) {
1424 }
1425
1427 Tagged<AccessCheckInfo> access_check_info =
1429 if (!access_check_info.is_null()) {
1430 // There is currently no way to create objects with typed array elements
1431 // and access checks.
1432 DCHECK(!holder_->map()->has_typed_array_or_rab_gsab_typed_array_elements());
1434 ? access_check_info->indexed_interceptor()
1435 : access_check_info->named_interceptor();
1436 if (interceptor != Tagged<Object>()) {
1437 return direct_handle(Cast<InterceptorInfo>(interceptor), isolate_);
1438 }
1439 }
1441}
1442
1448
1450 if (state() != LookupIterator::ACCESSOR) return false;
1451
1452 DirectHandle<Object> accessor_pair = GetAccessors();
1453 return IsAccessorPair(*accessor_pair, isolate_) &&
1455}
1456
1458 DirectHandle<AccessorPair> accessor_pair) {
1459 if (!HolderIsReceiverOrHiddenPrototype()) return false;
1460 if (!lookup_start_object_.is_identical_to(receiver_) &&
1461 !lookup_start_object_.is_identical_to(holder_)) {
1462 return false;
1463 }
1464
1466 DCHECK(IsAccessorPair(*GetAccessors(), isolate_));
1467
1468 Tagged<Object> getter = accessor_pair->getter();
1469 std::optional<Tagged<Name>> maybe_name =
1471 if (!maybe_name.has_value()) return false;
1472
1473 if (IsJSFunction(getter)) {
1474 // If the getter was a JSFunction there's no guarantee that the holder
1475 // actually has a property with the cached name. In that case look it up to
1476 // make sure.
1478 direct_handle(maybe_name.value(), isolate_));
1479 if (it.state() != DATA) return false;
1480 name_ = it.name();
1481 } else {
1482 name_ = direct_handle(maybe_name.value(), isolate_);
1483 }
1484
1485 // We have found a cached property! Modify the iterator accordingly.
1486 Restart();
1488 return true;
1489}
1490
1491// static
1493 Isolate* isolate, Tagged<FixedArray> array_elements,
1494 ElementsKind elements_kind, int array_length, size_t index) {
1496
1497 CHECK_EQ(array_elements->map(), ReadOnlyRoots(isolate).fixed_cow_array_map());
1498 DCHECK(IsFastElementsKind(elements_kind) &&
1499 IsSmiOrObjectElementsKind(elements_kind));
1500 USE(elements_kind);
1501 DCHECK_GE(array_length, 0);
1502
1503 // ________________________________________
1504 // ( Check against both JSArray::length and )
1505 // ( FixedArray::length. )
1506 // ----------------------------------------
1507 // o ^__^
1508 // o (oo)\_______
1509 // (__)\ )\/\
1510 // ||----w |
1511 // || ||
1512 // The former is the source of truth, but due to concurrent reads it may not
1513 // match the given `array_elements`.
1514 if (index >= static_cast<size_t>(array_length)) return {};
1515 if (index >= static_cast<size_t>(array_elements->length())) return {};
1516
1517 Tagged<Object> result = array_elements->get(static_cast<int>(index));
1518
1519 // ______________________________________
1520 // ( Filter out holes irrespective of the )
1521 // ( elements kind. )
1522 // --------------------------------------
1523 // o ^__^
1524 // o (..)\_______
1525 // (__)\ )\/\
1526 // ||----w |
1527 // || ||
1528 // The elements kind may not be consistent with the given elements backing
1529 // store.
1530 if (result == ReadOnlyRoots(isolate).the_hole_value()) return {};
1531
1532 return result;
1533}
1534
1535// static
1538 Tagged<Object>* result_out, Isolate* isolate, LocalIsolate* local_isolate,
1540 ElementsKind elements_kind, size_t index) {
1542
1544
1545 // Own 'constant' elements (PropertyAttributes READ_ONLY|DONT_DELETE) occur in
1546 // three main cases:
1547 //
1548 // 1. Frozen elements: guaranteed constant.
1549 // 2. Dictionary elements: may be constant.
1550 // 3. String wrapper elements: guaranteed constant.
1551
1552 // Interesting field reads below:
1553 //
1554 // - elements.length (immutable on FixedArrays).
1555 // - elements[i] (immutable if constant; be careful around dictionaries).
1556 // - holder.AsJSPrimitiveWrapper.value.AsString.length (immutable).
1557 // - holder.AsJSPrimitiveWrapper.value.AsString[i] (immutable).
1558 // - single_character_string_table()->get().
1559
1560 if (IsFrozenElementsKind(elements_kind)) {
1561 if (!IsFixedArray(elements)) return kGaveUp;
1562 Tagged<FixedArray> elements_fixed_array = Cast<FixedArray>(elements);
1563 if (index >= static_cast<uint32_t>(elements_fixed_array->length())) {
1564 return kGaveUp;
1565 }
1566 Tagged<Object> result = elements_fixed_array->get(static_cast<int>(index));
1567 if (IsHoleyElementsKindForRead(elements_kind) &&
1568 result == ReadOnlyRoots(isolate).the_hole_value()) {
1569 return kNotPresent;
1570 }
1571 *result_out = result;
1572 return kPresent;
1573 } else if (IsDictionaryElementsKind(elements_kind)) {
1574 if (!IsNumberDictionary(elements)) return kGaveUp;
1575 // TODO(jgruber, v8:7790): Add support. Dictionary elements require racy
1576 // NumberDictionary lookups. This should be okay in general (slot iteration
1577 // depends only on the dict's capacity), but 1. we'd need to update
1578 // NumberDictionary methods to do atomic reads, and 2. the dictionary
1579 // elements case isn't very important for callers of this function.
1580 return kGaveUp;
1581 } else if (IsStringWrapperElementsKind(elements_kind)) {
1582 // In this case we don't care about the actual `elements`. All in-bounds
1583 // reads are redirected to the wrapped String.
1584
1586 Tagged<String> wrapped_string = Cast<String>(js_value->value());
1588 reinterpret_cast<Tagged<String>*>(result_out), isolate, local_isolate,
1589 wrapped_string, index);
1590 } else {
1591 DCHECK(!IsFrozenElementsKind(elements_kind));
1592 DCHECK(!IsDictionaryElementsKind(elements_kind));
1593 DCHECK(!IsStringWrapperElementsKind(elements_kind));
1594 return kGaveUp;
1595 }
1596
1597 UNREACHABLE();
1598}
1599
1600// static
1602 Tagged<String>* result_out, Isolate* isolate, LocalIsolate* local_isolate,
1603 Tagged<String> string, size_t index) {
1605 // The access guard below protects string accesses related to internalized
1606 // strings.
1607 // TODO(jgruber): Support other string kinds.
1608 Tagged<Map> string_map = string->map(kAcquireLoad);
1609 InstanceType type = string_map->instance_type();
1612 return kGaveUp;
1613 }
1614
1615 const uint32_t length = static_cast<uint32_t>(string->length());
1616 if (index >= length) return kGaveUp;
1617
1618 uint16_t charcode;
1619 {
1620 SharedStringAccessGuardIfNeeded access_guard(local_isolate);
1621 charcode = string->Get(static_cast<int>(index), access_guard);
1622 }
1623
1624 if (charcode > unibrow::Latin1::kMaxChar) return kGaveUp;
1625
1626 Tagged<Object> value =
1627 isolate->factory()->single_character_string_table()->get(charcode,
1628 kRelaxedLoad);
1629
1630 DCHECK_NE(value, ReadOnlyRoots(isolate).undefined_value());
1631
1632 *result_out = Cast<String>(value);
1633 return kPresent;
1634}
1635
1636// static
1637std::optional<Tagged<PropertyCell>>
1639 Isolate* isolate, LocalIsolate* local_isolate,
1642
1643 Tagged<Map> holder_map = holder->map();
1644 if (holder_map->is_access_check_needed()) return {};
1645 if (holder_map->has_named_interceptor()) return {};
1646
1647 Tagged<GlobalDictionary> dict = holder->global_dictionary(kAcquireLoad);
1648 std::optional<Tagged<PropertyCell>> maybe_cell =
1649 dict->TryFindPropertyCellForConcurrentLookupIterator(isolate, name,
1650 kRelaxedLoad);
1651 if (!maybe_cell.has_value()) return {};
1652 Tagged<PropertyCell> cell = maybe_cell.value();
1653
1654 if (cell->property_details(kAcquireLoad).kind() == PropertyKind::kAccessor) {
1655 Tagged<Object> maybe_accessor_pair = cell->value(kAcquireLoad);
1656 if (!IsAccessorPair(maybe_accessor_pair)) return {};
1657
1658 std::optional<Tagged<Name>> maybe_cached_property_name =
1660 isolate,
1661 Cast<AccessorPair>(maybe_accessor_pair)->getter(kAcquireLoad));
1662 if (!maybe_cached_property_name.has_value()) return {};
1663
1664 maybe_cell = dict->TryFindPropertyCellForConcurrentLookupIterator(
1665 isolate, direct_handle(*maybe_cached_property_name, local_isolate),
1666 kRelaxedLoad);
1667 if (!maybe_cell.has_value()) return {};
1668 cell = maybe_cell.value();
1669 if (cell->property_details(kAcquireLoad).kind() != PropertyKind::kData)
1670 return {};
1671 }
1672
1673 DCHECK(maybe_cell.has_value());
1674 DCHECK_EQ(cell->property_details(kAcquireLoad).kind(), PropertyKind::kData);
1675 return cell;
1676}
1677
1678} // namespace v8::internal
Builtins::Kind kind
Definition builtins.cc:40
PropertyT * getter
static const uint16_t kMaxChar
Definition unicode.h:142
@ kArrayInstanceConstructorModified
Definition v8-isolate.h:497
@ kArraySpeciesModified
Definition v8-isolate.h:494
@ kArrayPrototypeConstructorModified
Definition v8-isolate.h:495
static Tagged< AccessCheckInfo > Get(Isolate *isolate, DirectHandle< JSObject > receiver)
Definition objects.cc:6594
static NEVER_READ_ONLY_SPACE DirectHandle< AccessorPair > Copy(Isolate *isolate, DirectHandle< AccessorPair > pair)
Definition objects.cc:4004
static V8_EXPORT_PRIVATE std::optional< Tagged< Object > > TryGetOwnCowElement(Isolate *isolate, Tagged< FixedArray > array_elements, ElementsKind elements_kind, int array_length, size_t index)
Definition lookup.cc:1492
static V8_EXPORT_PRIVATE Result TryGetOwnChar(Tagged< String > *result_out, Isolate *isolate, LocalIsolate *local_isolate, Tagged< String > string, size_t index)
Definition lookup.cc:1601
static V8_EXPORT_PRIVATE Result TryGetOwnConstantElement(Tagged< Object > *result_out, Isolate *isolate, LocalIsolate *local_isolate, Tagged< JSObject > holder, Tagged< FixedArrayBase > elements, ElementsKind elements_kind, size_t index)
Definition lookup.cc:1537
static V8_EXPORT_PRIVATE std::optional< Tagged< PropertyCell > > TryGetPropertyCell(Isolate *isolate, LocalIsolate *local_isolate, DirectHandle< JSGlobalObject > holder, DirectHandle< Name > name)
Definition lookup.cc:1638
virtual Handle< Object > Get(Isolate *isolate, DirectHandle< JSObject > holder, InternalIndex entry)=0
virtual Handle< Object > CompareAndSwapAtomic(Isolate *isolate, DirectHandle< JSObject > holder, InternalIndex entry, Tagged< Object > expected, Tagged< Object > value, SeqCstAccessTag tag)=0
virtual InternalIndex GetEntryForIndex(Isolate *isolate, Tagged< JSObject > holder, Tagged< FixedArrayBase > backing_store, size_t index)=0
virtual Handle< Object > SwapAtomic(Isolate *isolate, DirectHandle< JSObject > holder, InternalIndex entry, Tagged< Object > value, SeqCstAccessTag tag)=0
virtual void Delete(DirectHandle< JSObject > holder, InternalIndex entry)=0
virtual Handle< Object > GetAtomic(Isolate *isolate, DirectHandle< JSObject > holder, InternalIndex entry, SeqCstAccessTag tag)=0
virtual PropertyDetails GetDetails(Tagged< JSObject > holder, InternalIndex entry)=0
virtual void SetAtomic(DirectHandle< JSObject > holder, InternalIndex entry, Tagged< Object > value, SeqCstAccessTag tag)=0
virtual void Set(DirectHandle< JSObject > holder, InternalIndex entry, Tagged< Object > value)=0
Handle< AccessorPair > NewAccessorPair()
Handle< PropertyCell > NewPropertyCell(DirectHandle< Name > name, PropertyDetails details, DirectHandle< Object > value, AllocationType allocation=AllocationType::kOld)
Definition factory.cc:2216
static FieldIndex ForDetails(Tagged< Map > map, PropertyDetails details)
static FieldIndex ForDescriptor(Tagged< Map > map, InternalIndex descriptor_index)
static std::optional< Tagged< Name > > TryGetCachedPropertyName(Isolate *isolate, Tagged< Object > getter)
Definition templates.cc:153
V8_INLINE bool is_identical_to(const HandleBase &that) const
Definition handles-inl.h:36
static InternalIndex NotFound()
constexpr int as_int() const
void CountUsage(v8::Isolate::UseCounterFeature feature)
Definition isolate.cc:7028
Handle< NativeContext > native_context()
Definition isolate-inl.h:48
v8::internal::Factory * factory()
Definition isolate.h:1527
bool MayAccess(DirectHandle< NativeContext > accessing_context, DirectHandle< JSObject > receiver)
Definition isolate.cc:1815
static V8_EXPORT_PRIVATE DirectHandle< NumberDictionary > NormalizeElements(DirectHandle< JSObject > object)
static Handle< JSAny > FastPropertyAt(Isolate *isolate, DirectHandle< JSObject > object, Representation representation, FieldIndex index)
static Tagged< Map > InvalidatePrototypeChains(Tagged< Map > map)
static constexpr uint32_t kMaxElementIndex
Definition js-objects.h:924
static void SetNormalizedProperty(DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyDetails details)
static V8_EXPORT_PRIVATE void NormalizeProperties(Isolate *isolate, DirectHandle< JSObject > object, PropertyNormalizationMode mode, int expected_additional_properties, bool use_cache, const char *reason)
static V8_EXPORT_PRIVATE void TransitionElementsKind(DirectHandle< JSObject > object, ElementsKind to_kind)
static V8_EXPORT_PRIVATE void MigrateToMap(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Map > new_map, int expected_additional_properties=0)
static void ReoptimizeIfPrototype(DirectHandle< JSObject > object)
static void EnsureWritableFastElements(DirectHandle< JSObject > object)
static void DeleteNormalizedProperty(DirectHandle< JSReceiver > object, InternalIndex entry)
bool is_js_array_element(bool is_element) const
Definition lookup.h:307
const Configuration configuration_
Definition lookup.h:365
void WriteDataValue(DirectHandle< Object > value, bool initializing_store)
Definition lookup.cc:1094
PropertyDetails property_details() const
Definition lookup.h:229
static bool HasInternalMarkerProperty(Isolate *isolate, Tagged< JSReceiver > object, DirectHandle< Symbol > marker)
Definition lookup.cc:1395
DirectHandle< UnionOf< Map, PropertyCell > > transition_
Definition lookup.h:372
DirectHandle< Object > FetchValue(AllocationPolicy allocation_policy=AllocationPolicy::kAllocationAllowed) const
Definition lookup.cc:925
uint32_t array_index() const
Definition lookup.h:161
bool check_interceptor() const
Definition lookup.h:344
int GetFieldDescriptorIndex() const
Definition lookup.cc:1029
bool CanStayConst(Tagged< Object > value) const
Definition lookup.cc:964
DirectHandle< InterceptorInfo > GetInterceptor() const
Definition lookup-inl.h:417
V8_WARN_UNUSED_RESULT Tagged< JSReceiver > NextHolder(Tagged< Map > map)
Definition lookup.cc:1241
Isolate * isolate() const
Definition lookup.h:155
void RestartLookupForNonMaskingInterceptors()
Definition lookup.h:325
FieldIndex GetFieldIndex() const
Definition lookup.cc:1046
InternalIndex dictionary_entry() const
Definition lookup-inl.h:368
InterceptorState interceptor_state_
Definition lookup.h:368
DirectHandle< T > GetStoreTarget() const
Definition lookup-inl.h:395
DirectHandle< Object > GetAccessors() const
Definition lookup.cc:1064
DirectHandle< Name > name_
Definition lookup.h:371
DirectHandle< JSReceiver > holder_
Definition lookup.h:374
Isolate *const isolate_
Definition lookup.h:370
DirectHandle< Object > CompareAndSwapDataValue(DirectHandle< Object > expected, DirectHandle< Object > value, SeqCstAccessTag tag)
Definition lookup.cc:1196
DirectHandle< InterceptorInfo > GetInterceptorForFailedAccessCheck() const
Definition lookup.cc:1419
static void InternalUpdateProtector(Isolate *isolate, DirectHandle< JSAny > receiver, DirectHandle< Name > name)
Definition lookup.cc:200
bool SkipInterceptor(Tagged< JSObject > holder)
Definition lookup.cc:1221
State LookupInSpecialHolder(Tagged< Map > map, Tagged< JSReceiver > holder)
Definition lookup.cc:1281
bool LookupCachedProperty(DirectHandle< AccessorPair > accessor)
Definition lookup.cc:1457
bool DictCanStayConst(Tagged< Object > value) const
Definition lookup.cc:1000
static MaybeDirectHandle< JSReceiver > GetRootForNonJSReceiver(Isolate *isolate, DirectHandle< JSPrimitive > lookup_start_object, size_t index, Configuration configuration)
Definition lookup.cc:146
DirectHandle< PropertyCell > transition_cell() const
Definition lookup-inl.h:277
void PrepareTransitionToDataProperty(DirectHandle< JSReceiver > receiver, DirectHandle< Object > value, PropertyAttributes attributes, StoreOrigin store_origin)
Definition lookup.cc:613
const DirectHandle< JSAny > receiver_
Definition lookup.h:373
PropertyDetails property_details_
Definition lookup.h:369
void PrepareForDataProperty(DirectHandle< Object > value)
Definition lookup.cc:383
void NextInternal(Tagged< Map > map, Tagged< JSReceiver > holder)
Definition lookup.cc:84
DirectHandle< T > GetHolder() const
Definition lookup-inl.h:283
Handle< Object > GetDataValue(AllocationPolicy allocation_policy=AllocationPolicy::kAllocationAllowed) const
Definition lookup.cc:1069
State LookupInHolder(Tagged< Map > map, Tagged< JSReceiver > holder)
Definition lookup.h:315
DirectHandle< JSAny > lookup_start_object() const
Definition lookup.h:198
V8_EXPORT_PRIVATE void Start()
DirectHandle< Object > SwapDataValue(DirectHandle< Object > value, SeqCstAccessTag tag)
Definition lookup.cc:1174
State LookupInRegularHolder(Tagged< Map > map, Tagged< JSReceiver > holder)
Definition lookup.cc:1337
bool HolderIsReceiver() const
Definition lookup.cc:908
void ReconfigureDataProperty(DirectHandle< Object > value, PropertyAttributes attributes)
Definition lookup.cc:509
static MaybeDirectHandle< JSReceiver > GetRoot(Isolate *isolate, DirectHandle< JSAny > lookup_start_object, size_t index, Configuration configuration)
Definition lookup-inl.h:384
Factory * factory() const
Definition lookup.h:187
InternalIndex descriptor_number() const
Definition lookup-inl.h:360
void ApplyTransitionToDataProperty(DirectHandle< JSReceiver > receiver)
Definition lookup.cc:673
bool HolderIsReceiverOrHiddenPrototype() const
Definition lookup.cc:915
void TransitionToAccessorPair(DirectHandle< Object > pair, PropertyAttributes attributes)
Definition lookup.cc:858
DirectHandle< Map > GetReceiverMap() const
Definition lookup.cc:180
DirectHandle< Name > name() const
Definition lookup-inl.h:241
Representation representation() const
Definition lookup.h:239
void RestartInternal(InterceptorState interceptor_state)
Definition lookup.cc:105
PropertyConstness constness() const
Definition lookup.h:243
const DirectHandle< JSAny > lookup_start_object_
Definition lookup.h:375
DirectHandle< Map > transition_map() const
Definition lookup-inl.h:272
bool check_prototype_chain() const
Definition lookup.h:205
DirectHandle< PropertyCell > GetPropertyCell() const
Definition lookup.cc:1055
void TransitionToAccessorProperty(DirectHandle< Object > getter, DirectHandle< Object > setter, PropertyAttributes attributes)
Definition lookup.cc:789
static Handle< Map > ReconfigureExistingProperty(Isolate *isolate, DirectHandle< Map > map, InternalIndex descriptor, PropertyKind kind, PropertyAttributes attributes, PropertyConstness constness)
static Handle< UnionOf< Smi, Cell > > GetOrCreatePrototypeChainValidityCell(DirectHandle< Map > map, Isolate *isolate)
Definition map.cc:2419
static V8_EXPORT_PRIVATE DirectHandle< Map > TransitionToDataProperty(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes, PropertyConstness constness, StoreOrigin store_origin)
Definition map.cc:1987
static V8_EXPORT_PRIVATE DirectHandle< Map > PrepareForDataProperty(Isolate *isolate, DirectHandle< Map > old_map, InternalIndex descriptor_number, PropertyConstness constness, DirectHandle< Object > value)
Definition map.cc:1975
static V8_EXPORT_PRIVATE DirectHandle< Map > Update(Isolate *isolate, DirectHandle< Map > map)
Definition map.cc:839
static V8_EXPORT_PRIVATE DirectHandle< Map > TransitionToAccessorProperty(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Name > name, InternalIndex descriptor, DirectHandle< Object > getter, DirectHandle< Object > setter, PropertyAttributes attributes)
Definition map.cc:2072
V8_WARN_UNUSED_RESULT V8_INLINE bool ToHandle(DirectHandle< S > *out) const
static V8_WARN_UNUSED_RESULT HandleType< NumberDictionary > Set(Isolate *isolate, HandleType< NumberDictionary > dictionary, uint32_t key, DirectHandle< Object > value, DirectHandle< JSObject > dictionary_holder=DirectHandle< JSObject >::null(), PropertyDetails details=PropertyDetails::Empty())
static Tagged< Map > GetPrototypeChainRootMap(Tagged< Object > obj, Isolate *isolate)
Definition objects.cc:1683
static ElementsKind OptimalElementsKind(Tagged< Object > obj, PtrComprCageBase cage_base)
static PropertyCellType InitialType(Isolate *isolate, Tagged< Object > value)
Definition objects.cc:6440
static Handle< PropertyCell > PrepareForAndSetValue(Isolate *isolate, DirectHandle< GlobalDictionary > dictionary, InternalIndex entry, DirectHandle< Object > value, PropertyDetails details)
Definition objects.cc:6473
PropertyAttributes attributes() const
PropertyDetails CopyWithConstness(PropertyConstness constness) const
PropertyLocation location() const
Representation representation() const
static constexpr PropertyConstness kConstIfDictConstnessTracking
PropertyDetails set_index(int index) const
static constexpr PropertyDetails Empty(PropertyCellType cell_type=PropertyCellType::kNoCell)
PropertyConstness constness() const
PropertyDetails CopyAddAttributes(PropertyAttributes new_attributes) const
Tagged< Smi > AsSmi() const
Definition objects-inl.h:79
constexpr bool IsDouble() const
static HandleType< SwissNameDictionary > Add(IsolateT *isolate, HandleType< SwissNameDictionary > table, DirectHandle< Name > key, DirectHandle< Object > value, PropertyDetails details, InternalIndex *entry_out=nullptr)
V8_INLINE constexpr bool is_null() const
Definition tagged.h:502
#define V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
Definition globals.h:242
#define V8_DICT_PROPERTY_CONST_TRACKING_BOOL
Definition globals.h:249
TNode< Object > receiver
std::map< const std::string, const std::string > map
ZoneVector< RpoNumber > & result
V8_INLINE constexpr bool IsThinString(InstanceType instance_type)
V8_INLINE constexpr bool IsInternalizedString(InstanceType instance_type)
bool IsNone(Tagged< FieldType > obj)
Definition field-type.h:50
constexpr bool IsHoleyElementsKind(ElementsKind kind)
bool IsNumber(Tagged< Object > obj)
Map::Bits1::HasPrototypeSlotBit Map::Bits1::HasNamedInterceptorBit Map::Bits1::IsUndetectableBit Map::Bits1::IsConstructorBit Map::Bits2::IsImmutablePrototypeBit Map::Bits3::IsDeprecatedBit is_prototype_map
Definition map-inl.h:133
bool IsSealedElementsKind(ElementsKind kind)
bool IsSpecialIndex(Tagged< String > string)
V8_INLINE IndirectHandle< T > indirect_handle(DirectHandle< T > handle)
Definition handles.h:757
bool IsSpecialReceiverMap(Tagged< Map > map)
constexpr uint64_t kHoleNanInt64
Definition globals.h:1960
bool IsNonextensibleElementsKind(ElementsKind kind)
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
bool IsHoleyElementsKindForRead(ElementsKind kind)
bool IsSmiOrObjectElementsKind(ElementsKind kind)
ElementsKind GetHoleyElementsKind(ElementsKind packed_kind)
bool IsShared(Tagged< Object > obj)
bool IsFrozenElementsKind(ElementsKind kind)
bool IsFastElementsKind(ElementsKind kind)
bool IsDictionaryElementsKind(ElementsKind kind)
V8_EXPORT_PRIVATE FlagValues v8_flags
V8_INLINE bool IsWasmObject(T obj, Isolate *=nullptr)
Definition objects.h:725
bool IsStringWrapperElementsKind(ElementsKind kind)
ElementsKind GetMoreGeneralElementsKind(ElementsKind from_kind, ElementsKind to_kind)
kInstanceDescriptorsOffset kTransitionsOrPrototypeInfoOffset IsNull(value)||IsJSProxy(value)||IsWasmObject(value)||(IsJSObject(value) &&(HeapLayout
Definition map-inl.h:70
kInstanceDescriptorsOffset kTransitionsOrPrototypeInfoOffset prototype
Definition map-inl.h:69
PropertyNormalizationMode
Definition objects.h:60
@ KEEP_INOBJECT_PROPERTIES
Definition objects.h:62
@ CLEAR_INOBJECT_PROPERTIES
Definition objects.h:61
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
static constexpr ReleaseStoreTag kReleaseStore
Definition globals.h:2910
static constexpr SeqCstAccessTag kSeqCstAccess
Definition globals.h:2912
static constexpr RelaxedLoadTag kRelaxedLoad
Definition globals.h:2909
static constexpr RelaxedStoreTag kRelaxedStore
Definition globals.h:2911
static constexpr AcquireLoadTag kAcquireLoad
Definition globals.h:2908
#define RCS_SCOPE(...)
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define USE(...)
Definition macros.h:293
#define V8_UNLIKELY(condition)
Definition v8config.h:660