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