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