• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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