OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef V8_TRANSITIONS_H_ | 5 #ifndef V8_TRANSITIONS_H_ |
6 #define V8_TRANSITIONS_H_ | 6 #define V8_TRANSITIONS_H_ |
7 | 7 |
8 #include "src/checks.h" | 8 #include "src/checks.h" |
9 #include "src/elements-kind.h" | 9 #include "src/elements-kind.h" |
10 #include "src/heap/heap.h" | 10 #include "src/heap/heap.h" |
11 #include "src/isolate.h" | 11 #include "src/isolate.h" |
12 #include "src/objects.h" | 12 #include "src/objects.h" |
13 | 13 |
14 namespace v8 { | 14 namespace v8 { |
15 namespace internal { | 15 namespace internal { |
16 | 16 |
17 | 17 |
18 // TransitionArrays are fixed arrays used to hold map transitions for property, | 18 // TransitionArrays are fixed arrays used to hold map transitions for property, |
19 // constant, and element changes. They can either be simple transition arrays | 19 // constant, and element changes. "Simple" transitions storing only a single |
20 // that store a single property transition, or a full transition array that has | 20 // property transition are stored inline (i.e. the target map is stored |
| 21 // directly); otherwise a full transition array is used that has |
21 // prototype transitions and multiple property transitons. The details related | 22 // prototype transitions and multiple property transitons. The details related |
22 // to property transitions are accessed in the descriptor array of the target | 23 // to property transitions are accessed in the descriptor array of the target |
23 // map. In the case of a simple transition, the key is also read from the | 24 // map. In the case of a simple transition, the key is also read from the |
24 // descriptor array of the target map. | 25 // descriptor array of the target map. |
25 // | 26 // |
26 // The simple format of the these objects is: | 27 // This class provides a static interface that operates directly on maps |
27 // [0] Undefined or back pointer map | 28 // and handles the distinction between simple and full transitions storage. |
28 // [1] Single transition | |
29 // | 29 // |
30 // The full format is: | 30 // The full format is: |
31 // [0] Undefined or back pointer map | 31 // [0] Smi(0) or fixed array of prototype transitions |
32 // [1] Smi(0) or fixed array of prototype transitions | 32 // [1] Number of transitions |
33 // [2] Number of transitions | 33 // [2] First transition |
34 // [3] First transition | 34 // [2 + number of transitions * kTransitionSize]: start of slack |
35 // [3 + number of transitions * kTransitionSize]: start of slack | |
36 class TransitionArray: public FixedArray { | 35 class TransitionArray: public FixedArray { |
37 public: | 36 public: |
38 // Accessors for fetching instance transition at transition number. | 37 // Insert a new transition into |map|'s transition array, extending it |
39 inline Name* GetKey(int transition_number); | 38 // as necessary. |
40 inline void SetKey(int transition_number, Name* value); | 39 static void Insert(Handle<Map> map, Handle<Name> name, Handle<Map> target, |
41 inline Object** GetKeySlot(int transition_number); | 40 SimpleTransitionFlag flag); |
42 int GetSortedKeyIndex(int transition_number) { return transition_number; } | |
43 | 41 |
44 Name* GetSortedKey(int transition_number) { | 42 static Map* SearchTransition(Map* map, PropertyKind kind, Name* name, |
45 return GetKey(transition_number); | 43 PropertyAttributes attributes); |
| 44 |
| 45 static Map* SearchSpecial(Map* map, Symbol* name); |
| 46 |
| 47 static Handle<Map> FindTransitionToField(Handle<Map> map, Handle<Name> name); |
| 48 |
| 49 static Handle<String> ExpectedTransitionKey(Handle<Map> map); |
| 50 |
| 51 static Handle<Map> ExpectedTransitionTarget(Handle<Map> map) { |
| 52 DCHECK(!ExpectedTransitionKey(map).is_null()); |
| 53 return Handle<Map>(GetSimpleTransition(map->raw_transitions())); |
| 54 } |
| 55 static inline bool IsEmpty(Object* raw_transition) { |
| 56 return raw_transition->IsSmi() || |
| 57 (raw_transition->IsWeakCell() && |
| 58 WeakCell::cast(raw_transition)->cleared()); |
| 59 } |
| 60 static inline bool IsSimpleTransition(Object* raw_transition) { |
| 61 DCHECK(!raw_transition->IsWeakCell() || |
| 62 WeakCell::cast(raw_transition)->cleared() || |
| 63 WeakCell::cast(raw_transition)->value()->IsMap()); |
| 64 return raw_transition->IsWeakCell() && |
| 65 !WeakCell::cast(raw_transition)->cleared(); |
| 66 } |
| 67 static inline Map* GetSimpleTransition(Object* raw_transition) { |
| 68 DCHECK(IsSimpleTransition(raw_transition)); |
| 69 return Map::cast(WeakCell::cast(raw_transition)->value()); |
| 70 } |
| 71 static inline bool IsFullTransitionArray(Object* raw_transitions) { |
| 72 return raw_transitions->IsTransitionArray(); |
46 } | 73 } |
47 | 74 |
48 inline Map* GetTarget(int transition_number); | 75 // The size of transition arrays are limited so they do not end up in large |
49 inline void SetTarget(int transition_number, Map* target); | 76 // object space. Otherwise ClearNonLiveReferences would leak memory while |
| 77 // applying in-place right trimming. |
| 78 static bool CanHaveMoreTransitions(Handle<Map> map); |
50 | 79 |
51 inline PropertyDetails GetTargetDetails(int transition_number); | 80 // ===== PROTOTYPE TRANSITIONS ===== |
52 inline Object* GetTargetValue(int transition_number); | 81 // When you set the prototype of an object using the __proto__ accessor you |
| 82 // need a new map for the object (the prototype is stored in the map). In |
| 83 // order not to multiply maps unnecessarily we store these as transitions in |
| 84 // the original map. That way we can transition to the same map if the same |
| 85 // prototype is set, rather than creating a new map every time. The |
| 86 // transitions are in the form of a map where the keys are prototype objects |
| 87 // and the values are the maps they transition to. |
| 88 // Cache format: |
| 89 // 0: finger - index of the first free cell in the cache |
| 90 // 1 + i: target map |
| 91 static const int kMaxCachedPrototypeTransitions = 256; |
| 92 static Handle<Map> PutPrototypeTransition(Handle<Map> map, |
| 93 Handle<Object> prototype, |
| 94 Handle<Map> target_map); |
53 | 95 |
54 inline bool HasElementsTransition(); | 96 static Handle<Map> GetPrototypeTransition(Handle<Map> map, |
| 97 Handle<Object> prototype); |
55 | 98 |
56 inline Object* back_pointer_storage(); | 99 static FixedArray* GetPrototypeTransitions(Map* map); |
57 inline void set_back_pointer_storage( | 100 |
58 Object* back_pointer, | 101 static int NumberOfPrototypeTransitions(FixedArray* proto_transitions) { |
59 WriteBarrierMode mode = UPDATE_WRITE_BARRIER); | 102 if (proto_transitions->length() == 0) return 0; |
| 103 Object* raw = proto_transitions->get(kProtoTransitionNumberOfEntriesOffset); |
| 104 return Smi::cast(raw)->value(); |
| 105 } |
| 106 |
| 107 static void SetNumberOfPrototypeTransitions(FixedArray* proto_transitions, |
| 108 int value); |
60 | 109 |
61 inline FixedArray* GetPrototypeTransitions(); | 110 inline FixedArray* GetPrototypeTransitions(); |
62 inline void SetPrototypeTransitions( | 111 inline void SetPrototypeTransitions( |
63 FixedArray* prototype_transitions, | 112 FixedArray* prototype_transitions, |
64 WriteBarrierMode mode = UPDATE_WRITE_BARRIER); | 113 WriteBarrierMode mode = UPDATE_WRITE_BARRIER); |
65 inline Object** GetPrototypeTransitionsSlot(); | 114 inline Object** GetPrototypeTransitionsSlot(); |
66 inline bool HasPrototypeTransitions(); | 115 inline bool HasPrototypeTransitions(); |
67 | 116 |
68 // Returns the number of transitions in the array. | 117 // ===== ITERATION ===== |
69 int number_of_transitions() { | 118 |
70 if (IsSimpleTransition()) return 1; | 119 typedef void (*TraverseCallback)(Map* map, void* data); |
71 if (length() <= kFirstIndex) return 0; | 120 |
72 return Smi::cast(get(kTransitionLengthIndex))->value(); | 121 // Traverse the transition tree in postorder. |
| 122 static void TraverseTransitionTree(Map* map, TraverseCallback callback, |
| 123 void* data) { |
| 124 // Make sure that we do not allocate in the callback. |
| 125 DisallowHeapAllocation no_allocation; |
| 126 TraverseTransitionTreeInternal(map, callback, data); |
73 } | 127 } |
74 | 128 |
75 int number_of_transitions_storage() { | 129 // ===== FINE-GRAINED ACCESSORS ===== |
76 if (IsSimpleTransition()) return 1; | 130 |
77 if (length() <= kFirstIndex) return 0; | 131 // Accessors for fetching instance transition at transition number. |
78 return (length() - kFirstIndex) / kTransitionSize; | 132 static inline Name* GetKey(Object* raw_transitions, int transition_number); |
| 133 inline Name* GetKey(int transition_number); |
| 134 inline void SetKey(int transition_number, Name* value); |
| 135 inline Object** GetKeySlot(int transition_number); |
| 136 int GetSortedKeyIndex(int transition_number) { return transition_number; } |
| 137 |
| 138 Name* GetSortedKey(int transition_number) { |
| 139 return GetKey(transition_number); |
79 } | 140 } |
80 | 141 |
81 int NumberOfSlackTransitions() { | 142 static inline Map* GetTarget(Object* raw_transitions, int transition_number); |
82 return number_of_transitions_storage() - number_of_transitions(); | 143 inline Map* GetTarget(int transition_number); |
83 } | 144 inline void SetTarget(int transition_number, Map* target); |
84 | |
85 inline void SetNumberOfTransitions(int number_of_transitions); | |
86 inline int number_of_entries() { return number_of_transitions(); } | |
87 | |
88 // Creates a FullTransitionArray from a SimpleTransitionArray in | |
89 // containing_map. | |
90 static Handle<TransitionArray> ExtendToFullTransitionArray( | |
91 Handle<Map> containing_map); | |
92 | |
93 // Return a transition array, using the array from the owning map if it | |
94 // already has one (copying into a larger array if necessary), otherwise | |
95 // creating a new one according to flag. | |
96 // TODO(verwaest): This should not cause an existing transition to be | |
97 // overwritten. | |
98 static Handle<TransitionArray> Insert(Handle<Map> map, Handle<Name> name, | |
99 Handle<Map> target, | |
100 SimpleTransitionFlag flag); | |
101 // Search a transition for a given kind, property name and attributes. | |
102 int Search(PropertyKind kind, Name* name, PropertyAttributes attributes, | |
103 int* out_insertion_index = NULL); | |
104 | |
105 // Search a non-property transition (like elements kind, observe or frozen | |
106 // transitions). | |
107 inline int SearchSpecial(Symbol* symbol, int* out_insertion_index = NULL) { | |
108 return SearchName(symbol, out_insertion_index); | |
109 } | |
110 | 145 |
111 static inline PropertyDetails GetTargetDetails(Name* name, Map* target); | 146 static inline PropertyDetails GetTargetDetails(Name* name, Map* target); |
112 | 147 |
113 // Allocates a TransitionArray. | 148 // Returns the number of transitions in the array. |
114 static Handle<TransitionArray> Allocate(Isolate* isolate, | 149 static int NumberOfTransitions(Object* raw_transitions); |
115 int number_of_transitions, | 150 // Required for templatized Search interface. |
116 int slack = 0); | 151 inline int number_of_entries() { return number_of_transitions(); } |
117 | 152 |
118 bool IsSimpleTransition() { | 153 inline void SetNumberOfTransitions(int number_of_transitions); |
119 return length() == kSimpleTransitionSize && | |
120 get(kSimpleTransitionTarget)->IsHeapObject() && | |
121 // The IntrusivePrototypeTransitionIterator may have set the map of the | |
122 // prototype transitions array to a smi. In that case, there are | |
123 // prototype transitions, hence this transition array is a full | |
124 // transition array. | |
125 HeapObject::cast(get(kSimpleTransitionTarget))->map()->IsMap() && | |
126 get(kSimpleTransitionTarget)->IsMap(); | |
127 } | |
128 | 154 |
129 bool IsFullTransitionArray() { | 155 static int Capacity(Object* raw_transitions); |
130 return length() > kFirstIndex || | |
131 (length() == kFirstIndex && !IsSimpleTransition()); | |
132 } | |
133 | 156 |
134 // Casting. | 157 // Casting. |
135 static inline TransitionArray* cast(Object* obj); | 158 static inline TransitionArray* cast(Object* obj); |
136 | 159 |
137 // Constant for denoting key was not found. | |
138 static const int kNotFound = -1; | |
139 | |
140 static const int kBackPointerStorageIndex = 0; | |
141 | |
142 // Layout for full transition arrays. | |
143 static const int kPrototypeTransitionsIndex = 1; | |
144 static const int kTransitionLengthIndex = 2; | |
145 static const int kFirstIndex = 3; | |
146 | |
147 // Layout for simple transition arrays. | |
148 static const int kSimpleTransitionTarget = 1; | |
149 static const int kSimpleTransitionSize = 2; | |
150 static const int kSimpleTransitionIndex = 0; | |
151 STATIC_ASSERT(kSimpleTransitionIndex != kNotFound); | |
152 | |
153 static const int kBackPointerStorageOffset = FixedArray::kHeaderSize; | |
154 | |
155 // Layout for the full transition array header. | |
156 static const int kPrototypeTransitionsOffset = kBackPointerStorageOffset + | |
157 kPointerSize; | |
158 static const int kTransitionLengthOffset = | |
159 kPrototypeTransitionsOffset + kPointerSize; | |
160 | |
161 // Layout of map transition entries in full transition arrays. | |
162 static const int kTransitionKey = 0; | |
163 static const int kTransitionTarget = 1; | |
164 static const int kTransitionSize = 2; | 160 static const int kTransitionSize = 2; |
| 161 static const int kProtoTransitionHeaderSize = 1; |
165 | 162 |
166 #if defined(DEBUG) || defined(OBJECT_PRINT) | 163 #if defined(DEBUG) || defined(OBJECT_PRINT) |
167 // For our gdb macros, we should perhaps change these in the future. | 164 // For our gdb macros, we should perhaps change these in the future. |
168 void Print(); | 165 void Print(); |
169 | 166 |
170 // Print all the transitions. | 167 // Print all the transitions. |
171 void PrintTransitions(std::ostream& os, bool print_header = true); // NOLINT | 168 static void PrintTransitions(std::ostream& os, Object* transitions, |
| 169 bool print_header = true); // NOLINT |
172 #endif | 170 #endif |
173 | 171 |
174 #ifdef DEBUG | 172 #ifdef DEBUG |
175 bool IsSortedNoDuplicates(int valid_entries = -1); | 173 bool IsSortedNoDuplicates(int valid_entries = -1); |
176 bool IsConsistentWithBackPointers(Map* current_map); | 174 static bool IsSortedNoDuplicates(Map* map); |
177 bool IsEqualTo(TransitionArray* other); | 175 static bool IsConsistentWithBackPointers(Map* map); |
178 | 176 |
179 // Returns true for a non-property transitions like elements kind, observed | 177 // Returns true for a non-property transitions like elements kind, observed |
180 // or frozen transitions. | 178 // or frozen transitions. |
181 static inline bool IsSpecialTransition(Name* name); | 179 static inline bool IsSpecialTransition(Name* name); |
182 #endif | 180 #endif |
183 | 181 |
| 182 // Constant for denoting key was not found. |
| 183 static const int kNotFound = -1; |
| 184 |
184 // The maximum number of transitions we want in a transition array (should | 185 // The maximum number of transitions we want in a transition array (should |
185 // fit in a page). | 186 // fit in a page). |
186 static const int kMaxNumberOfTransitions = 1024 + 512; | 187 static const int kMaxNumberOfTransitions = 1024 + 512; |
187 | 188 |
188 // Returns the fixed array length required to hold number_of_transitions | 189 private: |
189 // transitions. | 190 // Layout for full transition arrays. |
190 static int LengthFor(int number_of_transitions) { | 191 static const int kPrototypeTransitionsIndex = 0; |
191 return ToKeyIndex(number_of_transitions); | 192 static const int kTransitionLengthIndex = 1; |
192 } | 193 static const int kFirstIndex = 2; |
193 | 194 |
194 private: | 195 // Layout of map transition entries in full transition arrays. |
| 196 static const int kTransitionKey = 0; |
| 197 static const int kTransitionTarget = 1; |
| 198 STATIC_ASSERT(kTransitionSize == 2); |
| 199 |
| 200 static const int kProtoTransitionNumberOfEntriesOffset = 0; |
| 201 STATIC_ASSERT(kProtoTransitionHeaderSize == 1); |
| 202 |
195 // Conversion from transition number to array indices. | 203 // Conversion from transition number to array indices. |
196 static int ToKeyIndex(int transition_number) { | 204 static int ToKeyIndex(int transition_number) { |
197 return kFirstIndex + | 205 return kFirstIndex + |
198 (transition_number * kTransitionSize) + | 206 (transition_number * kTransitionSize) + |
199 kTransitionKey; | 207 kTransitionKey; |
200 } | 208 } |
201 | 209 |
202 static int ToTargetIndex(int transition_number) { | 210 static int ToTargetIndex(int transition_number) { |
203 return kFirstIndex + | 211 return kFirstIndex + |
204 (transition_number * kTransitionSize) + | 212 (transition_number * kTransitionSize) + |
205 kTransitionTarget; | 213 kTransitionTarget; |
206 } | 214 } |
207 | 215 |
208 static Handle<TransitionArray> AllocateSimple( | 216 // Returns the fixed array length required to hold number_of_transitions |
209 Isolate* isolate, Handle<Map> target); | 217 // transitions. |
| 218 static int LengthFor(int number_of_transitions) { |
| 219 return ToKeyIndex(number_of_transitions); |
| 220 } |
210 | 221 |
211 // Allocate a new transition array with a single entry. | 222 // Allocates a TransitionArray. |
212 static Handle<TransitionArray> NewWith(Handle<Map> map, | 223 static Handle<TransitionArray> Allocate(Isolate* isolate, |
213 Handle<Name> name, | 224 int number_of_transitions, |
214 Handle<Map> target, | 225 int slack = 0); |
215 SimpleTransitionFlag flag); | |
216 | 226 |
| 227 static void EnsureHasFullTransitionArray(Handle<Map> map); |
| 228 static void ReplaceTransitions(Handle<Map> map, Object* new_transitions); |
| 229 |
| 230 // Search a transition for a given kind, property name and attributes. |
| 231 int Search(PropertyKind kind, Name* name, PropertyAttributes attributes, |
| 232 int* out_insertion_index = NULL); |
| 233 |
| 234 // Search a non-property transition (like elements kind, observe or frozen |
| 235 // transitions). |
| 236 inline int SearchSpecial(Symbol* symbol, int* out_insertion_index = NULL) { |
| 237 return SearchName(symbol, out_insertion_index); |
| 238 } |
217 // Search a first transition for a given property name. | 239 // Search a first transition for a given property name. |
218 inline int SearchName(Name* name, int* out_insertion_index = NULL); | 240 inline int SearchName(Name* name, int* out_insertion_index = NULL); |
219 int SearchDetails(int transition, PropertyKind kind, | 241 int SearchDetails(int transition, PropertyKind kind, |
220 PropertyAttributes attributes, int* out_insertion_index); | 242 PropertyAttributes attributes, int* out_insertion_index); |
221 | 243 |
| 244 int number_of_transitions() { |
| 245 if (length() < kFirstIndex) return 0; |
| 246 return Smi::cast(get(kTransitionLengthIndex))->value(); |
| 247 } |
| 248 |
| 249 static inline PropertyDetails GetSimpleTargetDetails(Map* transition) { |
| 250 return transition->GetLastDescriptorDetails(); |
| 251 } |
| 252 |
| 253 static inline Name* GetSimpleTransitionKey(Map* transition) { |
| 254 int descriptor = transition->LastAdded(); |
| 255 return transition->instance_descriptors()->GetKey(descriptor); |
| 256 } |
| 257 |
| 258 static void TraverseTransitionTreeInternal(Map* map, |
| 259 TraverseCallback callback, |
| 260 void* data); |
| 261 |
| 262 static void SetPrototypeTransitions(Handle<Map> map, |
| 263 Handle<FixedArray> proto_transitions); |
| 264 |
222 // Compares two tuples <key, kind, attributes>, returns -1 if | 265 // Compares two tuples <key, kind, attributes>, returns -1 if |
223 // tuple1 is "less" than tuple2, 0 if tuple1 equal to tuple2 and 1 otherwise. | 266 // tuple1 is "less" than tuple2, 0 if tuple1 equal to tuple2 and 1 otherwise. |
224 static inline int CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1, | 267 static inline int CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1, |
225 PropertyAttributes attributes1, Name* key2, | 268 PropertyAttributes attributes1, Name* key2, |
226 uint32_t hash2, PropertyKind kind2, | 269 uint32_t hash2, PropertyKind kind2, |
227 PropertyAttributes attributes2); | 270 PropertyAttributes attributes2); |
228 | 271 |
229 // Compares keys, returns -1 if key1 is "less" than key2, | 272 // Compares keys, returns -1 if key1 is "less" than key2, |
230 // 0 if key1 equal to key2 and 1 otherwise. | 273 // 0 if key1 equal to key2 and 1 otherwise. |
231 static inline int CompareNames(Name* key1, uint32_t hash1, Name* key2, | 274 static inline int CompareNames(Name* key1, uint32_t hash1, Name* key2, |
232 uint32_t hash2); | 275 uint32_t hash2); |
233 | 276 |
234 // Compares two details, returns -1 if details1 is "less" than details2, | 277 // Compares two details, returns -1 if details1 is "less" than details2, |
235 // 0 if details1 equal to details2 and 1 otherwise. | 278 // 0 if details1 equal to details2 and 1 otherwise. |
236 static inline int CompareDetails(PropertyKind kind1, | 279 static inline int CompareDetails(PropertyKind kind1, |
237 PropertyAttributes attributes1, | 280 PropertyAttributes attributes1, |
238 PropertyKind kind2, | 281 PropertyKind kind2, |
239 PropertyAttributes attributes2); | 282 PropertyAttributes attributes2); |
240 | 283 |
241 inline void NoIncrementalWriteBarrierSet(int transition_number, | 284 inline void NoIncrementalWriteBarrierSet(int transition_number, |
242 Name* key, | 285 Name* key, |
243 Map* target); | 286 Map* target); |
244 | 287 |
245 // Copy a single transition from the origin array. | 288 // Copy a single transition from the origin array. |
246 inline void NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin, | 289 inline void NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin, |
247 int origin_transition, | 290 int origin_transition, |
248 int target_transition); | 291 int target_transition); |
249 | 292 |
| 293 #ifdef DEBUG |
| 294 static void CheckNewTransitionsAreConsistent(Handle<Map> map, |
| 295 TransitionArray* old_transitions, |
| 296 Object* transitions); |
| 297 #endif |
| 298 |
250 DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray); | 299 DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray); |
251 }; | 300 }; |
252 | 301 |
253 | 302 |
254 } } // namespace v8::internal | 303 } } // namespace v8::internal |
255 | 304 |
256 #endif // V8_TRANSITIONS_H_ | 305 #endif // V8_TRANSITIONS_H_ |
OLD | NEW |