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 #include "src/v8.h" | 5 #include "src/v8.h" |
6 | 6 |
7 #include "src/objects.h" | 7 #include "src/objects.h" |
8 #include "src/transitions-inl.h" | 8 #include "src/transitions-inl.h" |
9 #include "src/utils.h" | 9 #include "src/utils.h" |
10 | 10 |
11 namespace v8 { | 11 namespace v8 { |
12 namespace internal { | 12 namespace internal { |
13 | 13 |
14 | 14 |
15 Handle<TransitionArray> TransitionArray::Allocate(Isolate* isolate, | 15 // static |
16 int number_of_transitions, | 16 void TransitionArray::Insert(Handle<Map> map, Handle<Name> name, |
17 int slack) { | 17 Handle<Map> target, SimpleTransitionFlag flag) { |
18 Handle<FixedArray> array = isolate->factory()->NewFixedArray( | 18 Isolate* isolate = map->GetIsolate(); |
19 LengthFor(number_of_transitions + slack)); | 19 target->SetBackPointer(*map); |
20 array->set(kPrototypeTransitionsIndex, Smi::FromInt(0)); | |
21 array->set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions)); | |
22 return Handle<TransitionArray>::cast(array); | |
23 } | |
24 | 20 |
25 | 21 // If the map doesn't have any transitions at all yet, install the new one. |
26 Handle<TransitionArray> TransitionArray::AllocateSimple(Isolate* isolate, | 22 if (CanStoreSimpleTransition(map->raw_transitions())) { |
27 Handle<Map> target) { | 23 if (flag == SIMPLE_PROPERTY_TRANSITION) { |
28 Handle<FixedArray> array = | 24 Handle<WeakCell> cell = Map::WeakCellForMap(target); |
29 isolate->factory()->NewFixedArray(kSimpleTransitionSize); | 25 map->set_raw_transitions(*cell); |
30 array->set(kSimpleTransitionTarget, *target); | 26 return; |
31 return Handle<TransitionArray>::cast(array); | 27 } |
32 } | 28 // If the flag requires a full TransitionArray, allocate one. |
33 | 29 Handle<TransitionArray> result = Allocate(isolate, 0, 1); |
34 | 30 map->set_raw_transitions(*result); |
35 void TransitionArray::NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin, | |
36 int origin_transition, | |
37 int target_transition) { | |
38 NoIncrementalWriteBarrierSet(target_transition, | |
39 origin->GetKey(origin_transition), | |
40 origin->GetTarget(origin_transition)); | |
41 } | |
42 | |
43 | |
44 Handle<TransitionArray> TransitionArray::NewWith(Handle<Map> map, | |
45 Handle<Name> name, | |
46 Handle<Map> target, | |
47 SimpleTransitionFlag flag) { | |
48 Handle<TransitionArray> result; | |
49 Isolate* isolate = name->GetIsolate(); | |
50 | |
51 if (flag == SIMPLE_PROPERTY_TRANSITION) { | |
52 result = AllocateSimple(isolate, target); | |
53 } else { | |
54 result = Allocate(isolate, 1); | |
55 result->NoIncrementalWriteBarrierSet(0, *name, *target); | |
56 } | |
57 result->set_back_pointer_storage(map->GetBackPointer()); | |
58 return result; | |
59 } | |
60 | |
61 | |
62 Handle<TransitionArray> TransitionArray::ExtendToFullTransitionArray( | |
63 Handle<Map> containing_map) { | |
64 DCHECK(!containing_map->transitions()->IsFullTransitionArray()); | |
65 int nof = containing_map->transitions()->number_of_transitions(); | |
66 | |
67 // A transition array may shrink during GC. | |
68 Handle<TransitionArray> result = Allocate(containing_map->GetIsolate(), nof); | |
69 DisallowHeapAllocation no_gc; | |
70 int new_nof = containing_map->transitions()->number_of_transitions(); | |
71 if (new_nof != nof) { | |
72 DCHECK(new_nof == 0); | |
73 result->Shrink(ToKeyIndex(0)); | |
74 result->SetNumberOfTransitions(0); | |
75 } else if (nof == 1) { | |
76 result->NoIncrementalWriteBarrierCopyFrom( | |
77 containing_map->transitions(), kSimpleTransitionIndex, 0); | |
78 } | 31 } |
79 | 32 |
80 result->set_back_pointer_storage( | 33 bool is_special_transition = flag == SPECIAL_TRANSITION; |
81 containing_map->transitions()->back_pointer_storage()); | 34 // If the map has a simple transition, check if it should be overwritten. |
82 return result; | 35 if (IsSimpleTransition(map->raw_transitions())) { |
83 } | 36 Map* old_target = GetSimpleTransition(map->raw_transitions()); |
84 | 37 Name* key = GetSimpleTransitionKey(old_target); |
85 | 38 PropertyDetails old_details = GetSimpleTargetDetails(old_target); |
86 Handle<TransitionArray> TransitionArray::Insert(Handle<Map> map, | 39 PropertyDetails new_details = is_special_transition |
87 Handle<Name> name, | 40 ? PropertyDetails(NONE, DATA, 0) |
88 Handle<Map> target, | 41 : GetTargetDetails(*name, *target); |
89 SimpleTransitionFlag flag) { | 42 if (flag == SIMPLE_PROPERTY_TRANSITION && key->Equals(*name) && |
90 if (!map->HasTransitionArray()) { | 43 old_details.kind() == new_details.kind() && |
91 return TransitionArray::NewWith(map, name, target, flag); | 44 old_details.attributes() == new_details.attributes()) { |
| 45 Handle<WeakCell> cell = Map::WeakCellForMap(target); |
| 46 map->set_raw_transitions(*cell); |
| 47 return; |
| 48 } |
| 49 // Otherwise allocate a full TransitionArray with slack for a new entry. |
| 50 Handle<TransitionArray> result = Allocate(isolate, 1, 1); |
| 51 // Re-read existing data; the allocation might have caused it to be cleared. |
| 52 if (IsSimpleTransition(map->raw_transitions())) { |
| 53 old_target = GetSimpleTransition(map->raw_transitions()); |
| 54 result->NoIncrementalWriteBarrierSet( |
| 55 0, GetSimpleTransitionKey(old_target), old_target); |
| 56 } else { |
| 57 result->SetNumberOfTransitions(0); |
| 58 } |
| 59 map->set_raw_transitions(*result); |
92 } | 60 } |
93 | 61 |
94 int number_of_transitions = map->transitions()->number_of_transitions(); | 62 // At this point, we know that the map has a full TransitionArray. |
95 int new_nof = number_of_transitions; | 63 DCHECK(IsFullTransitionArray(map->raw_transitions())); |
96 | 64 |
97 bool is_special_transition = flag == SPECIAL_TRANSITION; | 65 int number_of_transitions = 0; |
| 66 int new_nof = 0; |
| 67 int insertion_index = kNotFound; |
98 DCHECK_EQ(is_special_transition, IsSpecialTransition(*name)); | 68 DCHECK_EQ(is_special_transition, IsSpecialTransition(*name)); |
99 PropertyDetails details = is_special_transition | 69 PropertyDetails details = is_special_transition |
100 ? PropertyDetails(NONE, DATA, 0) | 70 ? PropertyDetails(NONE, DATA, 0) |
101 : GetTargetDetails(*name, *target); | 71 : GetTargetDetails(*name, *target); |
102 | 72 |
103 int insertion_index = kNotFound; | 73 { |
104 int index = | 74 DisallowHeapAllocation no_gc; |
105 is_special_transition | 75 TransitionArray* array = TransitionArray::cast(map->raw_transitions()); |
106 ? map->transitions()->SearchSpecial(Symbol::cast(*name), | 76 number_of_transitions = array->number_of_transitions(); |
107 &insertion_index) | 77 new_nof = number_of_transitions; |
108 : map->transitions()->Search(details.kind(), *name, | |
109 details.attributes(), &insertion_index); | |
110 if (index == kNotFound) { | |
111 ++new_nof; | |
112 } else { | |
113 insertion_index = index; | |
114 } | |
115 DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions); | |
116 | 78 |
117 CHECK(new_nof <= kMaxNumberOfTransitions); | 79 int index = |
118 | 80 is_special_transition |
119 if (new_nof <= map->transitions()->number_of_transitions_storage()) { | 81 ? array->SearchSpecial(Symbol::cast(*name), &insertion_index) |
120 DisallowHeapAllocation no_gc; | 82 : array->Search(details.kind(), *name, details.attributes(), |
121 TransitionArray* array = map->transitions(); | 83 &insertion_index); |
122 | 84 // If an existing entry was found, overwrite it and return. |
123 if (index != kNotFound) { | 85 if (index != kNotFound) { |
124 array->SetTarget(index, *target); | 86 array->SetTarget(index, *target); |
125 return handle(array); | 87 return; |
126 } | 88 } |
127 | 89 |
128 array->SetNumberOfTransitions(new_nof); | 90 ++new_nof; |
129 for (index = number_of_transitions; index > insertion_index; --index) { | 91 CHECK(new_nof <= kMaxNumberOfTransitions); |
130 Name* key = array->GetKey(index - 1); | 92 DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions); |
131 array->SetKey(index, key); | 93 |
132 array->SetTarget(index, array->GetTarget(index - 1)); | 94 // If there is enough capacity, insert new entry into the existing array. |
| 95 if (new_nof <= Capacity(array)) { |
| 96 array->SetNumberOfTransitions(new_nof); |
| 97 for (index = number_of_transitions; index > insertion_index; --index) { |
| 98 array->SetKey(index, array->GetKey(index - 1)); |
| 99 array->SetTarget(index, array->GetTarget(index - 1)); |
| 100 } |
| 101 array->SetKey(index, *name); |
| 102 array->SetTarget(index, *target); |
| 103 SLOW_DCHECK(array->IsSortedNoDuplicates()); |
| 104 return; |
133 } | 105 } |
134 array->SetKey(index, *name); | |
135 array->SetTarget(index, *target); | |
136 SLOW_DCHECK(array->IsSortedNoDuplicates()); | |
137 return handle(array); | |
138 } | 106 } |
139 | 107 |
| 108 // We're gonna need a bigger TransitionArray. |
140 Handle<TransitionArray> result = Allocate( | 109 Handle<TransitionArray> result = Allocate( |
141 map->GetIsolate(), new_nof, | 110 map->GetIsolate(), new_nof, |
142 Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions)); | 111 Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions)); |
143 | 112 |
144 // The map's transition array may grown smaller during the allocation above as | 113 // The map's transition array may have shrunk during the allocation above as |
145 // it was weakly traversed, though it is guaranteed not to disappear. Trim the | 114 // it was weakly traversed, though it is guaranteed not to disappear. Trim the |
146 // result copy if needed, and recompute variables. | 115 // result copy if needed, and recompute variables. |
147 DCHECK(map->HasTransitionArray()); | 116 DCHECK(IsFullTransitionArray(map->raw_transitions())); |
148 DisallowHeapAllocation no_gc; | 117 DisallowHeapAllocation no_gc; |
149 TransitionArray* array = map->transitions(); | 118 TransitionArray* array = TransitionArray::cast(map->raw_transitions()); |
150 if (array->number_of_transitions() != number_of_transitions) { | 119 if (array->number_of_transitions() != number_of_transitions) { |
151 DCHECK(array->number_of_transitions() < number_of_transitions); | 120 DCHECK(array->number_of_transitions() < number_of_transitions); |
152 | 121 |
153 number_of_transitions = array->number_of_transitions(); | 122 number_of_transitions = array->number_of_transitions(); |
154 new_nof = number_of_transitions; | 123 new_nof = number_of_transitions; |
155 | 124 |
156 insertion_index = kNotFound; | 125 insertion_index = kNotFound; |
157 index = is_special_transition ? map->transitions()->SearchSpecial( | 126 int index = |
158 Symbol::cast(*name), &insertion_index) | 127 is_special_transition |
159 : map->transitions()->Search( | 128 ? array->SearchSpecial(Symbol::cast(*name), &insertion_index) |
160 details.kind(), *name, | 129 : array->Search(details.kind(), *name, details.attributes(), |
161 details.attributes(), &insertion_index); | 130 &insertion_index); |
162 if (index == kNotFound) { | 131 if (index == kNotFound) { |
163 ++new_nof; | 132 ++new_nof; |
164 } else { | 133 } else { |
165 insertion_index = index; | 134 insertion_index = index; |
166 } | 135 } |
167 DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions); | 136 DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions); |
168 | 137 |
169 result->Shrink(ToKeyIndex(new_nof)); | 138 result->Shrink(ToKeyIndex(new_nof)); |
170 result->SetNumberOfTransitions(new_nof); | 139 result->SetNumberOfTransitions(new_nof); |
171 } | 140 } |
172 | 141 |
173 if (array->HasPrototypeTransitions()) { | 142 if (array->HasPrototypeTransitions()) { |
174 result->SetPrototypeTransitions(array->GetPrototypeTransitions()); | 143 result->SetPrototypeTransitions(array->GetPrototypeTransitions()); |
175 } | 144 } |
176 | 145 |
177 DCHECK_NE(kNotFound, insertion_index); | 146 DCHECK_NE(kNotFound, insertion_index); |
178 for (int i = 0; i < insertion_index; ++i) { | 147 for (int i = 0; i < insertion_index; ++i) { |
179 result->NoIncrementalWriteBarrierCopyFrom(array, i, i); | 148 result->NoIncrementalWriteBarrierCopyFrom(array, i, i); |
180 } | 149 } |
181 result->NoIncrementalWriteBarrierSet(insertion_index, *name, *target); | 150 result->NoIncrementalWriteBarrierSet(insertion_index, *name, *target); |
182 for (int i = insertion_index; i < number_of_transitions; ++i) { | 151 for (int i = insertion_index; i < number_of_transitions; ++i) { |
183 result->NoIncrementalWriteBarrierCopyFrom(array, i, i + 1); | 152 result->NoIncrementalWriteBarrierCopyFrom(array, i, i + 1); |
184 } | 153 } |
185 | 154 |
186 result->set_back_pointer_storage(array->back_pointer_storage()); | |
187 SLOW_DCHECK(result->IsSortedNoDuplicates()); | 155 SLOW_DCHECK(result->IsSortedNoDuplicates()); |
188 return result; | 156 map->set_raw_transitions(*result); |
189 } | 157 } |
190 | 158 |
| 159 |
| 160 // static |
| 161 Map* TransitionArray::SearchTransition(Map* map, PropertyKind kind, Name* name, |
| 162 PropertyAttributes attributes) { |
| 163 Object* raw_transitions = map->raw_transitions(); |
| 164 if (IsSimpleTransition(raw_transitions)) { |
| 165 Map* target = GetSimpleTransition(raw_transitions); |
| 166 Name* key = GetSimpleTransitionKey(target); |
| 167 if (!key->Equals(name)) return NULL; |
| 168 PropertyDetails details = GetSimpleTargetDetails(target); |
| 169 if (details.attributes() != attributes) return NULL; |
| 170 if (details.kind() != kind) return NULL; |
| 171 return target; |
| 172 } |
| 173 if (IsFullTransitionArray(raw_transitions)) { |
| 174 TransitionArray* transitions = TransitionArray::cast(raw_transitions); |
| 175 int transition = transitions->Search(kind, name, attributes); |
| 176 if (transition == kNotFound) return NULL; |
| 177 return transitions->GetTarget(transition); |
| 178 } |
| 179 return NULL; |
| 180 } |
| 181 |
| 182 |
| 183 // static |
| 184 Map* TransitionArray::SearchSpecial(Map* map, Symbol* name) { |
| 185 Object* raw_transitions = map->raw_transitions(); |
| 186 if (IsFullTransitionArray(raw_transitions)) { |
| 187 TransitionArray* transitions = TransitionArray::cast(raw_transitions); |
| 188 int transition = transitions->SearchSpecial(name); |
| 189 if (transition == kNotFound) return NULL; |
| 190 return transitions->GetTarget(transition); |
| 191 } |
| 192 return NULL; |
| 193 } |
| 194 |
| 195 |
| 196 // static |
| 197 Handle<Map> TransitionArray::FindTransitionToField(Handle<Map> map, |
| 198 Handle<Name> name) { |
| 199 DisallowHeapAllocation no_gc; |
| 200 Map* target = SearchTransition(*map, kData, *name, NONE); |
| 201 if (target == NULL) return Handle<Map>::null(); |
| 202 PropertyDetails details = target->GetLastDescriptorDetails(); |
| 203 DCHECK_EQ(NONE, details.attributes()); |
| 204 if (details.type() != DATA) return Handle<Map>::null(); |
| 205 return Handle<Map>(target); |
| 206 } |
| 207 |
| 208 |
| 209 // static |
| 210 Handle<String> TransitionArray::ExpectedTransitionKey(Handle<Map> map) { |
| 211 DisallowHeapAllocation no_gc; |
| 212 Object* raw_transition = map->raw_transitions(); |
| 213 if (!IsSimpleTransition(raw_transition)) return Handle<String>::null(); |
| 214 Map* target = GetSimpleTransition(raw_transition); |
| 215 PropertyDetails details = GetSimpleTargetDetails(target); |
| 216 if (details.type() != DATA) return Handle<String>::null(); |
| 217 if (details.attributes() != NONE) return Handle<String>::null(); |
| 218 Name* name = GetSimpleTransitionKey(target); |
| 219 if (!name->IsString()) return Handle<String>::null(); |
| 220 return Handle<String>(String::cast(name)); |
| 221 } |
| 222 |
| 223 |
| 224 // static |
| 225 bool TransitionArray::CanHaveMoreTransitions(Handle<Map> map) { |
| 226 Object* raw_transitions = map->raw_transitions(); |
| 227 if (IsFullTransitionArray(raw_transitions)) { |
| 228 TransitionArray* transitions = TransitionArray::cast(raw_transitions); |
| 229 return transitions->number_of_transitions() < kMaxNumberOfTransitions; |
| 230 } |
| 231 return true; |
| 232 } |
| 233 |
| 234 |
| 235 // static |
| 236 Handle<Map> TransitionArray::PutPrototypeTransition(Handle<Map> map, |
| 237 Handle<Object> prototype, |
| 238 Handle<Map> target_map) { |
| 239 DCHECK(HeapObject::cast(*prototype)->map()->IsMap()); |
| 240 // Don't cache prototype transition if this map is either shared, or a map of |
| 241 // a prototype. |
| 242 if (map->is_prototype_map()) return map; |
| 243 if (map->is_dictionary_map() || !FLAG_cache_prototype_transitions) return map; |
| 244 |
| 245 const int header = kProtoTransitionHeaderSize; |
| 246 |
| 247 Handle<FixedArray> cache(GetPrototypeTransitions(*map)); |
| 248 int capacity = cache->length() - header; |
| 249 int transitions = NumberOfPrototypeTransitions(*cache) + 1; |
| 250 |
| 251 if (transitions > capacity) { |
| 252 // Grow array by factor 2 up to MaxCachedPrototypeTransitions. |
| 253 int new_capacity = Min(kMaxCachedPrototypeTransitions, transitions * 2); |
| 254 if (new_capacity == capacity) return map; |
| 255 |
| 256 cache = FixedArray::CopySize(cache, header + new_capacity); |
| 257 if (capacity < 0) { |
| 258 // There was no prototype transitions array before, so the size |
| 259 // couldn't be copied. Initialize it explicitly. |
| 260 SetNumberOfPrototypeTransitions(*cache, 0); |
| 261 } |
| 262 |
| 263 SetPrototypeTransitions(map, cache); |
| 264 } |
| 265 |
| 266 // Reload number of transitions as GC might shrink them. |
| 267 int last = NumberOfPrototypeTransitions(*cache); |
| 268 int entry = header + last; |
| 269 |
| 270 cache->set(entry, *target_map); |
| 271 SetNumberOfPrototypeTransitions(*cache, last + 1); |
| 272 |
| 273 return map; |
| 274 } |
| 275 |
| 276 |
| 277 // static |
| 278 Handle<Map> TransitionArray::GetPrototypeTransition(Handle<Map> map, |
| 279 Handle<Object> prototype) { |
| 280 DisallowHeapAllocation no_gc; |
| 281 FixedArray* cache = GetPrototypeTransitions(*map); |
| 282 int number_of_transitions = NumberOfPrototypeTransitions(cache); |
| 283 for (int i = 0; i < number_of_transitions; i++) { |
| 284 Map* target = Map::cast(cache->get(kProtoTransitionHeaderSize + i)); |
| 285 if (target->prototype() == *prototype) return handle(target); |
| 286 } |
| 287 return Handle<Map>(); |
| 288 } |
| 289 |
| 290 |
| 291 // static |
| 292 FixedArray* TransitionArray::GetPrototypeTransitions(Map* map) { |
| 293 Object* raw_transitions = map->raw_transitions(); |
| 294 Heap* heap = map->GetHeap(); |
| 295 if (!IsFullTransitionArray(raw_transitions)) { |
| 296 return heap->empty_fixed_array(); |
| 297 } |
| 298 TransitionArray* transitions = TransitionArray::cast(raw_transitions); |
| 299 if (!transitions->HasPrototypeTransitions()) { |
| 300 return heap->empty_fixed_array(); |
| 301 } |
| 302 return transitions->GetPrototypeTransitions(); |
| 303 } |
| 304 |
| 305 |
| 306 // static |
| 307 void TransitionArray::SetNumberOfPrototypeTransitions( |
| 308 FixedArray* proto_transitions, int value) { |
| 309 DCHECK(proto_transitions->length() != 0); |
| 310 proto_transitions->set(kProtoTransitionNumberOfEntriesOffset, |
| 311 Smi::FromInt(value)); |
| 312 } |
| 313 |
| 314 |
| 315 // static |
| 316 int TransitionArray::NumberOfTransitions(Object* raw_transitions) { |
| 317 if (CanStoreSimpleTransition(raw_transitions)) return 0; |
| 318 if (IsSimpleTransition(raw_transitions)) return 1; |
| 319 DCHECK(IsFullTransitionArray(raw_transitions)); |
| 320 return TransitionArray::cast(raw_transitions)->number_of_transitions(); |
| 321 } |
| 322 |
| 323 |
| 324 // static |
| 325 int TransitionArray::Capacity(Object* raw_transitions) { |
| 326 if (!IsFullTransitionArray(raw_transitions)) return 1; |
| 327 TransitionArray* t = TransitionArray::cast(raw_transitions); |
| 328 if (t->length() <= kFirstIndex) return 0; |
| 329 return (t->length() - kFirstIndex) / kTransitionSize; |
| 330 } |
| 331 |
| 332 |
| 333 // Private static helper functions. |
| 334 |
| 335 Handle<TransitionArray> TransitionArray::Allocate(Isolate* isolate, |
| 336 int number_of_transitions, |
| 337 int slack) { |
| 338 Handle<FixedArray> array = isolate->factory()->NewFixedArray( |
| 339 LengthFor(number_of_transitions + slack)); |
| 340 array->set(kPrototypeTransitionsIndex, Smi::FromInt(0)); |
| 341 array->set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions)); |
| 342 return Handle<TransitionArray>::cast(array); |
| 343 } |
| 344 |
| 345 |
| 346 void TransitionArray::NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin, |
| 347 int origin_transition, |
| 348 int target_transition) { |
| 349 NoIncrementalWriteBarrierSet(target_transition, |
| 350 origin->GetKey(origin_transition), |
| 351 origin->GetTarget(origin_transition)); |
| 352 } |
| 353 |
| 354 |
| 355 static void ZapTransitionArray(TransitionArray* transitions) { |
| 356 MemsetPointer(transitions->data_start(), |
| 357 transitions->GetHeap()->the_hole_value(), |
| 358 transitions->length()); |
| 359 } |
| 360 |
| 361 |
| 362 void TransitionArray::ReplaceTransitions(Handle<Map> map, |
| 363 Object* new_transitions) { |
| 364 Object* raw_transitions = map->raw_transitions(); |
| 365 if (IsFullTransitionArray(raw_transitions)) { |
| 366 TransitionArray* old_transitions = TransitionArray::cast(raw_transitions); |
| 367 #ifdef DEBUG |
| 368 CheckNewTransitionsAreConsistent(map, old_transitions, new_transitions); |
| 369 DCHECK(old_transitions != new_transitions); |
| 370 #endif |
| 371 // Transition arrays are not shared. When one is replaced, it should not |
| 372 // keep referenced objects alive, so we zap it. |
| 373 // When there is another reference to the array somewhere (e.g. a handle), |
| 374 // not zapping turns from a waste of memory into a source of crashes. |
| 375 ZapTransitionArray(old_transitions); |
| 376 } |
| 377 map->set_raw_transitions(new_transitions); |
| 378 } |
| 379 |
| 380 |
| 381 static void ZapPrototypeTransitions(Object* raw_transitions) { |
| 382 DCHECK(TransitionArray::IsFullTransitionArray(raw_transitions)); |
| 383 TransitionArray* transitions = TransitionArray::cast(raw_transitions); |
| 384 if (!transitions->HasPrototypeTransitions()) return; |
| 385 FixedArray* proto_transitions = transitions->GetPrototypeTransitions(); |
| 386 MemsetPointer(proto_transitions->data_start(), |
| 387 proto_transitions->GetHeap()->the_hole_value(), |
| 388 proto_transitions->length()); |
| 389 } |
| 390 |
| 391 |
| 392 void TransitionArray::SetPrototypeTransitions( |
| 393 Handle<Map> map, Handle<FixedArray> proto_transitions) { |
| 394 EnsureHasFullTransitionArray(map); |
| 395 if (Heap::ShouldZapGarbage()) { |
| 396 Object* raw_transitions = map->raw_transitions(); |
| 397 DCHECK(raw_transitions != *proto_transitions); |
| 398 ZapPrototypeTransitions(raw_transitions); |
| 399 } |
| 400 TransitionArray* transitions = TransitionArray::cast(map->raw_transitions()); |
| 401 transitions->SetPrototypeTransitions(*proto_transitions); |
| 402 } |
| 403 |
| 404 |
| 405 void TransitionArray::EnsureHasFullTransitionArray(Handle<Map> map) { |
| 406 Object* raw_transitions = map->raw_transitions(); |
| 407 if (IsFullTransitionArray(raw_transitions)) return; |
| 408 int nof = IsSimpleTransition(raw_transitions) ? 1 : 0; |
| 409 Handle<TransitionArray> result = Allocate(map->GetIsolate(), nof); |
| 410 DisallowHeapAllocation no_gc; |
| 411 // Reload pointer after the allocation that just happened. |
| 412 raw_transitions = map->raw_transitions(); |
| 413 int new_nof = IsSimpleTransition(raw_transitions) ? 1 : 0; |
| 414 if (new_nof != nof) { |
| 415 DCHECK(new_nof == 0); |
| 416 result->Shrink(ToKeyIndex(0)); |
| 417 result->SetNumberOfTransitions(0); |
| 418 } else if (nof == 1) { |
| 419 Map* target = GetSimpleTransition(raw_transitions); |
| 420 Name* key = GetSimpleTransitionKey(target); |
| 421 result->NoIncrementalWriteBarrierSet(0, key, target); |
| 422 } |
| 423 ReplaceTransitions(map, *result); |
| 424 } |
| 425 |
| 426 |
| 427 void TransitionArray::TraverseTransitionTreeInternal(Map* map, |
| 428 TraverseCallback callback, |
| 429 void* data) { |
| 430 Object* raw_transitions = map->raw_transitions(); |
| 431 if (IsFullTransitionArray(raw_transitions)) { |
| 432 TransitionArray* transitions = TransitionArray::cast(raw_transitions); |
| 433 if (transitions->HasPrototypeTransitions()) { |
| 434 FixedArray* proto_trans = transitions->GetPrototypeTransitions(); |
| 435 for (int i = 0; i < NumberOfPrototypeTransitions(proto_trans); ++i) { |
| 436 int index = TransitionArray::kProtoTransitionHeaderSize + i; |
| 437 TraverseTransitionTreeInternal(Map::cast(proto_trans->get(index)), |
| 438 callback, data); |
| 439 } |
| 440 } |
| 441 for (int i = 0; i < transitions->number_of_transitions(); ++i) { |
| 442 TraverseTransitionTreeInternal(transitions->GetTarget(i), callback, data); |
| 443 } |
| 444 } else if (IsSimpleTransition(raw_transitions)) { |
| 445 TraverseTransitionTreeInternal(GetSimpleTransition(raw_transitions), |
| 446 callback, data); |
| 447 } |
| 448 callback(map, data); |
| 449 } |
| 450 |
| 451 |
| 452 #ifdef DEBUG |
| 453 void TransitionArray::CheckNewTransitionsAreConsistent( |
| 454 Handle<Map> map, TransitionArray* old_transitions, Object* transitions) { |
| 455 // This function only handles full transition arrays. |
| 456 DCHECK(IsFullTransitionArray(transitions)); |
| 457 TransitionArray* new_transitions = TransitionArray::cast(transitions); |
| 458 for (int i = 0; i < old_transitions->number_of_transitions(); i++) { |
| 459 Map* target = old_transitions->GetTarget(i); |
| 460 if (target->instance_descriptors() == map->instance_descriptors()) { |
| 461 Name* key = old_transitions->GetKey(i); |
| 462 int new_target_index; |
| 463 if (TransitionArray::IsSpecialTransition(key)) { |
| 464 new_target_index = new_transitions->SearchSpecial(Symbol::cast(key)); |
| 465 } else { |
| 466 PropertyDetails details = |
| 467 TransitionArray::GetTargetDetails(key, target); |
| 468 new_target_index = |
| 469 new_transitions->Search(details.kind(), key, details.attributes()); |
| 470 } |
| 471 DCHECK_NE(TransitionArray::kNotFound, new_target_index); |
| 472 DCHECK_EQ(target, new_transitions->GetTarget(new_target_index)); |
| 473 } |
| 474 } |
| 475 } |
| 476 #endif |
| 477 |
| 478 |
| 479 // Private non-static helper functions (operating on full transition arrays). |
191 | 480 |
192 int TransitionArray::SearchDetails(int transition, PropertyKind kind, | 481 int TransitionArray::SearchDetails(int transition, PropertyKind kind, |
193 PropertyAttributes attributes, | 482 PropertyAttributes attributes, |
194 int* out_insertion_index) { | 483 int* out_insertion_index) { |
195 int nof_transitions = number_of_transitions(); | 484 int nof_transitions = number_of_transitions(); |
196 DCHECK(transition < nof_transitions); | 485 DCHECK(transition < nof_transitions); |
197 Name* key = GetKey(transition); | 486 Name* key = GetKey(transition); |
198 for (; transition < nof_transitions && GetKey(transition) == key; | 487 for (; transition < nof_transitions && GetKey(transition) == key; |
199 transition++) { | 488 transition++) { |
200 Map* target = GetTarget(transition); | 489 Map* target = GetTarget(transition); |
(...skipping 15 matching lines...) Expand all Loading... |
216 int TransitionArray::Search(PropertyKind kind, Name* name, | 505 int TransitionArray::Search(PropertyKind kind, Name* name, |
217 PropertyAttributes attributes, | 506 PropertyAttributes attributes, |
218 int* out_insertion_index) { | 507 int* out_insertion_index) { |
219 int transition = SearchName(name, out_insertion_index); | 508 int transition = SearchName(name, out_insertion_index); |
220 if (transition == kNotFound) { | 509 if (transition == kNotFound) { |
221 return kNotFound; | 510 return kNotFound; |
222 } | 511 } |
223 return SearchDetails(transition, kind, attributes, out_insertion_index); | 512 return SearchDetails(transition, kind, attributes, out_insertion_index); |
224 } | 513 } |
225 } } // namespace v8::internal | 514 } } // namespace v8::internal |
OLD | NEW |