1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_OBJECTS_TRANSITIONS_INL_H_
6 #define V8_OBJECTS_TRANSITIONS_INL_H_
7
8 #include "src/objects/fixed-array-inl.h"
9 #include "src/objects/maybe-object-inl.h"
10 #include "src/objects/slots.h"
11 #include "src/objects/smi.h"
12 #include "src/objects/transitions.h"
13
14 // Has to be the last include (doesn't have include guards):
15 #include "src/objects/object-macros.h"
16
17 namespace v8 {
18 namespace internal {
19
20 // static
GetTransitionArray(Isolate * isolate,MaybeObject raw_transitions)21 TransitionArray TransitionsAccessor::GetTransitionArray(
22 Isolate* isolate, MaybeObject raw_transitions) {
23 DCHECK_EQ(kFullTransitionArray, GetEncoding(isolate, raw_transitions));
24 USE(isolate);
25 return TransitionArray::cast(raw_transitions.GetHeapObjectAssumeStrong());
26 }
27
28 // static
GetTransitionArray(Isolate * isolate,Handle<Map> map)29 TransitionArray TransitionsAccessor::GetTransitionArray(Isolate* isolate,
30 Handle<Map> map) {
31 MaybeObject raw_transitions = map->raw_transitions(isolate, kAcquireLoad);
32 return GetTransitionArray(isolate, raw_transitions);
33 }
34
transitions()35 TransitionArray TransitionsAccessor::transitions() {
36 return GetTransitionArray(isolate_, raw_transitions_);
37 }
38
OBJECT_CONSTRUCTORS_IMPL(TransitionArray,WeakFixedArray)39 OBJECT_CONSTRUCTORS_IMPL(TransitionArray, WeakFixedArray)
40
41 CAST_ACCESSOR(TransitionArray)
42
43 bool TransitionArray::HasPrototypeTransitions() {
44 return Get(kPrototypeTransitionsIndex) != MaybeObject::FromSmi(Smi::zero());
45 }
46
GetPrototypeTransitions()47 WeakFixedArray TransitionArray::GetPrototypeTransitions() {
48 DCHECK(HasPrototypeTransitions()); // Callers must check first.
49 Object prototype_transitions =
50 Get(kPrototypeTransitionsIndex)->GetHeapObjectAssumeStrong();
51 return WeakFixedArray::cast(prototype_transitions);
52 }
53
GetKeySlot(int transition_number)54 HeapObjectSlot TransitionArray::GetKeySlot(int transition_number) {
55 DCHECK(transition_number < number_of_transitions());
56 return HeapObjectSlot(RawFieldOfElementAt(ToKeyIndex(transition_number)));
57 }
58
SetPrototypeTransitions(WeakFixedArray transitions)59 void TransitionArray::SetPrototypeTransitions(WeakFixedArray transitions) {
60 DCHECK(transitions.IsWeakFixedArray());
61 WeakFixedArray::Set(kPrototypeTransitionsIndex,
62 HeapObjectReference::Strong(transitions));
63 }
64
NumberOfPrototypeTransitions(WeakFixedArray proto_transitions)65 int TransitionArray::NumberOfPrototypeTransitions(
66 WeakFixedArray proto_transitions) {
67 if (proto_transitions.length() == 0) return 0;
68 MaybeObject raw =
69 proto_transitions.Get(kProtoTransitionNumberOfEntriesOffset);
70 return raw.ToSmi().value();
71 }
72
GetKey(int transition_number)73 Name TransitionArray::GetKey(int transition_number) {
74 DCHECK(transition_number < number_of_transitions());
75 return Name::cast(
76 Get(ToKeyIndex(transition_number))->GetHeapObjectAssumeStrong());
77 }
78
GetKey(InternalIndex index)79 Name TransitionArray::GetKey(InternalIndex index) {
80 return GetKey(index.as_int());
81 }
82
GetKey(int transition_number)83 Name TransitionsAccessor::GetKey(int transition_number) {
84 switch (encoding()) {
85 case kPrototypeInfo:
86 case kUninitialized:
87 case kMigrationTarget:
88 UNREACHABLE();
89 return Name();
90 case kWeakRef: {
91 Map map = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
92 return GetSimpleTransitionKey(map);
93 }
94 case kFullTransitionArray:
95 return transitions().GetKey(transition_number);
96 }
97 UNREACHABLE();
98 }
99
SetKey(int transition_number,Name key)100 void TransitionArray::SetKey(int transition_number, Name key) {
101 DCHECK(transition_number < number_of_transitions());
102 WeakFixedArray::Set(ToKeyIndex(transition_number),
103 HeapObjectReference::Strong(key));
104 }
105
GetTargetSlot(int transition_number)106 HeapObjectSlot TransitionArray::GetTargetSlot(int transition_number) {
107 DCHECK(transition_number < number_of_transitions());
108 return HeapObjectSlot(RawFieldOfElementAt(ToTargetIndex(transition_number)));
109 }
110
111 // static
GetTargetDetails(Name name,Map target)112 PropertyDetails TransitionsAccessor::GetTargetDetails(Name name, Map target) {
113 DCHECK(!IsSpecialTransition(name.GetReadOnlyRoots(), name));
114 InternalIndex descriptor = target.LastAdded();
115 DescriptorArray descriptors = target.instance_descriptors(kRelaxedLoad);
116 // Transitions are allowed only for the last added property.
117 DCHECK(descriptors.GetKey(descriptor).Equals(name));
118 return descriptors.GetDetails(descriptor);
119 }
120
GetSimpleTargetDetails(Map transition)121 PropertyDetails TransitionsAccessor::GetSimpleTargetDetails(Map transition) {
122 return transition.GetLastDescriptorDetails(isolate_);
123 }
124
125 // static
GetSimpleTransitionKey(Map transition)126 Name TransitionsAccessor::GetSimpleTransitionKey(Map transition) {
127 InternalIndex descriptor = transition.LastAdded();
128 return transition.instance_descriptors().GetKey(descriptor);
129 }
130
131 // static
GetTargetFromRaw(MaybeObject raw)132 Map TransitionsAccessor::GetTargetFromRaw(MaybeObject raw) {
133 return Map::cast(raw->GetHeapObjectAssumeWeak());
134 }
135
GetRawTarget(int transition_number)136 MaybeObject TransitionArray::GetRawTarget(int transition_number) {
137 DCHECK(transition_number < number_of_transitions());
138 return Get(ToTargetIndex(transition_number));
139 }
140
GetTarget(int transition_number)141 Map TransitionArray::GetTarget(int transition_number) {
142 MaybeObject raw = GetRawTarget(transition_number);
143 return TransitionsAccessor::GetTargetFromRaw(raw);
144 }
145
GetTarget(int transition_number)146 Map TransitionsAccessor::GetTarget(int transition_number) {
147 switch (encoding()) {
148 case kPrototypeInfo:
149 case kUninitialized:
150 case kMigrationTarget:
151 UNREACHABLE();
152 return Map();
153 case kWeakRef:
154 return Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
155 case kFullTransitionArray:
156 return transitions().GetTarget(transition_number);
157 }
158 UNREACHABLE();
159 }
160
SetRawTarget(int transition_number,MaybeObject value)161 void TransitionArray::SetRawTarget(int transition_number, MaybeObject value) {
162 DCHECK(transition_number < number_of_transitions());
163 DCHECK(value->IsWeak());
164 DCHECK(value->GetHeapObjectAssumeWeak().IsMap());
165 WeakFixedArray::Set(ToTargetIndex(transition_number), value);
166 }
167
GetTargetIfExists(int transition_number,Isolate * isolate,Map * target)168 bool TransitionArray::GetTargetIfExists(int transition_number, Isolate* isolate,
169 Map* target) {
170 MaybeObject raw = GetRawTarget(transition_number);
171 HeapObject heap_object;
172 // If the raw target is a Smi, then this TransitionArray is in the process of
173 // being deserialized, and doesn't yet have an initialized entry for this
174 // transition.
175 if (raw.IsSmi()) {
176 DCHECK(isolate->has_active_deserializer());
177 DCHECK_EQ(raw.ToSmi(), Smi::uninitialized_deserialization_value());
178 return false;
179 }
180 if (raw->GetHeapObjectIfStrong(&heap_object) &&
181 heap_object.IsUndefined(isolate)) {
182 return false;
183 }
184 *target = TransitionsAccessor::GetTargetFromRaw(raw);
185 return true;
186 }
187
SearchNameForTesting(Name name,int * out_insertion_index)188 int TransitionArray::SearchNameForTesting(Name name, int* out_insertion_index) {
189 return SearchName(name, out_insertion_index);
190 }
191
SearchAndGetTargetForTesting(PropertyKind kind,Name name,PropertyAttributes attributes)192 Map TransitionArray::SearchAndGetTargetForTesting(
193 PropertyKind kind, Name name, PropertyAttributes attributes) {
194 return SearchAndGetTarget(kind, name, attributes);
195 }
196
SearchSpecial(Symbol symbol,bool concurrent_search,int * out_insertion_index)197 int TransitionArray::SearchSpecial(Symbol symbol, bool concurrent_search,
198 int* out_insertion_index) {
199 return SearchName(symbol, concurrent_search, out_insertion_index);
200 }
201
SearchName(Name name,bool concurrent_search,int * out_insertion_index)202 int TransitionArray::SearchName(Name name, bool concurrent_search,
203 int* out_insertion_index) {
204 DCHECK(name.IsUniqueName());
205 return internal::Search<ALL_ENTRIES>(this, name, number_of_entries(),
206 out_insertion_index, concurrent_search);
207 }
208
TransitionsAccessor(Isolate * isolate,Map map,bool concurrent_access)209 TransitionsAccessor::TransitionsAccessor(Isolate* isolate, Map map,
210 bool concurrent_access)
211 : isolate_(isolate),
212 map_(map),
213 raw_transitions_(map.raw_transitions(isolate_, kAcquireLoad)),
214 encoding_(GetEncoding(isolate_, raw_transitions_)),
215 concurrent_access_(concurrent_access) {
216 DCHECK_IMPLIES(encoding_ == kMigrationTarget, map_.is_deprecated());
217 }
218
Capacity()219 int TransitionsAccessor::Capacity() { return transitions().Capacity(); }
220
221 // static
GetEncoding(Isolate * isolate,MaybeObject raw_transitions)222 TransitionsAccessor::Encoding TransitionsAccessor::GetEncoding(
223 Isolate* isolate, MaybeObject raw_transitions) {
224 HeapObject heap_object;
225 if (raw_transitions->IsSmi() || raw_transitions->IsCleared()) {
226 return kUninitialized;
227 } else if (raw_transitions->IsWeak()) {
228 return kWeakRef;
229 } else if (raw_transitions->GetHeapObjectIfStrong(isolate, &heap_object)) {
230 if (heap_object.IsTransitionArray()) {
231 return kFullTransitionArray;
232 } else if (heap_object.IsPrototypeInfo()) {
233 return kPrototypeInfo;
234 } else {
235 DCHECK(heap_object.IsMap());
236 return kMigrationTarget;
237 }
238 } else {
239 UNREACHABLE();
240 }
241 }
242
243 // static
GetEncoding(Isolate * isolate,TransitionArray array)244 TransitionsAccessor::Encoding TransitionsAccessor::GetEncoding(
245 Isolate* isolate, TransitionArray array) {
246 return GetEncoding(isolate, MaybeObject::FromObject(array));
247 }
248
249 // static
GetEncoding(Isolate * isolate,Handle<Map> map)250 TransitionsAccessor::Encoding TransitionsAccessor::GetEncoding(
251 Isolate* isolate, Handle<Map> map) {
252 MaybeObject raw_transitions = map->raw_transitions(isolate, kAcquireLoad);
253 return GetEncoding(isolate, raw_transitions);
254 }
255
256 // static
SearchTransition(Isolate * isolate,Handle<Map> map,Name name,PropertyKind kind,PropertyAttributes attributes)257 MaybeHandle<Map> TransitionsAccessor::SearchTransition(
258 Isolate* isolate, Handle<Map> map, Name name, PropertyKind kind,
259 PropertyAttributes attributes) {
260 Map result = TransitionsAccessor(isolate, *map)
261 .SearchTransition(name, kind, attributes);
262 if (result.is_null()) return MaybeHandle<Map>();
263 return MaybeHandle<Map>(result, isolate);
264 }
265
266 // static
SearchSpecial(Isolate * isolate,Handle<Map> map,Symbol name)267 MaybeHandle<Map> TransitionsAccessor::SearchSpecial(Isolate* isolate,
268 Handle<Map> map,
269 Symbol name) {
270 Map result = TransitionsAccessor(isolate, *map).SearchSpecial(name);
271 if (result.is_null()) return MaybeHandle<Map>();
272 return MaybeHandle<Map>(result, isolate);
273 }
274
number_of_transitions()275 int TransitionArray::number_of_transitions() const {
276 if (length() < kFirstIndex) return 0;
277 return Get(kTransitionLengthIndex).ToSmi().value();
278 }
279
CompareKeys(Name key1,uint32_t hash1,PropertyKind kind1,PropertyAttributes attributes1,Name key2,uint32_t hash2,PropertyKind kind2,PropertyAttributes attributes2)280 int TransitionArray::CompareKeys(Name key1, uint32_t hash1, PropertyKind kind1,
281 PropertyAttributes attributes1, Name key2,
282 uint32_t hash2, PropertyKind kind2,
283 PropertyAttributes attributes2) {
284 int cmp = CompareNames(key1, hash1, key2, hash2);
285 if (cmp != 0) return cmp;
286
287 return CompareDetails(kind1, attributes1, kind2, attributes2);
288 }
289
CompareNames(Name key1,uint32_t hash1,Name key2,uint32_t hash2)290 int TransitionArray::CompareNames(Name key1, uint32_t hash1, Name key2,
291 uint32_t hash2) {
292 if (key1 != key2) {
293 // In case of hash collisions key1 is always "less" than key2.
294 return hash1 <= hash2 ? -1 : 1;
295 }
296
297 return 0;
298 }
299
CompareDetails(PropertyKind kind1,PropertyAttributes attributes1,PropertyKind kind2,PropertyAttributes attributes2)300 int TransitionArray::CompareDetails(PropertyKind kind1,
301 PropertyAttributes attributes1,
302 PropertyKind kind2,
303 PropertyAttributes attributes2) {
304 if (kind1 != kind2) {
305 return static_cast<int>(kind1) < static_cast<int>(kind2) ? -1 : 1;
306 }
307
308 if (attributes1 != attributes2) {
309 return static_cast<int>(attributes1) < static_cast<int>(attributes2) ? -1
310 : 1;
311 }
312
313 return 0;
314 }
315
Set(int transition_number,Name key,MaybeObject target)316 void TransitionArray::Set(int transition_number, Name key, MaybeObject target) {
317 WeakFixedArray::Set(ToKeyIndex(transition_number),
318 MaybeObject::FromObject(key));
319 WeakFixedArray::Set(ToTargetIndex(transition_number), target);
320 }
321
GetSortedKey(int transition_number)322 Name TransitionArray::GetSortedKey(int transition_number) {
323 return GetKey(transition_number);
324 }
325
number_of_entries()326 int TransitionArray::number_of_entries() const {
327 return number_of_transitions();
328 }
329
Capacity()330 int TransitionArray::Capacity() {
331 if (length() <= kFirstIndex) return 0;
332 return (length() - kFirstIndex) / kEntrySize;
333 }
334
SetNumberOfTransitions(int number_of_transitions)335 void TransitionArray::SetNumberOfTransitions(int number_of_transitions) {
336 DCHECK(number_of_transitions <= Capacity());
337 WeakFixedArray::Set(
338 kTransitionLengthIndex,
339 MaybeObject::FromSmi(Smi::FromInt(number_of_transitions)));
340 }
341
ExpectedTransitionKey()342 Handle<String> TransitionsAccessor::ExpectedTransitionKey() {
343 DisallowGarbageCollection no_gc;
344 switch (encoding()) {
345 case kPrototypeInfo:
346 case kUninitialized:
347 case kMigrationTarget:
348 case kFullTransitionArray:
349 return Handle<String>::null();
350 case kWeakRef: {
351 Map target = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
352 PropertyDetails details = GetSimpleTargetDetails(target);
353 if (details.location() != PropertyLocation::kField)
354 return Handle<String>::null();
355 DCHECK_EQ(PropertyKind::kData, details.kind());
356 if (details.attributes() != NONE) return Handle<String>::null();
357 Name name = GetSimpleTransitionKey(target);
358 if (!name.IsString()) return Handle<String>::null();
359 return handle(String::cast(name), isolate_);
360 }
361 }
362 UNREACHABLE();
363 }
364
ExpectedTransitionTarget()365 Handle<Map> TransitionsAccessor::ExpectedTransitionTarget() {
366 DCHECK(!ExpectedTransitionKey().is_null());
367 return handle(GetTarget(0), isolate_);
368 }
369
370 } // namespace internal
371 } // namespace v8
372
373 #include "src/objects/object-macros-undef.h"
374
375 #endif // V8_OBJECTS_TRANSITIONS_INL_H_
376