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 #include "src/objects/transitions.h"
6
7 #include "src/objects/objects-inl.h"
8 #include "src/objects/transitions-inl.h"
9 #include "src/utils/utils.h"
10
11 namespace v8 {
12 namespace internal {
13
GetSimpleTransition()14 Map TransitionsAccessor::GetSimpleTransition() {
15 switch (encoding()) {
16 case kWeakRef:
17 return Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
18 default:
19 return Map();
20 }
21 }
22
HasSimpleTransitionTo(Map map)23 bool TransitionsAccessor::HasSimpleTransitionTo(Map map) {
24 switch (encoding()) {
25 case kWeakRef:
26 return raw_transitions_->GetHeapObjectAssumeWeak() == map;
27 case kPrototypeInfo:
28 case kUninitialized:
29 case kMigrationTarget:
30 case kFullTransitionArray:
31 return false;
32 }
33 UNREACHABLE();
34 }
35
Insert(Handle<Name> name,Handle<Map> target,SimpleTransitionFlag flag)36 void TransitionsAccessor::Insert(Handle<Name> name, Handle<Map> target,
37 SimpleTransitionFlag flag) {
38 DCHECK(!concurrent_access_);
39 DCHECK(!map_handle_.is_null());
40 DCHECK_NE(kPrototypeInfo, encoding());
41 target->SetBackPointer(map_);
42
43 // If the map doesn't have any transitions at all yet, install the new one.
44 if (encoding() == kUninitialized || encoding() == kMigrationTarget) {
45 if (flag == SIMPLE_PROPERTY_TRANSITION) {
46 ReplaceTransitions(HeapObjectReference::Weak(*target));
47 return;
48 }
49 // If the flag requires a full TransitionArray, allocate one.
50 Handle<TransitionArray> result =
51 isolate_->factory()->NewTransitionArray(1, 0);
52 result->Set(0, *name, HeapObjectReference::Weak(*target));
53 ReplaceTransitions(MaybeObject::FromObject(*result));
54 Reload();
55 DCHECK_EQ(kFullTransitionArray, encoding());
56 return;
57 }
58
59 if (encoding() == kWeakRef) {
60 Map simple_transition = GetSimpleTransition();
61 DCHECK(!simple_transition.is_null());
62
63 if (flag == SIMPLE_PROPERTY_TRANSITION) {
64 Name key = GetSimpleTransitionKey(simple_transition);
65 PropertyDetails old_details = GetSimpleTargetDetails(simple_transition);
66 PropertyDetails new_details = GetTargetDetails(*name, *target);
67 if (key.Equals(*name) && old_details.kind() == new_details.kind() &&
68 old_details.attributes() == new_details.attributes()) {
69 ReplaceTransitions(HeapObjectReference::Weak(*target));
70 return;
71 }
72 }
73
74 // Otherwise allocate a full TransitionArray with slack for a new entry.
75 Handle<Map> map(simple_transition, isolate_);
76 Handle<TransitionArray> result =
77 isolate_->factory()->NewTransitionArray(1, 1);
78 // Reload state; allocations might have caused it to be cleared.
79 Reload();
80 simple_transition = GetSimpleTransition();
81 if (simple_transition.is_null()) {
82 result->Set(0, *name, HeapObjectReference::Weak(*target));
83 ReplaceTransitions(MaybeObject::FromObject(*result));
84 Reload();
85 DCHECK_EQ(kFullTransitionArray, encoding());
86 return;
87 }
88
89 // Insert the original transition in index 0.
90 result->Set(0, GetSimpleTransitionKey(simple_transition),
91 HeapObjectReference::Weak(simple_transition));
92
93 // Search for the correct index to insert the new transition.
94 int insertion_index;
95 int index;
96 if (flag == SPECIAL_TRANSITION) {
97 index = result->SearchSpecial(Symbol::cast(*name), &insertion_index);
98 } else {
99 PropertyDetails details = GetTargetDetails(*name, *target);
100 index = result->Search(details.kind(), *name, details.attributes(),
101 &insertion_index);
102 }
103 DCHECK_EQ(index, kNotFound);
104 USE(index);
105
106 result->SetNumberOfTransitions(2);
107 if (insertion_index == 0) {
108 // If the new transition will be inserted in index 0, move the original
109 // transition to index 1.
110 result->Set(1, GetSimpleTransitionKey(simple_transition),
111 HeapObjectReference::Weak(simple_transition));
112 }
113 result->SetKey(insertion_index, *name);
114 result->SetRawTarget(insertion_index, HeapObjectReference::Weak(*target));
115
116 SLOW_DCHECK(result->IsSortedNoDuplicates());
117 ReplaceTransitions(MaybeObject::FromObject(*result));
118 Reload();
119 DCHECK_EQ(kFullTransitionArray, encoding());
120 return;
121 }
122
123 // At this point, we know that the map has a full TransitionArray.
124 DCHECK_EQ(kFullTransitionArray, encoding());
125
126 int number_of_transitions = 0;
127 int new_nof = 0;
128 int insertion_index = kNotFound;
129 const bool is_special_transition = flag == SPECIAL_TRANSITION;
130 DCHECK_EQ(is_special_transition,
131 IsSpecialTransition(ReadOnlyRoots(isolate_), *name));
132 PropertyDetails details = is_special_transition
133 ? PropertyDetails::Empty()
134 : GetTargetDetails(*name, *target);
135
136 {
137 DisallowHeapAllocation no_gc;
138 TransitionArray array = transitions();
139 number_of_transitions = array.number_of_transitions();
140
141 int index = is_special_transition
142 ? array.SearchSpecial(Symbol::cast(*name), &insertion_index)
143 : array.Search(details.kind(), *name, details.attributes(),
144 &insertion_index);
145 // If an existing entry was found, overwrite it and return.
146 if (index != kNotFound) {
147 base::SharedMutexGuard<base::kExclusive> shared_mutex_guard(
148 isolate_->transition_array_access());
149 array.SetRawTarget(index, HeapObjectReference::Weak(*target));
150 return;
151 }
152
153 new_nof = number_of_transitions + 1;
154 CHECK_LE(new_nof, kMaxNumberOfTransitions);
155 DCHECK_GE(insertion_index, 0);
156 DCHECK_LE(insertion_index, number_of_transitions);
157
158 // If there is enough capacity, insert new entry into the existing array.
159 if (new_nof <= array.Capacity()) {
160 base::SharedMutexGuard<base::kExclusive> shared_mutex_guard(
161 isolate_->transition_array_access());
162 array.SetNumberOfTransitions(new_nof);
163 for (int i = number_of_transitions; i > insertion_index; --i) {
164 array.SetKey(i, array.GetKey(i - 1));
165 array.SetRawTarget(i, array.GetRawTarget(i - 1));
166 }
167 array.SetKey(insertion_index, *name);
168 array.SetRawTarget(insertion_index, HeapObjectReference::Weak(*target));
169 SLOW_DCHECK(array.IsSortedNoDuplicates());
170 return;
171 }
172 }
173
174 // We're gonna need a bigger TransitionArray.
175 Handle<TransitionArray> result = isolate_->factory()->NewTransitionArray(
176 new_nof,
177 Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions));
178
179 // The map's transition array may have shrunk during the allocation above as
180 // it was weakly traversed, though it is guaranteed not to disappear. Trim the
181 // result copy if needed, and recompute variables.
182 Reload();
183 DisallowHeapAllocation no_gc;
184 TransitionArray array = transitions();
185 if (array.number_of_transitions() != number_of_transitions) {
186 DCHECK_LT(array.number_of_transitions(), number_of_transitions);
187
188 int index = is_special_transition
189 ? array.SearchSpecial(Symbol::cast(*name), &insertion_index)
190 : array.Search(details.kind(), *name, details.attributes(),
191 &insertion_index);
192 CHECK_EQ(index, kNotFound);
193 USE(index);
194 DCHECK_GE(insertion_index, 0);
195 DCHECK_LE(insertion_index, number_of_transitions);
196
197 number_of_transitions = array.number_of_transitions();
198 new_nof = number_of_transitions + 1;
199 result->SetNumberOfTransitions(new_nof);
200 }
201
202 if (array.HasPrototypeTransitions()) {
203 result->SetPrototypeTransitions(array.GetPrototypeTransitions());
204 }
205
206 DCHECK_NE(kNotFound, insertion_index);
207 for (int i = 0; i < insertion_index; ++i) {
208 result->Set(i, array.GetKey(i), array.GetRawTarget(i));
209 }
210 result->Set(insertion_index, *name, HeapObjectReference::Weak(*target));
211 for (int i = insertion_index; i < number_of_transitions; ++i) {
212 result->Set(i + 1, array.GetKey(i), array.GetRawTarget(i));
213 }
214
215 SLOW_DCHECK(result->IsSortedNoDuplicates());
216 ReplaceTransitions(MaybeObject::FromObject(*result));
217 }
218
SearchTransition(Name name,PropertyKind kind,PropertyAttributes attributes)219 Map TransitionsAccessor::SearchTransition(Name name, PropertyKind kind,
220 PropertyAttributes attributes) {
221 DCHECK(name.IsUniqueName());
222 switch (encoding()) {
223 case kPrototypeInfo:
224 case kUninitialized:
225 case kMigrationTarget:
226 return Map();
227 case kWeakRef: {
228 Map map = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
229 if (!IsMatchingMap(map, name, kind, attributes)) return Map();
230 return map;
231 }
232 case kFullTransitionArray: {
233 if (concurrent_access_) isolate_->transition_array_access()->LockShared();
234 Map result = transitions().SearchAndGetTarget(kind, name, attributes);
235 if (concurrent_access_)
236 isolate_->transition_array_access()->UnlockShared();
237 return result;
238 }
239 }
240 UNREACHABLE();
241 }
242
SearchSpecial(Symbol name)243 Map TransitionsAccessor::SearchSpecial(Symbol name) {
244 if (encoding() != kFullTransitionArray) return Map();
245 int transition = transitions().SearchSpecial(name);
246 if (transition == kNotFound) return Map();
247 return transitions().GetTarget(transition);
248 }
249
250 // static
IsSpecialTransition(ReadOnlyRoots roots,Name name)251 bool TransitionsAccessor::IsSpecialTransition(ReadOnlyRoots roots, Name name) {
252 if (!name.IsSymbol()) return false;
253 return name == roots.nonextensible_symbol() ||
254 name == roots.sealed_symbol() || name == roots.frozen_symbol() ||
255 name == roots.elements_transition_symbol() ||
256 name == roots.strict_function_transition_symbol();
257 }
258
FindTransitionToDataProperty(Handle<Name> name,RequestedLocation requested_location)259 MaybeHandle<Map> TransitionsAccessor::FindTransitionToDataProperty(
260 Handle<Name> name, RequestedLocation requested_location) {
261 DCHECK(name->IsUniqueName());
262 DisallowHeapAllocation no_gc;
263 PropertyAttributes attributes = name->IsPrivate() ? DONT_ENUM : NONE;
264 Map target = SearchTransition(*name, kData, attributes);
265 if (target.is_null()) return MaybeHandle<Map>();
266 PropertyDetails details = target.GetLastDescriptorDetails(isolate_);
267 DCHECK_EQ(attributes, details.attributes());
268 DCHECK_EQ(kData, details.kind());
269 if (requested_location == kFieldOnly && details.location() != kField) {
270 return MaybeHandle<Map>();
271 }
272 return Handle<Map>(target, isolate_);
273 }
274
CanHaveMoreTransitions()275 bool TransitionsAccessor::CanHaveMoreTransitions() {
276 if (map_.is_dictionary_map()) return false;
277 if (encoding() == kFullTransitionArray) {
278 return transitions().number_of_transitions() < kMaxNumberOfTransitions;
279 }
280 return true;
281 }
282
283 // static
IsMatchingMap(Map target,Name name,PropertyKind kind,PropertyAttributes attributes)284 bool TransitionsAccessor::IsMatchingMap(Map target, Name name,
285 PropertyKind kind,
286 PropertyAttributes attributes) {
287 InternalIndex descriptor = target.LastAdded();
288 DescriptorArray descriptors = target.instance_descriptors(kRelaxedLoad);
289 Name key = descriptors.GetKey(descriptor);
290 if (key != name) return false;
291 return descriptors.GetDetails(descriptor)
292 .HasKindAndAttributes(kind, attributes);
293 }
294
295 // static
CompactPrototypeTransitionArray(Isolate * isolate,WeakFixedArray array)296 bool TransitionArray::CompactPrototypeTransitionArray(Isolate* isolate,
297 WeakFixedArray array) {
298 const int header = kProtoTransitionHeaderSize;
299 int number_of_transitions = NumberOfPrototypeTransitions(array);
300 if (number_of_transitions == 0) {
301 // Empty array cannot be compacted.
302 return false;
303 }
304 int new_number_of_transitions = 0;
305 for (int i = 0; i < number_of_transitions; i++) {
306 MaybeObject target = array.Get(header + i);
307 DCHECK(target->IsCleared() ||
308 (target->IsWeak() && target->GetHeapObject().IsMap()));
309 if (!target->IsCleared()) {
310 if (new_number_of_transitions != i) {
311 array.Set(header + new_number_of_transitions, target);
312 }
313 new_number_of_transitions++;
314 }
315 }
316 // Fill slots that became free with undefined value.
317 MaybeObject undefined =
318 MaybeObject::FromObject(*isolate->factory()->undefined_value());
319 for (int i = new_number_of_transitions; i < number_of_transitions; i++) {
320 array.Set(header + i, undefined);
321 }
322 if (number_of_transitions != new_number_of_transitions) {
323 SetNumberOfPrototypeTransitions(array, new_number_of_transitions);
324 }
325 return new_number_of_transitions < number_of_transitions;
326 }
327
328 // static
GrowPrototypeTransitionArray(Handle<WeakFixedArray> array,int new_capacity,Isolate * isolate)329 Handle<WeakFixedArray> TransitionArray::GrowPrototypeTransitionArray(
330 Handle<WeakFixedArray> array, int new_capacity, Isolate* isolate) {
331 // Grow array by factor 2 up to MaxCachedPrototypeTransitions.
332 int capacity = array->length() - kProtoTransitionHeaderSize;
333 new_capacity = std::min({kMaxCachedPrototypeTransitions, new_capacity});
334 DCHECK_GT(new_capacity, capacity);
335 int grow_by = new_capacity - capacity;
336 array = isolate->factory()->CopyWeakFixedArrayAndGrow(array, grow_by);
337 if (capacity < 0) {
338 // There was no prototype transitions array before, so the size
339 // couldn't be copied. Initialize it explicitly.
340 SetNumberOfPrototypeTransitions(*array, 0);
341 }
342 return array;
343 }
344
PutPrototypeTransition(Handle<Object> prototype,Handle<Map> target_map)345 void TransitionsAccessor::PutPrototypeTransition(Handle<Object> prototype,
346 Handle<Map> target_map) {
347 DCHECK(HeapObject::cast(*prototype).map().IsMap());
348 // Don't cache prototype transition if this map is either shared, or a map of
349 // a prototype.
350 if (map_.is_prototype_map()) return;
351 if (map_.is_dictionary_map() || !FLAG_cache_prototype_transitions) return;
352
353 const int header = TransitionArray::kProtoTransitionHeaderSize;
354
355 Handle<WeakFixedArray> cache(GetPrototypeTransitions(), isolate_);
356 int capacity = cache->length() - header;
357 int transitions = TransitionArray::NumberOfPrototypeTransitions(*cache) + 1;
358
359 if (transitions > capacity) {
360 // Grow the array if compacting it doesn't free space.
361 if (!TransitionArray::CompactPrototypeTransitionArray(isolate_, *cache)) {
362 if (capacity == TransitionArray::kMaxCachedPrototypeTransitions) return;
363 cache = TransitionArray::GrowPrototypeTransitionArray(
364 cache, 2 * transitions, isolate_);
365 Reload();
366 SetPrototypeTransitions(cache);
367 }
368 }
369
370 // Reload number of transitions as they might have been compacted.
371 int last = TransitionArray::NumberOfPrototypeTransitions(*cache);
372 int entry = header + last;
373
374 cache->Set(entry, HeapObjectReference::Weak(*target_map));
375 TransitionArray::SetNumberOfPrototypeTransitions(*cache, last + 1);
376 }
377
GetPrototypeTransition(Handle<Object> prototype)378 Handle<Map> TransitionsAccessor::GetPrototypeTransition(
379 Handle<Object> prototype) {
380 DisallowHeapAllocation no_gc;
381 WeakFixedArray cache = GetPrototypeTransitions();
382 int length = TransitionArray::NumberOfPrototypeTransitions(cache);
383 for (int i = 0; i < length; i++) {
384 MaybeObject target =
385 cache.Get(TransitionArray::kProtoTransitionHeaderSize + i);
386 DCHECK(target->IsWeakOrCleared());
387 HeapObject heap_object;
388 if (target->GetHeapObjectIfWeak(&heap_object)) {
389 Map map = Map::cast(heap_object);
390 if (map.prototype() == *prototype) {
391 return handle(map, isolate_);
392 }
393 }
394 }
395 return Handle<Map>();
396 }
397
GetPrototypeTransitions()398 WeakFixedArray TransitionsAccessor::GetPrototypeTransitions() {
399 if (encoding() != kFullTransitionArray ||
400 !transitions().HasPrototypeTransitions()) {
401 return ReadOnlyRoots(isolate_).empty_weak_fixed_array();
402 }
403 return transitions().GetPrototypeTransitions();
404 }
405
406 // static
SetNumberOfPrototypeTransitions(WeakFixedArray proto_transitions,int value)407 void TransitionArray::SetNumberOfPrototypeTransitions(
408 WeakFixedArray proto_transitions, int value) {
409 DCHECK_NE(proto_transitions.length(), 0);
410 proto_transitions.Set(kProtoTransitionNumberOfEntriesOffset,
411 MaybeObject::FromSmi(Smi::FromInt(value)));
412 }
413
NumberOfTransitions()414 int TransitionsAccessor::NumberOfTransitions() {
415 switch (encoding()) {
416 case kPrototypeInfo:
417 case kUninitialized:
418 case kMigrationTarget:
419 return 0;
420 case kWeakRef:
421 return 1;
422 case kFullTransitionArray:
423 return transitions().number_of_transitions();
424 }
425 UNREACHABLE();
426 return 0; // Make GCC happy.
427 }
428
SetMigrationTarget(Map migration_target)429 void TransitionsAccessor::SetMigrationTarget(Map migration_target) {
430 // We only cache the migration target for maps with empty transitions for GC's
431 // sake.
432 if (encoding() != kUninitialized) return;
433 DCHECK(map_.is_deprecated());
434 map_.set_raw_transitions(MaybeObject::FromObject(migration_target));
435 MarkNeedsReload();
436 }
437
GetMigrationTarget()438 Map TransitionsAccessor::GetMigrationTarget() {
439 if (encoding() == kMigrationTarget) {
440 return map_.raw_transitions()->cast<Map>();
441 }
442 return Map();
443 }
444
ReplaceTransitions(MaybeObject new_transitions)445 void TransitionsAccessor::ReplaceTransitions(MaybeObject new_transitions) {
446 if (encoding() == kFullTransitionArray) {
447 #if DEBUG
448 TransitionArray old_transitions = transitions();
449 CheckNewTransitionsAreConsistent(
450 old_transitions, new_transitions->GetHeapObjectAssumeStrong());
451 DCHECK(old_transitions != new_transitions->GetHeapObjectAssumeStrong());
452 #endif
453 }
454 map_.set_raw_transitions(new_transitions);
455 MarkNeedsReload();
456 }
457
SetPrototypeTransitions(Handle<WeakFixedArray> proto_transitions)458 void TransitionsAccessor::SetPrototypeTransitions(
459 Handle<WeakFixedArray> proto_transitions) {
460 EnsureHasFullTransitionArray();
461 transitions().SetPrototypeTransitions(*proto_transitions);
462 }
463
EnsureHasFullTransitionArray()464 void TransitionsAccessor::EnsureHasFullTransitionArray() {
465 if (encoding() == kFullTransitionArray) return;
466 int nof =
467 (encoding() == kUninitialized || encoding() == kMigrationTarget) ? 0 : 1;
468 Handle<TransitionArray> result = isolate_->factory()->NewTransitionArray(nof);
469 Reload(); // Reload after possible GC.
470 if (nof == 1) {
471 if (encoding() == kUninitialized) {
472 // If allocation caused GC and cleared the target, trim the new array.
473 result->SetNumberOfTransitions(0);
474 } else {
475 // Otherwise populate the new array.
476 Handle<Map> target(GetSimpleTransition(), isolate_);
477 Name key = GetSimpleTransitionKey(*target);
478 result->Set(0, key, HeapObjectReference::Weak(*target));
479 }
480 }
481 ReplaceTransitions(MaybeObject::FromObject(*result));
482 Reload(); // Reload after replacing transitions.
483 }
484
TraverseTransitionTreeInternal(TraverseCallback callback,void * data,DisallowHeapAllocation * no_gc)485 void TransitionsAccessor::TraverseTransitionTreeInternal(
486 TraverseCallback callback, void* data, DisallowHeapAllocation* no_gc) {
487 switch (encoding()) {
488 case kPrototypeInfo:
489 case kUninitialized:
490 case kMigrationTarget:
491 break;
492 case kWeakRef: {
493 Map simple_target =
494 Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
495 TransitionsAccessor(isolate_, simple_target, no_gc)
496 .TraverseTransitionTreeInternal(callback, data, no_gc);
497 break;
498 }
499 case kFullTransitionArray: {
500 if (transitions().HasPrototypeTransitions()) {
501 WeakFixedArray proto_trans = transitions().GetPrototypeTransitions();
502 int length = TransitionArray::NumberOfPrototypeTransitions(proto_trans);
503 for (int i = 0; i < length; ++i) {
504 int index = TransitionArray::kProtoTransitionHeaderSize + i;
505 MaybeObject target = proto_trans.Get(index);
506 HeapObject heap_object;
507 if (target->GetHeapObjectIfWeak(&heap_object)) {
508 TransitionsAccessor(isolate_, Map::cast(heap_object), no_gc)
509 .TraverseTransitionTreeInternal(callback, data, no_gc);
510 } else {
511 DCHECK(target->IsCleared());
512 }
513 }
514 }
515 for (int i = 0; i < transitions().number_of_transitions(); ++i) {
516 TransitionsAccessor(isolate_, transitions().GetTarget(i), no_gc)
517 .TraverseTransitionTreeInternal(callback, data, no_gc);
518 }
519 break;
520 }
521 }
522 callback(map_, data);
523 }
524
525 #ifdef DEBUG
CheckNewTransitionsAreConsistent(TransitionArray old_transitions,Object transitions)526 void TransitionsAccessor::CheckNewTransitionsAreConsistent(
527 TransitionArray old_transitions, Object transitions) {
528 // This function only handles full transition arrays.
529 DCHECK_EQ(kFullTransitionArray, encoding());
530 TransitionArray new_transitions = TransitionArray::cast(transitions);
531 for (int i = 0; i < old_transitions.number_of_transitions(); i++) {
532 Map target = old_transitions.GetTarget(i);
533 if (target.instance_descriptors(kRelaxedLoad) ==
534 map_.instance_descriptors(kRelaxedLoad)) {
535 Name key = old_transitions.GetKey(i);
536 int new_target_index;
537 if (IsSpecialTransition(ReadOnlyRoots(isolate_), key)) {
538 new_target_index = new_transitions.SearchSpecial(Symbol::cast(key));
539 } else {
540 PropertyDetails details = GetTargetDetails(key, target);
541 new_target_index =
542 new_transitions.Search(details.kind(), key, details.attributes());
543 }
544 DCHECK_NE(TransitionArray::kNotFound, new_target_index);
545 DCHECK_EQ(target, new_transitions.GetTarget(new_target_index));
546 }
547 }
548 }
549 #endif
550
551 // Private non-static helper functions (operating on full transition arrays).
552
SearchDetails(int transition,PropertyKind kind,PropertyAttributes attributes,int * out_insertion_index)553 int TransitionArray::SearchDetails(int transition, PropertyKind kind,
554 PropertyAttributes attributes,
555 int* out_insertion_index) {
556 int nof_transitions = number_of_transitions();
557 DCHECK(transition < nof_transitions);
558 Name key = GetKey(transition);
559 for (; transition < nof_transitions && GetKey(transition) == key;
560 transition++) {
561 Map target = GetTarget(transition);
562 PropertyDetails target_details =
563 TransitionsAccessor::GetTargetDetails(key, target);
564
565 int cmp = CompareDetails(kind, attributes, target_details.kind(),
566 target_details.attributes());
567 if (cmp == 0) {
568 return transition;
569 } else if (cmp < 0) {
570 break;
571 }
572 }
573 if (out_insertion_index != nullptr) *out_insertion_index = transition;
574 return kNotFound;
575 }
576
SearchDetailsAndGetTarget(int transition,PropertyKind kind,PropertyAttributes attributes)577 Map TransitionArray::SearchDetailsAndGetTarget(int transition,
578 PropertyKind kind,
579 PropertyAttributes attributes) {
580 int nof_transitions = number_of_transitions();
581 DCHECK(transition < nof_transitions);
582 Name key = GetKey(transition);
583 for (; transition < nof_transitions && GetKey(transition) == key;
584 transition++) {
585 Map target = GetTarget(transition);
586 PropertyDetails target_details =
587 TransitionsAccessor::GetTargetDetails(key, target);
588
589 int cmp = CompareDetails(kind, attributes, target_details.kind(),
590 target_details.attributes());
591 if (cmp == 0) {
592 return target;
593 } else if (cmp < 0) {
594 break;
595 }
596 }
597 return Map();
598 }
599
Search(PropertyKind kind,Name name,PropertyAttributes attributes,int * out_insertion_index)600 int TransitionArray::Search(PropertyKind kind, Name name,
601 PropertyAttributes attributes,
602 int* out_insertion_index) {
603 int transition = SearchName(name, out_insertion_index);
604 if (transition == kNotFound) return kNotFound;
605 return SearchDetails(transition, kind, attributes, out_insertion_index);
606 }
607
SearchAndGetTarget(PropertyKind kind,Name name,PropertyAttributes attributes)608 Map TransitionArray::SearchAndGetTarget(PropertyKind kind, Name name,
609 PropertyAttributes attributes) {
610 int transition = SearchName(name, nullptr);
611 if (transition == kNotFound) {
612 return Map();
613 }
614 return SearchDetailsAndGetTarget(transition, kind, attributes);
615 }
616
Sort()617 void TransitionArray::Sort() {
618 DisallowHeapAllocation no_gc;
619 // In-place insertion sort.
620 int length = number_of_transitions();
621 ReadOnlyRoots roots = GetReadOnlyRoots();
622 for (int i = 1; i < length; i++) {
623 Name key = GetKey(i);
624 MaybeObject target = GetRawTarget(i);
625 PropertyKind kind = kData;
626 PropertyAttributes attributes = NONE;
627 if (!TransitionsAccessor::IsSpecialTransition(roots, key)) {
628 Map target_map = TransitionsAccessor::GetTargetFromRaw(target);
629 PropertyDetails details =
630 TransitionsAccessor::GetTargetDetails(key, target_map);
631 kind = details.kind();
632 attributes = details.attributes();
633 }
634 int j;
635 for (j = i - 1; j >= 0; j--) {
636 Name temp_key = GetKey(j);
637 MaybeObject temp_target = GetRawTarget(j);
638 PropertyKind temp_kind = kData;
639 PropertyAttributes temp_attributes = NONE;
640 if (!TransitionsAccessor::IsSpecialTransition(roots, temp_key)) {
641 Map temp_target_map =
642 TransitionsAccessor::GetTargetFromRaw(temp_target);
643 PropertyDetails details =
644 TransitionsAccessor::GetTargetDetails(temp_key, temp_target_map);
645 temp_kind = details.kind();
646 temp_attributes = details.attributes();
647 }
648 int cmp = CompareKeys(temp_key, temp_key.hash(), temp_kind,
649 temp_attributes, key, key.hash(), kind, attributes);
650 if (cmp > 0) {
651 SetKey(j + 1, temp_key);
652 SetRawTarget(j + 1, temp_target);
653 } else {
654 break;
655 }
656 }
657 SetKey(j + 1, key);
658 SetRawTarget(j + 1, target);
659 }
660 DCHECK(IsSortedNoDuplicates());
661 }
662
HasIntegrityLevelTransitionTo(Map to,Symbol * out_symbol,PropertyAttributes * out_integrity_level)663 bool TransitionsAccessor::HasIntegrityLevelTransitionTo(
664 Map to, Symbol* out_symbol, PropertyAttributes* out_integrity_level) {
665 ReadOnlyRoots roots(isolate_);
666 if (SearchSpecial(roots.frozen_symbol()) == to) {
667 if (out_integrity_level) *out_integrity_level = FROZEN;
668 if (out_symbol) *out_symbol = roots.frozen_symbol();
669 } else if (SearchSpecial(roots.sealed_symbol()) == to) {
670 if (out_integrity_level) *out_integrity_level = SEALED;
671 if (out_symbol) *out_symbol = roots.sealed_symbol();
672 } else if (SearchSpecial(roots.nonextensible_symbol()) == to) {
673 if (out_integrity_level) *out_integrity_level = NONE;
674 if (out_symbol) *out_symbol = roots.nonextensible_symbol();
675 } else {
676 return false;
677 }
678 return true;
679 }
680
681 } // namespace internal
682 } // namespace v8
683