v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
transitions-inl.h
Go to the documentation of this file.
1// Copyright 2012 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#ifndef V8_OBJECTS_TRANSITIONS_INL_H_
6#define V8_OBJECTS_TRANSITIONS_INL_H_
7
9// Include the non-inl header before the rest of the headers.
10
11#include <ranges>
12#include <type_traits>
13
16#include "src/objects/slots.h"
17#include "src/objects/smi.h"
18
19// Has to be the last include (doesn't have include guards):
21
22namespace v8 {
23namespace internal {
24
25// static
32
33// static
40
44
48
55
59
62 return false;
63 }
64 return transitions()->HasSideStepTransitions();
65}
66
70 auto res = transitions()->GetSideStepTransitions()->get(
72 if (res.IsSmi()) {
75 return res.ToSmi();
76 }
78 if (res.GetHeapObjectIfWeak(&target)) return target;
79 DCHECK(res.IsCleared());
81}
82
96
103
105 Tagged<WeakFixedArray> transitions) {
106 DCHECK(IsWeakFixedArray(transitions));
108}
109
111 DCHECK(transition_number < number_of_transitions());
112 return HeapObjectSlot(RawFieldOfElementAt(ToKeyIndex(transition_number)));
113}
114
116 Tagged<WeakFixedArray> transitions) {
117 DCHECK(IsWeakFixedArray(transitions));
119}
120
122 Tagged<WeakFixedArray> proto_transitions) {
123 if (proto_transitions->length() == 0) return 0;
125 proto_transitions->get(kProtoTransitionNumberOfEntriesOffset);
126 return raw.ToSmi().value();
127}
128
129Tagged<Name> TransitionArray::GetKey(int transition_number) {
130 DCHECK(transition_number < number_of_transitions());
131 return Cast<Name>(
132 get(ToKeyIndex(transition_number)).GetHeapObjectAssumeStrong());
133}
134
136 switch (encoding()) {
137 case kPrototypeInfo:
138 case kUninitialized:
139 case kMigrationTarget:
140 UNREACHABLE();
141 return Tagged<Name>();
142 case kWeakRef: {
143 Tagged<Map> map = Cast<Map>(raw_transitions_.GetHeapObjectAssumeWeak());
144 return GetSimpleTransitionKey(map);
145 }
147 return transitions()->GetKey(transition_number);
148 }
149 UNREACHABLE();
150}
151
152void TransitionArray::SetKey(int transition_number, Tagged<Name> key) {
153 DCHECK(transition_number < number_of_transitions());
154 WeakFixedArray::set(ToKeyIndex(transition_number), key);
155}
156
158 DCHECK(transition_number < number_of_transitions());
159 return HeapObjectSlot(RawFieldOfElementAt(ToTargetIndex(transition_number)));
160}
161
162// static
164 Tagged<Map> target) {
166 InternalIndex descriptor = target->LastAdded();
167 Tagged<DescriptorArray> descriptors =
168 target->instance_descriptors(kRelaxedLoad);
169 // Transitions are allowed only for the last added property.
170 DCHECK(descriptors->GetKey(descriptor)->Equals(name));
171 return descriptors->GetDetails(descriptor);
172}
173
175 Tagged<Map> transition) {
176 return transition->GetLastDescriptorDetails(isolate_);
177}
178
179// static
181 Tagged<Map> transition) {
182 InternalIndex descriptor = transition->LastAdded();
183 return transition->instance_descriptors()->GetKey(descriptor);
184}
185
186// static
190
192 DCHECK(transition_number < number_of_transitions());
193 return get(ToTargetIndex(transition_number));
194}
195
197 Tagged<MaybeObject> raw = GetRawTarget(transition_number);
199}
200
202 switch (encoding()) {
203 case kPrototypeInfo:
204 case kUninitialized:
205 case kMigrationTarget:
206 UNREACHABLE();
207 return Map();
208 case kWeakRef:
209 return Cast<Map>(raw_transitions_.GetHeapObjectAssumeWeak());
211 return transitions()->GetTarget(transition_number);
212 }
213 UNREACHABLE();
214}
215
216void TransitionArray::SetRawTarget(int transition_number,
217 Tagged<MaybeObject> value) {
218 DCHECK(transition_number < number_of_transitions());
219 DCHECK(value.IsWeakOrCleared());
220 DCHECK(value.IsCleared() || IsMap(value.GetHeapObjectAssumeWeak()));
221 DCHECK(!value.IsCleared());
222 WeakFixedArray::set(ToTargetIndex(transition_number), value);
223}
224
225bool TransitionArray::GetTargetIfExists(int transition_number, Isolate* isolate,
226 Tagged<Map>* target) {
227 Tagged<MaybeObject> raw = GetRawTarget(transition_number);
228 Tagged<HeapObject> heap_object;
229 // If the raw target is a Smi, then this TransitionArray is in the process of
230 // being deserialized, and doesn't yet have an initialized entry for this
231 // transition.
232 if (raw.IsSmi()) {
233 DCHECK(isolate->has_active_deserializer());
235 return false;
236 }
237 if (raw.GetHeapObjectIfStrong(&heap_object) &&
238 IsUndefined(heap_object, isolate)) {
239 return false;
240 }
242 return true;
243}
244
246 int* out_insertion_index) {
247 return SearchName(name, out_insertion_index);
248}
249
254
256 bool concurrent_search,
257 int* out_insertion_index) {
258 return SearchName(symbol, concurrent_search, out_insertion_index);
259}
260
261int TransitionArray::SearchName(Tagged<Name> name, bool concurrent_search,
262 int* out_insertion_index) {
263 DCHECK(IsUniqueName(name));
264 SLOW_DCHECK_IMPLIES(!concurrent_search, IsSortedNoDuplicates());
265
266 if (number_of_transitions() == 0) {
267 if (out_insertion_index != nullptr) {
268 *out_insertion_index = 0;
269 }
270 return kNotFound;
271 }
272
273 // Do linear search for small arrays, and for searches in the background
274 // thread.
275 const int kMaxElementsForLinearSearch = 8;
276 if (number_of_transitions() <= kMaxElementsForLinearSearch ||
277 concurrent_search) {
278 return LinearSearchName(name, out_insertion_index);
279 }
280
281 return BinarySearchName(name, out_insertion_index);
282}
283
285 int* out_insertion_index) {
287 uint32_t hash = name->hash();
288
289 // Find the first index whose key's hash is greater-than-or-equal-to the
290 // search hash.
291 int i = *std::ranges::lower_bound(std::views::iota(0, end), hash,
292 std::less<>(), [&](int i) {
293 Tagged<Name> entry = GetKey(i);
294 return entry->hash();
295 });
296
297 // There may have been hash collisions, so search for the name from the first
298 // index until the first non-matching hash.
299 for (; i < end; ++i) {
300 Tagged<Name> entry = GetKey(i);
301 if (entry == name) {
302 return i;
303 }
304 uint32_t entry_hash = entry->hash();
305 if (entry_hash != hash) {
306 if (out_insertion_index != nullptr) {
307 *out_insertion_index = i + (entry_hash > hash ? 0 : 1);
308 }
309 return kNotFound;
310 }
311 }
312
313 if (out_insertion_index != nullptr) {
314 *out_insertion_index = end;
315 }
316 return kNotFound;
317}
318
320 int* out_insertion_index) {
321 int len = number_of_transitions();
322 if (out_insertion_index != nullptr) {
323 uint32_t hash = name->hash();
324 for (int i = 0; i < len; i++) {
325 Tagged<Name> entry = GetKey(i);
326 if (entry == name) return i;
327 if (entry->hash() > hash) {
328 *out_insertion_index = i;
329 return kNotFound;
330 }
331 }
332 *out_insertion_index = len;
333 return kNotFound;
334 } else {
335 for (int i = 0; i < len; i++) {
336 if (GetKey(i) == name) return i;
337 }
338 return kNotFound;
339 }
340}
341
343 bool concurrent_access)
344 : isolate_(isolate),
345 map_(map),
346 raw_transitions_(map->raw_transitions(isolate_, kAcquireLoad)),
347 encoding_(GetEncoding(isolate_, raw_transitions_)),
348 concurrent_access_(concurrent_access) {
349 DCHECK_IMPLIES(encoding_ == kMigrationTarget, map_->is_deprecated());
350}
351
352int TransitionsAccessor::Capacity() { return transitions()->Capacity(); }
353
354// static
357 Tagged<HeapObject> heap_object;
358 if (raw_transitions.IsSmi() || raw_transitions.IsCleared()) {
359 return kUninitialized;
360 } else if (raw_transitions.IsWeak()) {
361 return kWeakRef;
362 } else if (raw_transitions.GetHeapObjectIfStrong(isolate, &heap_object)) {
363 if (IsTransitionArray(heap_object)) {
365 } else if (IsPrototypeInfo(heap_object)) {
366 return kPrototypeInfo;
367 } else {
368 DCHECK(IsMap(heap_object));
369 return kMigrationTarget;
370 }
371 } else {
372 UNREACHABLE();
373 }
374}
375
376// static
383
384// static
391
392// static
394 Isolate* isolate, DirectHandle<Map> map, Tagged<Name> name,
396 Tagged<Map> result = TransitionsAccessor(isolate, *map)
397 .SearchTransition(name, kind, attributes);
398 if (result.is_null()) return MaybeHandle<Map>();
399 return MaybeHandle<Map>(result, isolate);
400}
401
402// static
405 Tagged<Symbol> name) {
406 Tagged<Map> result = TransitionsAccessor(isolate, *map).SearchSpecial(name);
407 if (result.is_null()) return {};
408 return MaybeHandle<Map>(result, isolate);
409}
410
412 if (length() < kFirstIndex) return 0;
413 return get(kTransitionLengthIndex).ToSmi().value();
414}
415
417 PropertyKind kind1,
418 PropertyAttributes attributes1,
419 Tagged<Name> key2, uint32_t hash2,
420 PropertyKind kind2,
421 PropertyAttributes attributes2) {
422 int cmp = CompareNames(key1, hash1, key2, hash2);
423 if (cmp != 0) return cmp;
424
425 return CompareDetails(kind1, attributes1, kind2, attributes2);
426}
427
429 Tagged<Name> key2, uint32_t hash2) {
430 if (key1 != key2) {
431 // In case of hash collisions key1 is always "less" than key2.
432 return hash1 <= hash2 ? -1 : 1;
433 }
434
435 return 0;
436}
437
439 PropertyAttributes attributes1,
440 PropertyKind kind2,
441 PropertyAttributes attributes2) {
442 if (kind1 != kind2) {
443 return static_cast<int>(kind1) < static_cast<int>(kind2) ? -1 : 1;
444 }
445
446 if (attributes1 != attributes2) {
447 return static_cast<int>(attributes1) < static_cast<int>(attributes2) ? -1
448 : 1;
449 }
450
451 return 0;
452}
453
454void TransitionArray::Set(int transition_number, Tagged<Name> key,
455 Tagged<MaybeObject> target) {
456 WeakFixedArray::set(ToKeyIndex(transition_number), key);
457 WeakFixedArray::set(ToTargetIndex(transition_number), target);
458}
459
461 if (length() <= kFirstIndex) return 0;
462 return (length() - kFirstIndex) / kEntrySize;
463}
464
470
471template <typename Char>
473 Tagged<Name> transition_name, Tagged<Map> transition_target,
474 base::Vector<const Char> key_chars) {
475 if (transition_target->NumberOfOwnDescriptors() == 0) return false;
476 PropertyDetails details = GetSimpleTargetDetails(transition_target);
477 if (details.location() != PropertyLocation::kField) return false;
479 if (details.attributes() != NONE) return false;
480 if (!IsString(transition_name)) return false;
481 if (!Cast<String>(transition_name)->IsEqualTo(key_chars)) return false;
482 return true;
483}
484
485template <typename Char>
487 base::Vector<const Char> key_chars) {
489 switch (encoding()) {
490 case kPrototypeInfo:
491 case kUninitialized:
492 case kMigrationTarget:
494 case kWeakRef: {
495 Tagged<Map> target =
496 Cast<Map>(raw_transitions_.GetHeapObjectAssumeWeak());
498 if (IsExpectedTransition(name, target, key_chars)) {
499 return {handle(Cast<String>(name), isolate_), handle(target, isolate_)};
500 }
502 }
505 Cast<TransitionArray>(raw_transitions_.GetHeapObjectAssumeStrong());
506 int entries = array->number_of_transitions();
507 // Do linear search for small entries.
508 const int kMaxEntriesForLinearSearch = 8;
509 if (entries > kMaxEntriesForLinearSearch)
511 for (int i = entries - 1; i >= 0; i--) {
512 Tagged<Name> name = array->GetKey(i);
513 Tagged<Map> target = array->GetTarget(i);
514 if (IsExpectedTransition(name, target, key_chars)) {
515 return {handle(Cast<String>(name), isolate_),
517 }
518 }
520 }
521 }
522 UNREACHABLE();
523}
524
525template <typename Callback, typename ProtoCallback, typename SideStepCallback,
526 bool with_key>
528 DisallowGarbageCollection* no_gc, Callback callback,
529 ProtoCallback proto_transition_callback,
530 SideStepCallback side_step_transition_callback) {
531 switch (encoding()) {
532 case kPrototypeInfo:
533 case kUninitialized:
534 case kMigrationTarget:
535 return;
536 case kWeakRef: {
537 Tagged<Map> target =
538 Cast<Map>(raw_transitions_.GetHeapObjectAssumeWeak());
539 if constexpr (with_key) {
540 callback(GetSimpleTransitionKey(target), target);
541 } else {
542 callback(target);
543 }
544 return;
545 }
549 Tagged<TransitionArray> transition_array = transitions();
550 int num_transitions = transition_array->number_of_transitions();
551 ReadOnlyRoots roots(isolate_);
552 for (int i = 0; i < num_transitions; ++i) {
553 if constexpr (with_key) {
554 Tagged<Name> key = transition_array->GetKey(i);
556 } else {
558 }
559 }
560 if constexpr (!std::is_same<ProtoCallback, std::nullptr_t>::value) {
563 transitions()->GetPrototypeTransitions();
565 for (int i = 0; i < length; i++) {
566 Tagged<MaybeObject> target =
568 Tagged<HeapObject> heap_object;
569 if (target.GetHeapObjectIfWeak(&heap_object)) {
570 proto_transition_callback(Cast<Map>(heap_object));
571 }
572 }
573 }
574 }
575 if constexpr (!std::is_same<SideStepCallback, std::nullptr_t>::value) {
578 transitions()->GetSideStepTransitions();
579 for (uint32_t i = SideStepTransition::kFirstMapIdx;
581 Tagged<MaybeObject> target = cache->get(i);
582 if (target.IsWeak() || target == SideStepTransition::Unreachable) {
583 if constexpr (with_key) {
584 side_step_transition_callback(
585 static_cast<SideStepTransition::Kind>(i),
586 target.GetHeapObjectOrSmi());
587 } else {
588 side_step_transition_callback(target.GetHeapObjectOrSmi());
589 }
590 }
591 }
592 }
593 }
594
595 return;
596 }
597 }
598 UNREACHABLE();
599}
600
601} // namespace internal
602} // namespace v8
603
605
606#endif // V8_OBJECTS_TRANSITIONS_INL_H_
Isolate * isolate_
Builtins::Kind kind
Definition builtins.cc:40
#define SLOW_DCHECK_IMPLIES(v1, v2)
Definition checks.h:22
base::Mutex * full_transition_array_access()
Definition isolate.h:749
PropertyAttributes attributes() const
PropertyLocation location() const
static constexpr Tagged< Smi > uninitialized_deserialization_value()
Definition smi.h:114
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
static constexpr Tagged< Smi > zero()
Definition smi.h:99
void set(int index, Tagged< ElementT > value, WriteBarrierMode mode=kDefaultMode)
bool GetHeapObjectIfStrong(Tagged< HeapObject > *result) const
Tagged< HeapObject > GetHeapObjectAssumeStrong() const
bool ToSmi(Tagged< Smi > *value) const
Tagged< HeapObject > GetHeapObjectAssumeWeak() const
constexpr V8_INLINE bool IsSmi() const
Definition tagged.h:508
static const int kSideStepTransitionsIndex
int SearchName(Tagged< Name > name, bool concurrent_search=false, int *out_insertion_index=nullptr)
static int ToKeyIndex(int transition_number)
V8_EXPORT_PRIVATE Tagged< Map > SearchAndGetTarget(PropertyKind kind, Tagged< Name > name, PropertyAttributes attributes)
Tagged< Name > GetKey(int transition_number)
void SetKey(int transition_number, Tagged< Name > value)
static int CompareDetails(PropertyKind kind1, PropertyAttributes attributes1, PropertyKind kind2, PropertyAttributes attributes2)
Tagged< MaybeObject > GetRawTarget(int transition_number)
Tagged< Map > SearchAndGetTargetForTesting(PropertyKind kind, Tagged< Name > name, PropertyAttributes attributes)
void SetPrototypeTransitions(Tagged< WeakFixedArray > prototype_transitions)
int SearchSpecial(Tagged< Symbol > symbol, bool concurrent_search=false, int *out_insertion_index=nullptr)
int BinarySearchName(Tagged< Name > name, int *out_insertion_index)
int LinearSearchName(Tagged< Name > name, int *out_insertion_index)
void SetSideStepTransitions(Tagged< WeakFixedArray > transitions)
Tagged< WeakFixedArray > GetSideStepTransitions()
HeapObjectSlot GetKeySlot(int transition_number)
void SetNumberOfTransitions(int number_of_transitions)
static int CompareKeys(Tagged< Name > key1, uint32_t hash1, PropertyKind kind1, PropertyAttributes attributes1, Tagged< Name > key2, uint32_t hash2, PropertyKind kind2, PropertyAttributes attributes2)
void SetRawTarget(int transition_number, Tagged< MaybeObject > target)
static const int kProtoTransitionNumberOfEntriesOffset
static int ToTargetIndex(int transition_number)
bool GetTargetIfExists(int transition_number, Isolate *isolate, Tagged< Map > *target)
Tagged< Map > GetTarget(int transition_number)
int SearchNameForTesting(Tagged< Name > name, int *out_insertion_index=nullptr)
static const int kProtoTransitionHeaderSize
void Set(int transition_number, Tagged< Name > key, Tagged< MaybeObject > target)
HeapObjectSlot GetTargetSlot(int transition_number)
static int NumberOfPrototypeTransitions(Tagged< WeakFixedArray > proto_transitions)
static const int kTransitionLengthIndex
static int CompareNames(Tagged< Name > key1, uint32_t hash1, Tagged< Name > key2, uint32_t hash2)
Tagged< WeakFixedArray > GetPrototypeTransitions()
static constexpr int kNotFound
static const int kPrototypeTransitionsIndex
std::pair< Handle< String >, Handle< Map > > ExpectedTransition(base::Vector< const Char > key_chars)
bool IsExpectedTransition(Tagged< Name > transition_name, Tagged< Map > transition_target, base::Vector< const Char > key_chars)
Tagged< Map > SearchTransition(Tagged< Name > name, PropertyKind kind, PropertyAttributes attributes)
static Tagged< TransitionArray > GetTransitionArray(Isolate *isolate, Tagged< MaybeObject > raw_transitions)
PropertyDetails GetSimpleTargetDetails(Tagged< Map > transition)
void SetSideStepTransition(SideStepTransition::Kind i, Tagged< Object > target)
static Tagged< Map > GetTargetFromRaw(Tagged< MaybeObject > raw)
Tagged< MaybeObject > raw_transitions_
Tagged< TransitionArray > transitions()
static bool IsSpecialTransition(ReadOnlyRoots roots, Tagged< Name > name)
Tagged< Object > GetSideStepTransition(SideStepTransition::Kind i)
void ForEachTransitionWithKey(DisallowGarbageCollection *no_gc, Callback callback, ProtoCallback proto_transition_callback, SideStepCallback side_step_transition_callback)
TransitionsAccessor(Isolate *isolate, Tagged< Map > map, bool concurrent_access=false)
static PropertyDetails GetTargetDetails(Tagged< Name > name, Tagged< Map > target)
Tagged< Map > SearchSpecial(Tagged< Symbol > name)
static Tagged< Name > GetSimpleTransitionKey(Tagged< Map > transition)
Tagged< Map > GetTarget(int transition_number)
static Encoding GetEncoding(Isolate *isolate, Tagged< MaybeObject > raw_transitions)
Tagged< Name > GetKey(int transition_number)
const MapRef map_
int end
TNode< Object > target
TNode< Object > callback
ZoneVector< RpoNumber > & result
ZoneVector< Entry > entries
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
ReadOnlyRoots GetReadOnlyRoots()
Definition roots-inl.h:86
Tagged(T object) -> Tagged< T >
V8_INLINE constexpr bool IsSmi(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:665
kInstanceDescriptorsOffset raw_transitions
Definition map-inl.h:61
Tagged< MaybeWeak< T > > MakeWeak(Tagged< T > value)
Definition tagged.h:893
bool IsUniqueName(Tagged< Name > obj)
SlotTraits::THeapObjectSlot HeapObjectSlot
Definition globals.h:1253
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 RelaxedLoadTag kRelaxedLoad
Definition globals.h:2909
static constexpr AcquireLoadTag kAcquireLoad
Definition globals.h:2908
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
static constexpr int index_of(Kind kind)
Definition transitions.h:44
static constexpr uint32_t kLastMapIdx
Definition transitions.h:49
static constexpr Tagged< Smi > Empty
Definition transitions.h:40
static constexpr uint32_t kSize
Definition transitions.h:37
static constexpr uint32_t kFirstMapIdx
Definition transitions.h:47
static constexpr Tagged< Smi > Unreachable
Definition transitions.h:41