v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
transitions.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_H_
6#define V8_OBJECTS_TRANSITIONS_H_
7
8#include <optional>
9
10#include "src/common/checks.h"
14#include "src/objects/map.h"
16#include "src/objects/name.h"
17#include "src/objects/objects.h"
18
19// Has to be the last include (doesn't have include guards):
21
22namespace v8::internal {
23
24// Find all transitions with given name and calls the callback.
25using ForEachTransitionCallback = std::function<void(Tagged<Map>)>;
26
27// Descriptor for the contents of special side-step transition arrays.
28// Side-step transitions are accessed through the TransitionsAccessor which
29// enforces adherence to this format. The entries are either weak, Empty, or
30// Unreachable.
32 enum class Kind : uint32_t {
36 };
37 static constexpr uint32_t kSize =
38 static_cast<uint32_t>(Kind::kObjectAssignValidityCell) + 1;
39
40 static constexpr Tagged<Smi> Empty = Smi::FromInt(0);
41 static constexpr Tagged<Smi> Unreachable = Smi::FromInt(1);
42
43 private:
44 static constexpr int index_of(Kind kind) {
45 return static_cast<uint32_t>(kind);
46 }
47 static constexpr uint32_t kFirstMapIdx =
48 static_cast<uint32_t>(Kind::kCloneObject);
49 static constexpr uint32_t kLastMapIdx =
50 static_cast<uint32_t>(Kind::kObjectAssign);
51 friend class TransitionsAccessor;
52 friend class TransitionArray;
54};
55
56std::ostream& operator<<(std::ostream& os, SideStepTransition::Kind sidestep);
57
58// TransitionsAccessor is a helper class to encapsulate access to the various
59// ways a Map can store transitions to other maps in its respective field at
60// Map::kTransitionsOrPrototypeInfo.
61// It caches state information internally, which becomes stale when a Map's
62// transitions storage changes or when a GC cycle clears dead transitions;
63// so while a TransitionsAccessor instance can be used for several read-only
64// operations in a row (provided no GC happens between them), it must be
65// discarded and recreated after "Insert" and "UpdateHandler" operations.
66//
67// Internal details: a Map's field either holds an in-place weak reference to a
68// transition target, or a StoreIC handler for a transitioning store (which in
69// turn points to its target map), or a TransitionArray for several target maps
70// and/or handlers as well as prototype and ElementsKind transitions. Property
71// details (and in case of inline target storage, the key) are retrieved from
72// the target map's descriptor array. Stored transitions are weak in the GC
73// sense: both single transitions stored inline and TransitionArray fields are
74// cleared when the map they refer to is not otherwise reachable.
76 public:
77 // {concurrent_access} signals that the TransitionsAccessor will only be used
78 // in background threads. It acquires a reader lock for critical paths, as
79 // well as blocking the accessor from modifying the TransitionsArray.
80 inline TransitionsAccessor(Isolate* isolate, Tagged<Map> map,
81 bool concurrent_access = false);
82
83 // Insert a new transition into |map|'s transition array, extending it
84 // as necessary. This can trigger GC.
85 static void Insert(Isolate* isolate, DirectHandle<Map> map,
87 TransitionKindFlag flag) {
88 InsertHelper(isolate, map, name, DirectHandle<Map>(target), flag);
89 }
90 static void InsertNoneSentinel(Isolate* isolate, DirectHandle<Map> map,
91 DirectHandle<Name> name) {
92 InsertHelper(isolate, map, name, DirectHandle<Map>(),
93 TransitionKindFlag::SPECIAL_TRANSITION);
94 }
95
96 Tagged<Map> SearchTransition(Tagged<Name> name, PropertyKind kind,
97 PropertyAttributes attributes);
98 static inline MaybeHandle<Map> SearchTransition(
99 Isolate* isolate, DirectHandle<Map> map, Tagged<Name> name,
101
102 // Searches for a transition with a special symbol.
103 Tagged<Map> SearchSpecial(Tagged<Symbol> name);
104 static inline MaybeHandle<Map> SearchSpecial(Isolate* isolate,
106 Tagged<Symbol> name);
107
108 // Returns true for non-property transitions like elements kind, or
109 // or frozen/sealed transitions.
110 static bool IsSpecialTransition(ReadOnlyRoots roots, Tagged<Name> name);
111
112 MaybeHandle<Map> FindTransitionToField(DirectHandle<String> name);
113
114 // Find all transitions with given name and calls the callback.
115 // Neither GCs nor operations requiring Isolate::full_transition_array_access
116 // lock are allowed inside the callback.
117 // If any of the GC- or lock-requiring processing is necessary, it has to be
118 // done outside of the callback.
119 void ForEachTransitionTo(Tagged<Name> name,
122
123 template <typename Char>
124 inline bool IsExpectedTransition(Tagged<Name> transition_name,
125 Tagged<Map> transition_target,
126 base::Vector<const Char> key_chars);
127
128 template <typename Char>
129 inline std::pair<Handle<String>, Handle<Map>> ExpectedTransition(
130 base::Vector<const Char> key_chars);
131
132 template <typename Callback, typename ProtoCallback,
133 typename SideStepCallback>
135 ProtoCallback proto_transition_callback,
136 SideStepCallback side_step_transition_callback) {
137 ForEachTransitionWithKey<Callback, ProtoCallback, SideStepCallback, false>(
138 no_gc, callback, proto_transition_callback,
139 side_step_transition_callback);
140 }
141
142 template <typename Callback, typename ProtoCallback,
143 typename SideStepCallback, bool with_key = true>
144 void ForEachTransitionWithKey(DisallowGarbageCollection* no_gc,
145 Callback callback,
146 ProtoCallback proto_transition_callback,
147 SideStepCallback side_step_transition_callback);
148
149 int NumberOfTransitions();
150 // The size of transition arrays are limited so they do not end up in large
151 // object space. Otherwise ClearNonLiveReferences would leak memory while
152 // applying in-place right trimming.
153 static const int kMaxNumberOfTransitions = 1024 + 512;
154 inline Tagged<Name> GetKey(int transition_number);
155 inline Tagged<Map> GetTarget(int transition_number);
156 static inline PropertyDetails GetTargetDetails(Tagged<Name> name,
157 Tagged<Map> target);
158
159 static bool CanHaveMoreTransitions(Isolate* isolate, DirectHandle<Map> map);
160
161 static bool IsMatchingMap(Tagged<Map> target, Tagged<Name> name,
163
164 bool HasIntegrityLevelTransitionTo(
165 Tagged<Map> to, Tagged<Symbol>* out_symbol = nullptr,
166 PropertyAttributes* out_integrity_level = nullptr);
167
168 // ===== ITERATION =====
169 using TraverseCallback = std::function<void(Tagged<Map>)>;
170
171 // Traverse the transition tree in preorder.
173 // Make sure that we do not allocate in the callback.
175 base::MutexGuardIf mutex_guard(isolate_->full_transition_array_access(),
176 concurrent_access_);
177 TraverseTransitionTreeInternal(callback, &no_gc);
178 }
179
180 // ===== PROTOTYPE TRANSITIONS =====
181 // When you set the prototype of an object using the __proto__ accessor you
182 // need a new map for the object (the prototype is stored in the map). In
183 // order not to multiply maps unnecessarily we store these as transitions in
184 // the original map. That way we can transition to the same map if the same
185 // prototype is set, rather than creating a new map every time. The
186 // transitions are in the form of a map where the keys are prototype objects
187 // and the values are the maps they transition to.
188 // PutPrototypeTransition can trigger GC.
189 static bool PutPrototypeTransition(Isolate* isolate, DirectHandle<Map>,
190 DirectHandle<Object> prototype,
191 DirectHandle<Map> target_map);
192 static std::optional<Tagged<Map>> GetPrototypeTransition(
193 Isolate* isolate, Tagged<Map> map, Tagged<Object> prototype);
194 bool HasPrototypeTransitions();
195
196 // During the first-time Map::Update and Map::TryUpdate, the migration target
197 // map could be cached in the raw_transitions slot of the old map that is
198 // deprecated from the map transition tree. The next time old map is updated,
199 // we will check this cache slot as a shortcut to get the migration target
200 // map.
201 static void SetMigrationTarget(Isolate* isolate, DirectHandle<Map> map,
202 Tagged<Map> migration_target);
203 Tagged<Map> GetMigrationTarget();
204
205 inline bool HasSideStepTransitions();
206 static void EnsureHasSideStepTransitions(Isolate* isolate,
208 inline Tagged<Object> GetSideStepTransition(SideStepTransition::Kind i);
209 inline void SetSideStepTransition(SideStepTransition::Kind i,
210 Tagged<Object> target);
211
212#if DEBUG || OBJECT_PRINT
213 void PrintTransitions(std::ostream& os);
214 static void PrintOneTransition(std::ostream& os, Tagged<Name> key,
215 Tagged<Map> target);
216 void PrintTransitionTree();
217 void PrintTransitionTree(std::ostream& os, int level,
219#endif
220#if DEBUG
221 static void CheckNewTransitionsAreConsistent(Isolate* isolate,
223 Tagged<Object> transitions);
224 bool IsConsistentWithBackPointers();
225 bool IsSortedNoDuplicates();
226#endif
227
228 protected:
229 // Allow tests to use inheritance to access internals.
237
238 inline Encoding encoding() { return encoding_; }
239
240 inline int Capacity();
241
242 inline Tagged<TransitionArray> transitions();
243
245
246 private:
247 friend class MarkCompactCollector; // For HasSimpleTransitionTo.
248 friend class TransitionArray;
249
250 static inline Encoding GetEncoding(Isolate* isolate,
251 Tagged<MaybeObject> raw_transitions);
252 static inline Encoding GetEncoding(Isolate* isolate,
254 static inline Encoding GetEncoding(Isolate* isolate, DirectHandle<Map> map);
255
256 static inline Tagged<TransitionArray> GetTransitionArray(
257 Isolate* isolate, Tagged<MaybeObject> raw_transitions);
258 static inline Tagged<TransitionArray> GetTransitionArray(
259 Isolate* isolate, DirectHandle<Map> map);
260
261 static inline Tagged<Map> GetSimpleTransition(Isolate* isolate,
263 static inline Tagged<Name> GetSimpleTransitionKey(Tagged<Map> transition);
264 inline PropertyDetails GetSimpleTargetDetails(Tagged<Map> transition);
265
266 static inline Tagged<Map> GetTargetFromRaw(Tagged<MaybeObject> raw);
267
268 static void EnsureHasFullTransitionArray(Isolate* isolate,
270 static void SetPrototypeTransitions(
271 Isolate* isolate, DirectHandle<Map> map,
272 DirectHandle<WeakFixedArray> proto_transitions);
273 static Tagged<WeakFixedArray> GetPrototypeTransitions(Isolate* isolate,
274 Tagged<Map> map);
275
276 static void InsertHelper(Isolate* isolate, DirectHandle<Map> map,
278 TransitionKindFlag flag);
279
280 static inline void ReplaceTransitions(
281 Isolate* isolate, DirectHandle<Map> map,
282 Tagged<UnionOf<TransitionArray, MaybeWeak<Map>>> new_transitions);
283 static inline void ReplaceTransitions(
284 Isolate* isolate, DirectHandle<Map> map,
285 DirectHandle<TransitionArray> new_transitions);
286
287 bool HasSimpleTransitionTo(Tagged<Map> map);
288
290
291 void TraverseTransitionTreeInternal(const TraverseCallback& callback,
293
299
301};
302
303// TransitionArrays are fixed arrays used to hold map transitions for property,
304// constant, and element changes.
305// The TransitionArray class exposes a very low-level interface. Most clients
306// should use TransitionsAccessors.
307// TransitionArrays have the following format:
308// [0] Tagged<Smi>(0) or WeakFixedArray of prototype transitions (strong ref)
309// [1] Tagged<Smi>(0) or WeakFixedArray of side-step transitions (strong ref)
310// [2] Number of transitions (can be zero after trimming)
311// [3] First transition key (strong ref)
312// [4] First transition target (weak ref)
313// ...
314// [4 + number of transitions * kTransitionSize]: start of slack
315// TODO(olivf): The slots for prototype transitions and side-steps could be
316// shared.
318 public:
319 inline int number_of_transitions() const;
320
322 inline bool HasPrototypeTransitions();
323
324 // Accessors for fetching instance transition at transition number.
325 inline void SetKey(int transition_number, Tagged<Name> value);
326 inline Tagged<Name> GetKey(int transition_number);
327 inline HeapObjectSlot GetKeySlot(int transition_number);
328
329 inline Tagged<Map> GetTarget(int transition_number);
330 inline void SetRawTarget(int transition_number, Tagged<MaybeObject> target);
331 inline Tagged<MaybeObject> GetRawTarget(int transition_number);
332 inline HeapObjectSlot GetTargetSlot(int transition_number);
333 inline bool GetTargetIfExists(int transition_number, Isolate* isolate,
334 Tagged<Map>* target);
335
336 static constexpr int kNotFound = -1;
337
338#ifdef DEBUG
339 V8_EXPORT_PRIVATE bool IsSortedNoDuplicates();
340#endif
341
342 V8_EXPORT_PRIVATE void Sort();
343
344 void PrintInternal(std::ostream& os);
345
348
349 // Layout for full transition arrays.
350 static const int kPrototypeTransitionsIndex = 0;
351 static const int kSideStepTransitionsIndex = 1;
352 static const int kTransitionLengthIndex = 2;
353 static const int kFirstIndex = 3;
354
355 // Layout of map transition entries in full transition arrays.
356 static const int kEntryKeyIndex = 0;
357 static const int kEntryTargetIndex = 1;
358 static const int kEntrySize = 2;
359
360 // Conversion from transition number to array indices.
361 static int ToKeyIndex(int transition_number) {
362 return kFirstIndex + (transition_number * kEntrySize) + kEntryKeyIndex;
363 }
364
365 static int ToTargetIndex(int transition_number) {
366 return kFirstIndex + (transition_number * kEntrySize) + kEntryTargetIndex;
367 }
368
369 inline int SearchNameForTesting(Tagged<Name> name,
370 int* out_insertion_index = nullptr);
371
374
375 // Accessors for side-step transitions.
376 inline bool HasSideStepTransitions();
377 static void CreateSideStepTransitions(
378 Isolate* isolate, DirectHandle<TransitionArray> transitions);
379
380 private:
381 friend class Factory;
384
386
387 inline int Capacity();
388
389 // ===== PROTOTYPE TRANSITIONS =====
390 // Cache format:
391 // 0: finger - index of the first free cell in the cache
392 // 1 + i: target map
393 static const int kProtoTransitionHeaderSize = 1;
394 static const int kMaxCachedPrototypeTransitions = 256;
395
396 inline void SetPrototypeTransitions(
397 Tagged<WeakFixedArray> prototype_transitions);
398
399 static inline int NumberOfPrototypeTransitions(
400 Tagged<WeakFixedArray> proto_transitions);
402 Tagged<WeakFixedArray> proto_transitions, int value);
403
405 static_assert(kProtoTransitionHeaderSize == 1);
406
407 // Returns the fixed array length required to hold number_of_transitions
408 // transitions.
412
413 // Search a transition for a given kind, property name and attributes.
415 PropertyAttributes attributes, int* out_insertion_index = nullptr);
416
419
420 // Search a non-property transition (like elements kind, observe or frozen
421 // transitions).
422 inline int SearchSpecial(Tagged<Symbol> symbol,
423 bool concurrent_search = false,
424 int* out_insertion_index = nullptr);
425 // Search a first transition for a given property name.
426 inline int SearchName(Tagged<Name> name, bool concurrent_search = false,
427 int* out_insertion_index = nullptr);
428 int SearchDetails(int transition, PropertyKind kind,
429 PropertyAttributes attributes, int* out_insertion_index);
431 PropertyAttributes attributes);
432
433 inline int LinearSearchName(Tagged<Name> name, int* out_insertion_index);
434 inline int BinarySearchName(Tagged<Name> name, int* out_insertion_index);
435
436 // Find all transitions with given name and calls the callback.
439
440 static bool CompactPrototypeTransitionArray(Isolate* isolate,
442
444 DirectHandle<WeakFixedArray> array, int new_capacity, Isolate* isolate);
445
446 // Compares two tuples <key, kind, attributes>, returns -1 if
447 // tuple1 is "less" than tuple2, 0 if tuple1 equal to tuple2 and 1 otherwise.
448 static inline int CompareKeys(Tagged<Name> key1, uint32_t hash1,
449 PropertyKind kind1,
450 PropertyAttributes attributes1,
451 Tagged<Name> key2, uint32_t hash2,
452 PropertyKind kind2,
453 PropertyAttributes attributes2);
454
455 // Compares keys, returns -1 if key1 is "less" than key2,
456 // 0 if key1 equal to key2 and 1 otherwise.
457 static inline int CompareNames(Tagged<Name> key1, uint32_t hash1,
458 Tagged<Name> key2, uint32_t hash2);
459
460 // Compares two details, returns -1 if details1 is "less" than details2,
461 // 0 if details1 equal to details2 and 1 otherwise.
462 static inline int CompareDetails(PropertyKind kind1,
463 PropertyAttributes attributes1,
464 PropertyKind kind2,
465 PropertyAttributes attributes2);
466
467 inline void Set(int transition_number, Tagged<Name> key,
468 Tagged<MaybeObject> target);
469
471 inline void SetSideStepTransitions(Tagged<WeakFixedArray> transitions);
472};
473
474} // namespace v8::internal
475
477
478#endif // V8_OBJECTS_TRANSITIONS_H_
Isolate * isolate_
#define DISALLOW_GARBAGE_COLLECTION(name)
Builtins::Kind kind
Definition builtins.cc:40
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
int SearchDetails(int transition, PropertyKind kind, PropertyAttributes attributes, int *out_insertion_index)
int Search(PropertyKind kind, Tagged< Name > name, PropertyAttributes attributes, int *out_insertion_index=nullptr)
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)
static int LengthFor(int number_of_transitions)
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)
static DirectHandle< WeakFixedArray > GrowPrototypeTransitionArray(DirectHandle< WeakFixedArray > array, int new_capacity, Isolate *isolate)
V8_EXPORT_PRIVATE void Sort()
static const int kEntryKeyIndex
static bool CompactPrototypeTransitionArray(Isolate *isolate, Tagged< WeakFixedArray > array)
void ForEachTransitionTo(Tagged< Name > name, const ForEachTransitionCallback &callback)
Tagged< MaybeObject > GetRawTarget(int transition_number)
Tagged< Map > SearchDetailsAndGetTarget(int transition, PropertyKind kind, PropertyAttributes attributes)
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 kEntryTargetIndex
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)
void PrintInternal(std::ostream &os)
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 void CreateSideStepTransitions(Isolate *isolate, DirectHandle< TransitionArray > transitions)
static const int kMaxCachedPrototypeTransitions
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 void SetNumberOfPrototypeTransitions(Tagged< WeakFixedArray > proto_transitions, int value)
static const int kPrototypeTransitionsIndex
Tagged< Map > GetTargetMapFromWeakRef()
static void InsertNoneSentinel(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Name > name)
Definition transitions.h:90
void ForEachTransition(DisallowGarbageCollection *no_gc, Callback callback, ProtoCallback proto_transition_callback, SideStepCallback side_step_transition_callback)
void TraverseTransitionTree(const TraverseCallback &callback)
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionsAccessor)
Tagged< MaybeObject > raw_transitions_
std::function< void(Tagged< Map >)> TraverseCallback
static void Insert(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Name > name, DirectHandle< Map > target, TransitionKindFlag flag)
Definition transitions.h:85
DisallowGarbageCollection no_gc_
TNode< Object > callback
std::ostream & operator<<(std::ostream &os, AtomicMemoryOrder order)
typename detail::FlattenUnionHelper< Union<>, Ts... >::type UnionOf
Definition union.h:123
std::function< void(Tagged< Map >)> ForEachTransitionCallback
Definition transitions.h:25
#define DECL_VERIFIER(Name)
#define DECL_PRINTER(Name)
#define V8_EXPORT_PRIVATE
Definition macros.h:460
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
std::unique_ptr< ValueMirror > key