1 // Copyright 2017 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/map-updater.h"
6
7 #include "src/field-type.h"
8 #include "src/handles.h"
9 #include "src/isolate.h"
10 #include "src/objects-inl.h"
11 #include "src/objects.h"
12 #include "src/transitions.h"
13
14 namespace v8 {
15 namespace internal {
16
17 namespace {
18
EqualImmutableValues(Object * obj1,Object * obj2)19 inline bool EqualImmutableValues(Object* obj1, Object* obj2) {
20 if (obj1 == obj2) return true; // Valid for both kData and kAccessor kinds.
21 // TODO(ishell): compare AccessorPairs.
22 return false;
23 }
24
25 } // namespace
26
MapUpdater(Isolate * isolate,Handle<Map> old_map)27 MapUpdater::MapUpdater(Isolate* isolate, Handle<Map> old_map)
28 : isolate_(isolate),
29 old_map_(old_map),
30 old_descriptors_(old_map->instance_descriptors(), isolate_),
31 old_nof_(old_map_->NumberOfOwnDescriptors()),
32 new_elements_kind_(old_map_->elements_kind()),
33 is_transitionable_fast_elements_kind_(
34 IsTransitionableFastElementsKind(new_elements_kind_)) {
35 // We shouldn't try to update remote objects.
36 DCHECK(!old_map->FindRootMap(isolate)
37 ->GetConstructor()
38 ->IsFunctionTemplateInfo());
39 }
40
GetKey(int descriptor) const41 Name* MapUpdater::GetKey(int descriptor) const {
42 return old_descriptors_->GetKey(descriptor);
43 }
44
GetDetails(int descriptor) const45 PropertyDetails MapUpdater::GetDetails(int descriptor) const {
46 DCHECK_LE(0, descriptor);
47 if (descriptor == modified_descriptor_) {
48 return PropertyDetails(new_kind_, new_attributes_, new_location_,
49 new_constness_, new_representation_);
50 }
51 return old_descriptors_->GetDetails(descriptor);
52 }
53
GetValue(int descriptor) const54 Object* MapUpdater::GetValue(int descriptor) const {
55 DCHECK_LE(0, descriptor);
56 if (descriptor == modified_descriptor_) {
57 DCHECK_EQ(kDescriptor, new_location_);
58 return *new_value_;
59 }
60 DCHECK_EQ(kDescriptor, GetDetails(descriptor).location());
61 return old_descriptors_->GetStrongValue(descriptor);
62 }
63
GetFieldType(int descriptor) const64 FieldType* MapUpdater::GetFieldType(int descriptor) const {
65 DCHECK_LE(0, descriptor);
66 if (descriptor == modified_descriptor_) {
67 DCHECK_EQ(kField, new_location_);
68 return *new_field_type_;
69 }
70 DCHECK_EQ(kField, GetDetails(descriptor).location());
71 return old_descriptors_->GetFieldType(descriptor);
72 }
73
GetOrComputeFieldType(int descriptor,PropertyLocation location,Representation representation) const74 Handle<FieldType> MapUpdater::GetOrComputeFieldType(
75 int descriptor, PropertyLocation location,
76 Representation representation) const {
77 DCHECK_LE(0, descriptor);
78 // |location| is just a pre-fetched GetDetails(descriptor).location().
79 DCHECK_EQ(location, GetDetails(descriptor).location());
80 if (location == kField) {
81 return handle(GetFieldType(descriptor), isolate_);
82 } else {
83 return GetValue(descriptor)->OptimalType(isolate_, representation);
84 }
85 }
86
GetOrComputeFieldType(Handle<DescriptorArray> descriptors,int descriptor,PropertyLocation location,Representation representation)87 Handle<FieldType> MapUpdater::GetOrComputeFieldType(
88 Handle<DescriptorArray> descriptors, int descriptor,
89 PropertyLocation location, Representation representation) {
90 // |location| is just a pre-fetched GetDetails(descriptor).location().
91 DCHECK_EQ(descriptors->GetDetails(descriptor).location(), location);
92 if (location == kField) {
93 return handle(descriptors->GetFieldType(descriptor), isolate_);
94 } else {
95 return descriptors->GetStrongValue(descriptor)
96 ->OptimalType(isolate_, representation);
97 }
98 }
99
ReconfigureToDataField(int descriptor,PropertyAttributes attributes,PropertyConstness constness,Representation representation,Handle<FieldType> field_type)100 Handle<Map> MapUpdater::ReconfigureToDataField(int descriptor,
101 PropertyAttributes attributes,
102 PropertyConstness constness,
103 Representation representation,
104 Handle<FieldType> field_type) {
105 DCHECK_EQ(kInitialized, state_);
106 DCHECK_LE(0, descriptor);
107 DCHECK(!old_map_->is_dictionary_map());
108 modified_descriptor_ = descriptor;
109 new_kind_ = kData;
110 new_attributes_ = attributes;
111 new_location_ = kField;
112
113 PropertyDetails old_details =
114 old_descriptors_->GetDetails(modified_descriptor_);
115
116 // If property kind is not reconfigured merge the result with
117 // representation/field type from the old descriptor.
118 if (old_details.kind() == new_kind_) {
119 new_constness_ = GeneralizeConstness(constness, old_details.constness());
120
121 Representation old_representation = old_details.representation();
122 new_representation_ = representation.generalize(old_representation);
123
124 Handle<FieldType> old_field_type =
125 GetOrComputeFieldType(old_descriptors_, modified_descriptor_,
126 old_details.location(), new_representation_);
127
128 new_field_type_ =
129 Map::GeneralizeFieldType(old_representation, old_field_type,
130 new_representation_, field_type, isolate_);
131 } else {
132 // We don't know if this is a first property kind reconfiguration
133 // and we don't know which value was in this property previously
134 // therefore we can't treat such a property as constant.
135 new_constness_ = PropertyConstness::kMutable;
136 new_representation_ = representation;
137 new_field_type_ = field_type;
138 }
139
140 Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
141 isolate_, old_map_->instance_type(), &new_constness_,
142 &new_representation_, &new_field_type_);
143
144 if (TryRecofigureToDataFieldInplace() == kEnd) return result_map_;
145 if (FindRootMap() == kEnd) return result_map_;
146 if (FindTargetMap() == kEnd) return result_map_;
147 ConstructNewMap();
148 DCHECK_EQ(kEnd, state_);
149 return result_map_;
150 }
151
ReconfigureElementsKind(ElementsKind elements_kind)152 Handle<Map> MapUpdater::ReconfigureElementsKind(ElementsKind elements_kind) {
153 DCHECK_EQ(kInitialized, state_);
154 new_elements_kind_ = elements_kind;
155 is_transitionable_fast_elements_kind_ =
156 IsTransitionableFastElementsKind(new_elements_kind_);
157
158 if (FindRootMap() == kEnd) return result_map_;
159 if (FindTargetMap() == kEnd) return result_map_;
160 ConstructNewMap();
161 DCHECK_EQ(kEnd, state_);
162 return result_map_;
163 }
164
Update()165 Handle<Map> MapUpdater::Update() {
166 DCHECK_EQ(kInitialized, state_);
167 DCHECK(old_map_->is_deprecated());
168
169 if (FindRootMap() == kEnd) return result_map_;
170 if (FindTargetMap() == kEnd) return result_map_;
171 ConstructNewMap();
172 DCHECK_EQ(kEnd, state_);
173 return result_map_;
174 }
175
GeneralizeField(Handle<Map> map,int modify_index,PropertyConstness new_constness,Representation new_representation,Handle<FieldType> new_field_type)176 void MapUpdater::GeneralizeField(Handle<Map> map, int modify_index,
177 PropertyConstness new_constness,
178 Representation new_representation,
179 Handle<FieldType> new_field_type) {
180 Map::GeneralizeField(isolate_, map, modify_index, new_constness,
181 new_representation, new_field_type);
182
183 DCHECK_EQ(*old_descriptors_, old_map_->instance_descriptors());
184 }
185
CopyGeneralizeAllFields(const char * reason)186 MapUpdater::State MapUpdater::CopyGeneralizeAllFields(const char* reason) {
187 result_map_ = Map::CopyGeneralizeAllFields(
188 isolate_, old_map_, new_elements_kind_, modified_descriptor_, new_kind_,
189 new_attributes_, reason);
190 state_ = kEnd;
191 return state_; // Done.
192 }
193
TryRecofigureToDataFieldInplace()194 MapUpdater::State MapUpdater::TryRecofigureToDataFieldInplace() {
195 // If it's just a representation generalization case (i.e. property kind and
196 // attributes stays unchanged) it's fine to transition from None to anything
197 // but double without any modification to the object, because the default
198 // uninitialized value for representation None can be overwritten by both
199 // smi and tagged values. Doubles, however, would require a box allocation.
200 if (new_representation_.IsNone() || new_representation_.IsDouble()) {
201 return state_; // Not done yet.
202 }
203
204 PropertyDetails old_details =
205 old_descriptors_->GetDetails(modified_descriptor_);
206 Representation old_representation = old_details.representation();
207 if (!old_representation.IsNone()) {
208 return state_; // Not done yet.
209 }
210
211 DCHECK_EQ(new_kind_, old_details.kind());
212 DCHECK_EQ(new_attributes_, old_details.attributes());
213 DCHECK_EQ(kField, old_details.location());
214 if (FLAG_trace_generalization) {
215 old_map_->PrintGeneralization(
216 isolate_, stdout, "uninitialized field", modified_descriptor_, old_nof_,
217 old_nof_, false, old_representation, new_representation_,
218 handle(old_descriptors_->GetFieldType(modified_descriptor_), isolate_),
219 MaybeHandle<Object>(), new_field_type_, MaybeHandle<Object>());
220 }
221 Handle<Map> field_owner(
222 old_map_->FindFieldOwner(isolate_, modified_descriptor_), isolate_);
223
224 GeneralizeField(field_owner, modified_descriptor_, new_constness_,
225 new_representation_, new_field_type_);
226 // Check that the descriptor array was updated.
227 DCHECK(old_descriptors_->GetDetails(modified_descriptor_)
228 .representation()
229 .Equals(new_representation_));
230 DCHECK(old_descriptors_->GetFieldType(modified_descriptor_)
231 ->NowIs(new_field_type_));
232
233 result_map_ = old_map_;
234 state_ = kEnd;
235 return state_; // Done.
236 }
237
FindRootMap()238 MapUpdater::State MapUpdater::FindRootMap() {
239 DCHECK_EQ(kInitialized, state_);
240 // Check the state of the root map.
241 root_map_ = handle(old_map_->FindRootMap(isolate_), isolate_);
242 ElementsKind from_kind = root_map_->elements_kind();
243 ElementsKind to_kind = new_elements_kind_;
244 if (root_map_->is_deprecated()) {
245 state_ = kEnd;
246 result_map_ = handle(
247 JSFunction::cast(root_map_->GetConstructor())->initial_map(), isolate_);
248 result_map_ = Map::AsElementsKind(isolate_, result_map_, to_kind);
249 DCHECK(result_map_->is_dictionary_map());
250 return state_;
251 }
252 int root_nof = root_map_->NumberOfOwnDescriptors();
253 if (!old_map_->EquivalentToForTransition(*root_map_)) {
254 return CopyGeneralizeAllFields("GenAll_NotEquivalent");
255 }
256
257 // TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
258 if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
259 to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
260 to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
261 !(IsTransitionableFastElementsKind(from_kind) &&
262 IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
263 return CopyGeneralizeAllFields("GenAll_InvalidElementsTransition");
264 }
265
266 if (modified_descriptor_ >= 0 && modified_descriptor_ < root_nof) {
267 PropertyDetails old_details =
268 old_descriptors_->GetDetails(modified_descriptor_);
269 if (old_details.kind() != new_kind_ ||
270 old_details.attributes() != new_attributes_) {
271 return CopyGeneralizeAllFields("GenAll_RootModification1");
272 }
273 if (old_details.location() != kField) {
274 return CopyGeneralizeAllFields("GenAll_RootModification2");
275 }
276 if (new_constness_ != old_details.constness() &&
277 (!FLAG_modify_map_inplace || !old_map_->is_prototype_map())) {
278 return CopyGeneralizeAllFields("GenAll_RootModification3");
279 }
280 if (!new_representation_.fits_into(old_details.representation())) {
281 return CopyGeneralizeAllFields("GenAll_RootModification4");
282 }
283
284 DCHECK_EQ(kData, old_details.kind());
285 DCHECK_EQ(kData, new_kind_);
286 DCHECK_EQ(kField, new_location_);
287 FieldType* old_field_type =
288 old_descriptors_->GetFieldType(modified_descriptor_);
289 if (!new_field_type_->NowIs(old_field_type)) {
290 return CopyGeneralizeAllFields("GenAll_RootModification5");
291 }
292
293 // Modify root map in-place.
294 if (FLAG_modify_map_inplace && new_constness_ != old_details.constness()) {
295 // Only prototype root maps are allowed to be updated in-place.
296 // TODO(ishell): fix all the stubs that use prototype map check to
297 // ensure that the prototype was not modified.
298 DCHECK(old_map_->is_prototype_map());
299 DCHECK(old_map_->is_stable());
300 DCHECK(IsGeneralizableTo(old_details.constness(), new_constness_));
301 GeneralizeField(old_map_, modified_descriptor_, new_constness_,
302 old_details.representation(),
303 handle(old_field_type, isolate_));
304 }
305 }
306
307 // From here on, use the map with correct elements kind as root map.
308 root_map_ = Map::AsElementsKind(isolate_, root_map_, to_kind);
309 state_ = kAtRootMap;
310 return state_; // Not done yet.
311 }
312
FindTargetMap()313 MapUpdater::State MapUpdater::FindTargetMap() {
314 DCHECK_EQ(kAtRootMap, state_);
315 target_map_ = root_map_;
316
317 int root_nof = root_map_->NumberOfOwnDescriptors();
318 for (int i = root_nof; i < old_nof_; ++i) {
319 PropertyDetails old_details = GetDetails(i);
320 Map* transition = TransitionsAccessor(isolate_, target_map_)
321 .SearchTransition(GetKey(i), old_details.kind(),
322 old_details.attributes());
323 if (transition == nullptr) break;
324 Handle<Map> tmp_map(transition, isolate_);
325
326 Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
327 isolate_);
328
329 // Check if target map is incompatible.
330 PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
331 DCHECK_EQ(old_details.kind(), tmp_details.kind());
332 DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
333 if (old_details.kind() == kAccessor &&
334 !EqualImmutableValues(GetValue(i),
335 tmp_descriptors->GetStrongValue(i))) {
336 // TODO(ishell): mutable accessors are not implemented yet.
337 return CopyGeneralizeAllFields("GenAll_Incompatible");
338 }
339 PropertyConstness tmp_constness = tmp_details.constness();
340 if (!FLAG_modify_map_inplace &&
341 !IsGeneralizableTo(old_details.constness(), tmp_constness)) {
342 break;
343 }
344 if (!IsGeneralizableTo(old_details.location(), tmp_details.location())) {
345 break;
346 }
347 Representation tmp_representation = tmp_details.representation();
348 if (!old_details.representation().fits_into(tmp_representation)) {
349 break;
350 }
351
352 if (tmp_details.location() == kField) {
353 Handle<FieldType> old_field_type =
354 GetOrComputeFieldType(i, old_details.location(), tmp_representation);
355 PropertyConstness constness =
356 FLAG_modify_map_inplace ? old_details.constness() : tmp_constness;
357 GeneralizeField(tmp_map, i, constness, tmp_representation,
358 old_field_type);
359 } else {
360 // kDescriptor: Check that the value matches.
361 if (!EqualImmutableValues(GetValue(i),
362 tmp_descriptors->GetStrongValue(i))) {
363 break;
364 }
365 }
366 DCHECK(!tmp_map->is_deprecated());
367 target_map_ = tmp_map;
368 }
369
370 // Directly change the map if the target map is more general.
371 int target_nof = target_map_->NumberOfOwnDescriptors();
372 if (target_nof == old_nof_) {
373 #ifdef DEBUG
374 if (modified_descriptor_ >= 0) {
375 DescriptorArray* target_descriptors = target_map_->instance_descriptors();
376 PropertyDetails details =
377 target_descriptors->GetDetails(modified_descriptor_);
378 DCHECK_EQ(new_kind_, details.kind());
379 DCHECK_EQ(new_attributes_, details.attributes());
380 DCHECK(IsGeneralizableTo(new_constness_, details.constness()));
381 DCHECK_EQ(new_location_, details.location());
382 DCHECK(new_representation_.fits_into(details.representation()));
383 if (new_location_ == kField) {
384 DCHECK_EQ(kField, details.location());
385 DCHECK(new_field_type_->NowIs(
386 target_descriptors->GetFieldType(modified_descriptor_)));
387 } else {
388 DCHECK(details.location() == kField ||
389 EqualImmutableValues(
390 *new_value_,
391 target_descriptors->GetStrongValue(modified_descriptor_)));
392 }
393 }
394 #endif
395 if (*target_map_ != *old_map_) {
396 old_map_->NotifyLeafMapLayoutChange(isolate_);
397 }
398 result_map_ = target_map_;
399 state_ = kEnd;
400 return state_; // Done.
401 }
402
403 // Find the last compatible target map in the transition tree.
404 for (int i = target_nof; i < old_nof_; ++i) {
405 PropertyDetails old_details = GetDetails(i);
406 Map* transition = TransitionsAccessor(isolate_, target_map_)
407 .SearchTransition(GetKey(i), old_details.kind(),
408 old_details.attributes());
409 if (transition == nullptr) break;
410 Handle<Map> tmp_map(transition, isolate_);
411 Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
412 isolate_);
413 #ifdef DEBUG
414 // Check that target map is compatible.
415 PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
416 DCHECK_EQ(old_details.kind(), tmp_details.kind());
417 DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
418 #endif
419 if (old_details.kind() == kAccessor &&
420 !EqualImmutableValues(GetValue(i),
421 tmp_descriptors->GetStrongValue(i))) {
422 return CopyGeneralizeAllFields("GenAll_Incompatible");
423 }
424 DCHECK(!tmp_map->is_deprecated());
425 target_map_ = tmp_map;
426 }
427
428 state_ = kAtTargetMap;
429 return state_; // Not done yet.
430 }
431
BuildDescriptorArray()432 Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() {
433 InstanceType instance_type = old_map_->instance_type();
434 int target_nof = target_map_->NumberOfOwnDescriptors();
435 Handle<DescriptorArray> target_descriptors(
436 target_map_->instance_descriptors(), isolate_);
437
438 // Allocate a new descriptor array large enough to hold the required
439 // descriptors, with minimally the exact same size as the old descriptor
440 // array.
441 int new_slack =
442 Max(old_nof_, old_descriptors_->number_of_descriptors()) - old_nof_;
443 Handle<DescriptorArray> new_descriptors =
444 DescriptorArray::Allocate(isolate_, old_nof_, new_slack);
445 DCHECK(new_descriptors->length() > target_descriptors->length() ||
446 new_descriptors->NumberOfSlackDescriptors() > 0 ||
447 new_descriptors->number_of_descriptors() ==
448 old_descriptors_->number_of_descriptors());
449 DCHECK(new_descriptors->number_of_descriptors() == old_nof_);
450
451 int root_nof = root_map_->NumberOfOwnDescriptors();
452
453 // Given that we passed root modification check in FindRootMap() so
454 // the root descriptors are either not modified at all or already more
455 // general than we requested. Take |root_nof| entries as is.
456 // 0 -> |root_nof|
457 int current_offset = 0;
458 for (int i = 0; i < root_nof; ++i) {
459 PropertyDetails old_details = old_descriptors_->GetDetails(i);
460 if (old_details.location() == kField) {
461 current_offset += old_details.field_width_in_words();
462 }
463 Descriptor d(handle(GetKey(i), isolate_),
464 MaybeObjectHandle(old_descriptors_->GetValue(i), isolate_),
465 old_details);
466 new_descriptors->Set(i, &d);
467 }
468
469 // Merge "updated" old_descriptor entries with target_descriptor entries.
470 // |root_nof| -> |target_nof|
471 for (int i = root_nof; i < target_nof; ++i) {
472 Handle<Name> key(GetKey(i), isolate_);
473 PropertyDetails old_details = GetDetails(i);
474 PropertyDetails target_details = target_descriptors->GetDetails(i);
475
476 PropertyKind next_kind = old_details.kind();
477 PropertyAttributes next_attributes = old_details.attributes();
478 DCHECK_EQ(next_kind, target_details.kind());
479 DCHECK_EQ(next_attributes, target_details.attributes());
480
481 PropertyConstness next_constness = GeneralizeConstness(
482 old_details.constness(), target_details.constness());
483
484 // Note: failed values equality check does not invalidate per-object
485 // property constness.
486 PropertyLocation next_location =
487 old_details.location() == kField ||
488 target_details.location() == kField ||
489 !EqualImmutableValues(target_descriptors->GetStrongValue(i),
490 GetValue(i))
491 ? kField
492 : kDescriptor;
493
494 if (!FLAG_track_constant_fields && next_location == kField) {
495 next_constness = PropertyConstness::kMutable;
496 }
497 // Ensure that mutable values are stored in fields.
498 DCHECK_IMPLIES(next_constness == PropertyConstness::kMutable,
499 next_location == kField);
500
501 Representation next_representation =
502 old_details.representation().generalize(
503 target_details.representation());
504
505 if (next_location == kField) {
506 Handle<FieldType> old_field_type =
507 GetOrComputeFieldType(i, old_details.location(), next_representation);
508
509 Handle<FieldType> target_field_type =
510 GetOrComputeFieldType(target_descriptors, i,
511 target_details.location(), next_representation);
512
513 Handle<FieldType> next_field_type = Map::GeneralizeFieldType(
514 old_details.representation(), old_field_type, next_representation,
515 target_field_type, isolate_);
516
517 Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
518 isolate_, instance_type, &next_constness, &next_representation,
519 &next_field_type);
520
521 MaybeObjectHandle wrapped_type(
522 Map::WrapFieldType(isolate_, next_field_type));
523 Descriptor d;
524 if (next_kind == kData) {
525 d = Descriptor::DataField(key, current_offset, next_attributes,
526 next_constness, next_representation,
527 wrapped_type);
528 } else {
529 // TODO(ishell): mutable accessors are not implemented yet.
530 UNIMPLEMENTED();
531 }
532 current_offset += d.GetDetails().field_width_in_words();
533 new_descriptors->Set(i, &d);
534 } else {
535 DCHECK_EQ(kDescriptor, next_location);
536 DCHECK_EQ(PropertyConstness::kConst, next_constness);
537
538 Handle<Object> value(GetValue(i), isolate_);
539 Descriptor d;
540 if (next_kind == kData) {
541 DCHECK(!FLAG_track_constant_fields);
542 d = Descriptor::DataConstant(key, value, next_attributes);
543 } else {
544 DCHECK_EQ(kAccessor, next_kind);
545 d = Descriptor::AccessorConstant(key, value, next_attributes);
546 }
547 new_descriptors->Set(i, &d);
548 }
549 }
550
551 // Take "updated" old_descriptor entries.
552 // |target_nof| -> |old_nof|
553 for (int i = target_nof; i < old_nof_; ++i) {
554 PropertyDetails old_details = GetDetails(i);
555 Handle<Name> key(GetKey(i), isolate_);
556
557 PropertyKind next_kind = old_details.kind();
558 PropertyAttributes next_attributes = old_details.attributes();
559 PropertyConstness next_constness = old_details.constness();
560 PropertyLocation next_location = old_details.location();
561 Representation next_representation = old_details.representation();
562
563 Descriptor d;
564 if (next_location == kField) {
565 Handle<FieldType> next_field_type =
566 GetOrComputeFieldType(i, old_details.location(), next_representation);
567
568 // If the |new_elements_kind_| is still transitionable then the old map's
569 // elements kind is also transitionable and therefore the old descriptors
570 // array must already have non in-place generalizable fields.
571 CHECK_IMPLIES(is_transitionable_fast_elements_kind_,
572 !Map::IsInplaceGeneralizableField(
573 next_constness, next_representation, *next_field_type));
574
575 MaybeObjectHandle wrapped_type(
576 Map::WrapFieldType(isolate_, next_field_type));
577 Descriptor d;
578 if (next_kind == kData) {
579 DCHECK_IMPLIES(!FLAG_track_constant_fields,
580 next_constness == PropertyConstness::kMutable);
581 d = Descriptor::DataField(key, current_offset, next_attributes,
582 next_constness, next_representation,
583 wrapped_type);
584 } else {
585 // TODO(ishell): mutable accessors are not implemented yet.
586 UNIMPLEMENTED();
587 }
588 current_offset += d.GetDetails().field_width_in_words();
589 new_descriptors->Set(i, &d);
590 } else {
591 DCHECK_EQ(kDescriptor, next_location);
592 DCHECK_EQ(PropertyConstness::kConst, next_constness);
593
594 Handle<Object> value(GetValue(i), isolate_);
595 if (next_kind == kData) {
596 d = Descriptor::DataConstant(key, value, next_attributes);
597 } else {
598 DCHECK_EQ(kAccessor, next_kind);
599 d = Descriptor::AccessorConstant(key, value, next_attributes);
600 }
601 new_descriptors->Set(i, &d);
602 }
603 }
604
605 new_descriptors->Sort();
606 return new_descriptors;
607 }
608
FindSplitMap(Handle<DescriptorArray> descriptors)609 Handle<Map> MapUpdater::FindSplitMap(Handle<DescriptorArray> descriptors) {
610 DisallowHeapAllocation no_allocation;
611
612 int root_nof = root_map_->NumberOfOwnDescriptors();
613 Map* current = *root_map_;
614 for (int i = root_nof; i < old_nof_; i++) {
615 Name* name = descriptors->GetKey(i);
616 PropertyDetails details = descriptors->GetDetails(i);
617 Map* next =
618 TransitionsAccessor(isolate_, current, &no_allocation)
619 .SearchTransition(name, details.kind(), details.attributes());
620 if (next == nullptr) break;
621 DescriptorArray* next_descriptors = next->instance_descriptors();
622
623 PropertyDetails next_details = next_descriptors->GetDetails(i);
624 DCHECK_EQ(details.kind(), next_details.kind());
625 DCHECK_EQ(details.attributes(), next_details.attributes());
626 if (details.constness() != next_details.constness()) break;
627 if (details.location() != next_details.location()) break;
628 if (!details.representation().Equals(next_details.representation())) break;
629
630 if (next_details.location() == kField) {
631 FieldType* next_field_type = next_descriptors->GetFieldType(i);
632 if (!descriptors->GetFieldType(i)->NowIs(next_field_type)) {
633 break;
634 }
635 } else {
636 if (!EqualImmutableValues(descriptors->GetStrongValue(i),
637 next_descriptors->GetStrongValue(i))) {
638 break;
639 }
640 }
641 current = next;
642 }
643 return handle(current, isolate_);
644 }
645
ConstructNewMap()646 MapUpdater::State MapUpdater::ConstructNewMap() {
647 Handle<DescriptorArray> new_descriptors = BuildDescriptorArray();
648
649 Handle<Map> split_map = FindSplitMap(new_descriptors);
650 int split_nof = split_map->NumberOfOwnDescriptors();
651 DCHECK_NE(old_nof_, split_nof);
652
653 PropertyDetails split_details = GetDetails(split_nof);
654 TransitionsAccessor transitions(isolate_, split_map);
655
656 // Invalidate a transition target at |key|.
657 Map* maybe_transition = transitions.SearchTransition(
658 GetKey(split_nof), split_details.kind(), split_details.attributes());
659 if (maybe_transition != nullptr) {
660 maybe_transition->DeprecateTransitionTree(isolate_);
661 }
662
663 // If |maybe_transition| is not nullptr then the transition array already
664 // contains entry for given descriptor. This means that the transition
665 // could be inserted regardless of whether transitions array is full or not.
666 if (maybe_transition == nullptr && !transitions.CanHaveMoreTransitions()) {
667 return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
668 }
669
670 old_map_->NotifyLeafMapLayoutChange(isolate_);
671
672 if (FLAG_trace_generalization && modified_descriptor_ >= 0) {
673 PropertyDetails old_details =
674 old_descriptors_->GetDetails(modified_descriptor_);
675 PropertyDetails new_details =
676 new_descriptors->GetDetails(modified_descriptor_);
677 MaybeHandle<FieldType> old_field_type;
678 MaybeHandle<FieldType> new_field_type;
679 MaybeHandle<Object> old_value;
680 MaybeHandle<Object> new_value;
681 if (old_details.location() == kField) {
682 old_field_type = handle(
683 old_descriptors_->GetFieldType(modified_descriptor_), isolate_);
684 } else {
685 old_value = handle(old_descriptors_->GetStrongValue(modified_descriptor_),
686 isolate_);
687 }
688 if (new_details.location() == kField) {
689 new_field_type =
690 handle(new_descriptors->GetFieldType(modified_descriptor_), isolate_);
691 } else {
692 new_value = handle(new_descriptors->GetStrongValue(modified_descriptor_),
693 isolate_);
694 }
695
696 old_map_->PrintGeneralization(
697 isolate_, stdout, "", modified_descriptor_, split_nof, old_nof_,
698 old_details.location() == kDescriptor && new_location_ == kField,
699 old_details.representation(), new_details.representation(),
700 old_field_type, old_value, new_field_type, new_value);
701 }
702
703 Handle<LayoutDescriptor> new_layout_descriptor =
704 LayoutDescriptor::New(isolate_, split_map, new_descriptors, old_nof_);
705
706 Handle<Map> new_map = Map::AddMissingTransitions(
707 isolate_, split_map, new_descriptors, new_layout_descriptor);
708
709 // Deprecated part of the transition tree is no longer reachable, so replace
710 // current instance descriptors in the "survived" part of the tree with
711 // the new descriptors to maintain descriptors sharing invariant.
712 split_map->ReplaceDescriptors(isolate_, *new_descriptors,
713 *new_layout_descriptor);
714
715 result_map_ = new_map;
716 state_ = kEnd;
717 return state_; // Done.
718 }
719
720 } // namespace internal
721 } // namespace v8
722