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/objects/map-updater.h"
6 
7 #include "src/execution/isolate.h"
8 #include "src/handles/handles.h"
9 #include "src/objects/field-type.h"
10 #include "src/objects/objects-inl.h"
11 #include "src/objects/objects.h"
12 #include "src/objects/property-details.h"
13 #include "src/objects/transitions.h"
14 
15 namespace v8 {
16 namespace internal {
17 
18 namespace {
19 
EqualImmutableValues(Object obj1,Object obj2)20 inline bool EqualImmutableValues(Object obj1, Object obj2) {
21   if (obj1 == obj2) return true;  // Valid for both kData and kAccessor kinds.
22   // TODO(ishell): compare AccessorPairs.
23   return false;
24 }
25 
26 }  // namespace
27 
MapUpdater(Isolate * isolate,Handle<Map> old_map)28 MapUpdater::MapUpdater(Isolate* isolate, Handle<Map> old_map)
29     : isolate_(isolate),
30       old_map_(old_map),
31       old_descriptors_(old_map->instance_descriptors(kRelaxedLoad), isolate_),
32       old_nof_(old_map_->NumberOfOwnDescriptors()),
33       new_elements_kind_(old_map_->elements_kind()),
34       is_transitionable_fast_elements_kind_(
35           IsTransitionableFastElementsKind(new_elements_kind_)) {
36   // We shouldn't try to update remote objects.
37   DCHECK(
38       !old_map->FindRootMap(isolate).GetConstructor().IsFunctionTemplateInfo());
39 }
40 
GetKey(InternalIndex descriptor) const41 Name MapUpdater::GetKey(InternalIndex descriptor) const {
42   return old_descriptors_->GetKey(descriptor);
43 }
44 
GetDetails(InternalIndex descriptor) const45 PropertyDetails MapUpdater::GetDetails(InternalIndex descriptor) const {
46   DCHECK(descriptor.is_found());
47   if (descriptor == modified_descriptor_) {
48     PropertyAttributes attributes = new_attributes_;
49     // If the original map was sealed or frozen, let us used the old
50     // attributes so that we follow the same transition path as before.
51     // Note that the user could not have changed the attributes because
52     // both seal and freeze make the properties non-configurable.
53     if (integrity_level_ == SEALED || integrity_level_ == FROZEN) {
54       attributes = old_descriptors_->GetDetails(descriptor).attributes();
55     }
56     return PropertyDetails(new_kind_, attributes, new_location_, new_constness_,
57                            new_representation_);
58   }
59   return old_descriptors_->GetDetails(descriptor);
60 }
61 
GetValue(InternalIndex descriptor) const62 Object MapUpdater::GetValue(InternalIndex descriptor) const {
63   DCHECK(descriptor.is_found());
64   if (descriptor == modified_descriptor_) {
65     DCHECK_EQ(kDescriptor, new_location_);
66     return *new_value_;
67   }
68   DCHECK_EQ(kDescriptor, GetDetails(descriptor).location());
69   return old_descriptors_->GetStrongValue(descriptor);
70 }
71 
GetFieldType(InternalIndex descriptor) const72 FieldType MapUpdater::GetFieldType(InternalIndex descriptor) const {
73   DCHECK(descriptor.is_found());
74   if (descriptor == modified_descriptor_) {
75     DCHECK_EQ(kField, new_location_);
76     return *new_field_type_;
77   }
78   DCHECK_EQ(kField, GetDetails(descriptor).location());
79   return old_descriptors_->GetFieldType(descriptor);
80 }
81 
GetOrComputeFieldType(InternalIndex descriptor,PropertyLocation location,Representation representation) const82 Handle<FieldType> MapUpdater::GetOrComputeFieldType(
83     InternalIndex descriptor, PropertyLocation location,
84     Representation representation) const {
85   DCHECK(descriptor.is_found());
86   // |location| is just a pre-fetched GetDetails(descriptor).location().
87   DCHECK_EQ(location, GetDetails(descriptor).location());
88   if (location == kField) {
89     return handle(GetFieldType(descriptor), isolate_);
90   } else {
91     return GetValue(descriptor).OptimalType(isolate_, representation);
92   }
93 }
94 
GetOrComputeFieldType(Handle<DescriptorArray> descriptors,InternalIndex descriptor,PropertyLocation location,Representation representation)95 Handle<FieldType> MapUpdater::GetOrComputeFieldType(
96     Handle<DescriptorArray> descriptors, InternalIndex descriptor,
97     PropertyLocation location, Representation representation) {
98   // |location| is just a pre-fetched GetDetails(descriptor).location().
99   DCHECK_EQ(descriptors->GetDetails(descriptor).location(), location);
100   if (location == kField) {
101     return handle(descriptors->GetFieldType(descriptor), isolate_);
102   } else {
103     return descriptors->GetStrongValue(descriptor)
104         .OptimalType(isolate_, representation);
105   }
106 }
107 
ReconfigureToDataField(InternalIndex descriptor,PropertyAttributes attributes,PropertyConstness constness,Representation representation,Handle<FieldType> field_type)108 Handle<Map> MapUpdater::ReconfigureToDataField(InternalIndex descriptor,
109                                                PropertyAttributes attributes,
110                                                PropertyConstness constness,
111                                                Representation representation,
112                                                Handle<FieldType> field_type) {
113   DCHECK_EQ(kInitialized, state_);
114   DCHECK(descriptor.is_found());
115   DCHECK(!old_map_->is_dictionary_map());
116   modified_descriptor_ = descriptor;
117   new_kind_ = kData;
118   new_attributes_ = attributes;
119   new_location_ = kField;
120 
121   PropertyDetails old_details =
122       old_descriptors_->GetDetails(modified_descriptor_);
123 
124   // If property kind is not reconfigured merge the result with
125   // representation/field type from the old descriptor.
126   if (old_details.kind() == new_kind_) {
127     new_constness_ = GeneralizeConstness(constness, old_details.constness());
128 
129     Representation old_representation = old_details.representation();
130     new_representation_ = representation.generalize(old_representation);
131 
132     Handle<FieldType> old_field_type =
133         GetOrComputeFieldType(old_descriptors_, modified_descriptor_,
134                               old_details.location(), new_representation_);
135 
136     new_field_type_ =
137         Map::GeneralizeFieldType(old_representation, old_field_type,
138                                  new_representation_, field_type, isolate_);
139   } else {
140     // We don't know if this is a first property kind reconfiguration
141     // and we don't know which value was in this property previously
142     // therefore we can't treat such a property as constant.
143     new_constness_ = PropertyConstness::kMutable;
144     new_representation_ = representation;
145     new_field_type_ = field_type;
146   }
147 
148   Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
149       isolate_, old_map_->instance_type(), &new_representation_,
150       &new_field_type_);
151 
152   if (TryReconfigureToDataFieldInplace() == kEnd) return result_map_;
153   if (FindRootMap() == kEnd) return result_map_;
154   if (FindTargetMap() == kEnd) return result_map_;
155   if (ConstructNewMap() == kAtIntegrityLevelSource) {
156     ConstructNewMapWithIntegrityLevelTransition();
157   }
158   DCHECK_EQ(kEnd, state_);
159   return result_map_;
160 }
161 
ReconfigureElementsKind(ElementsKind elements_kind)162 Handle<Map> MapUpdater::ReconfigureElementsKind(ElementsKind elements_kind) {
163   DCHECK_EQ(kInitialized, state_);
164   new_elements_kind_ = elements_kind;
165   is_transitionable_fast_elements_kind_ =
166       IsTransitionableFastElementsKind(new_elements_kind_);
167 
168   if (FindRootMap() == kEnd) return result_map_;
169   if (FindTargetMap() == kEnd) return result_map_;
170   if (ConstructNewMap() == kAtIntegrityLevelSource) {
171     ConstructNewMapWithIntegrityLevelTransition();
172   }
173   DCHECK_EQ(kEnd, state_);
174   return result_map_;
175 }
176 
Update()177 Handle<Map> MapUpdater::Update() {
178   DCHECK_EQ(kInitialized, state_);
179   DCHECK(old_map_->is_deprecated());
180 
181   if (FindRootMap() == kEnd) return result_map_;
182   if (FindTargetMap() == kEnd) return result_map_;
183   if (ConstructNewMap() == kAtIntegrityLevelSource) {
184     ConstructNewMapWithIntegrityLevelTransition();
185   }
186   DCHECK_EQ(kEnd, state_);
187   if (FLAG_fast_map_update) {
188     TransitionsAccessor(isolate_, old_map_).SetMigrationTarget(*result_map_);
189   }
190   return result_map_;
191 }
192 
GeneralizeField(Handle<Map> map,InternalIndex modify_index,PropertyConstness new_constness,Representation new_representation,Handle<FieldType> new_field_type)193 void MapUpdater::GeneralizeField(Handle<Map> map, InternalIndex modify_index,
194                                  PropertyConstness new_constness,
195                                  Representation new_representation,
196                                  Handle<FieldType> new_field_type) {
197   Map::GeneralizeField(isolate_, map, modify_index, new_constness,
198                        new_representation, new_field_type);
199 
200   DCHECK(*old_descriptors_ == old_map_->instance_descriptors(kRelaxedLoad) ||
201          *old_descriptors_ ==
202              integrity_source_map_->instance_descriptors(kRelaxedLoad));
203 }
204 
Normalize(const char * reason)205 MapUpdater::State MapUpdater::Normalize(const char* reason) {
206   result_map_ = Map::Normalize(isolate_, old_map_, new_elements_kind_,
207                                CLEAR_INOBJECT_PROPERTIES, reason);
208   state_ = kEnd;
209   return state_;  // Done.
210 }
211 
TryReconfigureToDataFieldInplace()212 MapUpdater::State MapUpdater::TryReconfigureToDataFieldInplace() {
213   // Updating deprecated maps in-place doesn't make sense.
214   if (old_map_->is_deprecated()) return state_;
215 
216   if (new_representation_.IsNone()) return state_;  // Not done yet.
217 
218   PropertyDetails old_details =
219       old_descriptors_->GetDetails(modified_descriptor_);
220   Representation old_representation = old_details.representation();
221   if (!old_representation.CanBeInPlaceChangedTo(new_representation_)) {
222     return state_;  // Not done yet.
223   }
224 
225   DCHECK_EQ(new_kind_, old_details.kind());
226   DCHECK_EQ(new_attributes_, old_details.attributes());
227   DCHECK_EQ(kField, old_details.location());
228   if (FLAG_trace_generalization) {
229     old_map_->PrintGeneralization(
230         isolate_, stdout, "uninitialized field", modified_descriptor_, old_nof_,
231         old_nof_, false, old_representation, new_representation_,
232         old_details.constness(), new_constness_,
233         handle(old_descriptors_->GetFieldType(modified_descriptor_), isolate_),
234         MaybeHandle<Object>(), new_field_type_, MaybeHandle<Object>());
235   }
236   GeneralizeField(old_map_, modified_descriptor_, new_constness_,
237                   new_representation_, new_field_type_);
238   // Check that the descriptor array was updated.
239   DCHECK(old_descriptors_->GetDetails(modified_descriptor_)
240              .representation()
241              .Equals(new_representation_));
242   DCHECK(old_descriptors_->GetFieldType(modified_descriptor_)
243              .NowIs(new_field_type_));
244 
245   result_map_ = old_map_;
246   state_ = kEnd;
247   return state_;  // Done.
248 }
249 
TrySaveIntegrityLevelTransitions()250 bool MapUpdater::TrySaveIntegrityLevelTransitions() {
251   // Figure out the most restrictive integrity level transition (it should
252   // be the last one in the transition tree).
253   Handle<Map> previous =
254       handle(Map::cast(old_map_->GetBackPointer()), isolate_);
255   Symbol integrity_level_symbol;
256   TransitionsAccessor last_transitions(isolate_, previous);
257   if (!last_transitions.HasIntegrityLevelTransitionTo(
258           *old_map_, &integrity_level_symbol, &integrity_level_)) {
259     // The last transition was not integrity level transition - just bail out.
260     // This can happen in the following cases:
261     // - there are private symbol transitions following the integrity level
262     //   transitions (see crbug.com/v8/8854).
263     // - there is a getter added in addition to an existing setter (or a setter
264     //   in addition to an existing getter).
265     return false;
266   }
267   integrity_level_symbol_ = handle(integrity_level_symbol, isolate_);
268   integrity_source_map_ = previous;
269 
270   // Now walk up the back pointer chain and skip all integrity level
271   // transitions. If we encounter any non-integrity level transition interleaved
272   // with integrity level transitions, just bail out.
273   while (!integrity_source_map_->is_extensible()) {
274     previous =
275         handle(Map::cast(integrity_source_map_->GetBackPointer()), isolate_);
276     TransitionsAccessor transitions(isolate_, previous);
277     if (!transitions.HasIntegrityLevelTransitionTo(*integrity_source_map_)) {
278       return false;
279     }
280     integrity_source_map_ = previous;
281   }
282 
283   // Integrity-level transitions never change number of descriptors.
284   CHECK_EQ(old_map_->NumberOfOwnDescriptors(),
285            integrity_source_map_->NumberOfOwnDescriptors());
286 
287   has_integrity_level_transition_ = true;
288   old_descriptors_ = handle(
289       integrity_source_map_->instance_descriptors(kRelaxedLoad), isolate_);
290   return true;
291 }
292 
FindRootMap()293 MapUpdater::State MapUpdater::FindRootMap() {
294   DCHECK_EQ(kInitialized, state_);
295   // Check the state of the root map.
296   root_map_ = handle(old_map_->FindRootMap(isolate_), isolate_);
297   ElementsKind from_kind = root_map_->elements_kind();
298   ElementsKind to_kind = new_elements_kind_;
299 
300   if (root_map_->is_deprecated()) {
301     state_ = kEnd;
302     result_map_ = handle(
303         JSFunction::cast(root_map_->GetConstructor()).initial_map(), isolate_);
304     result_map_ = Map::AsElementsKind(isolate_, result_map_, to_kind);
305     DCHECK(result_map_->is_dictionary_map());
306     return state_;
307   }
308 
309   if (!old_map_->EquivalentToForTransition(*root_map_)) {
310     return Normalize("Normalize_NotEquivalent");
311   } else if (old_map_->is_extensible() != root_map_->is_extensible()) {
312     DCHECK(!old_map_->is_extensible());
313     DCHECK(root_map_->is_extensible());
314     // We have an integrity level transition in the tree, let us make a note
315     // of that transition to be able to replay it later.
316     if (!TrySaveIntegrityLevelTransitions()) {
317       return Normalize("Normalize_PrivateSymbolsOnNonExtensible");
318     }
319 
320     // We want to build transitions to the original element kind (before
321     // the seal transitions), so change {to_kind} accordingly.
322     DCHECK(to_kind == DICTIONARY_ELEMENTS ||
323            to_kind == SLOW_STRING_WRAPPER_ELEMENTS ||
324            IsTypedArrayElementsKind(to_kind) ||
325            IsAnyNonextensibleElementsKind(to_kind));
326     to_kind = integrity_source_map_->elements_kind();
327   }
328 
329   // TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
330   if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
331       to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
332       to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
333       !(IsTransitionableFastElementsKind(from_kind) &&
334         IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
335     return Normalize("Normalize_InvalidElementsTransition");
336   }
337 
338   int root_nof = root_map_->NumberOfOwnDescriptors();
339   if (modified_descriptor_.is_found() &&
340       modified_descriptor_.as_int() < root_nof) {
341     PropertyDetails old_details =
342         old_descriptors_->GetDetails(modified_descriptor_);
343     if (old_details.kind() != new_kind_ ||
344         old_details.attributes() != new_attributes_) {
345       return Normalize("Normalize_RootModification1");
346     }
347     if (old_details.location() != kField) {
348       return Normalize("Normalize_RootModification2");
349     }
350     if (!new_representation_.fits_into(old_details.representation())) {
351       return Normalize("Normalize_RootModification4");
352     }
353 
354     DCHECK_EQ(kData, old_details.kind());
355     DCHECK_EQ(kData, new_kind_);
356     DCHECK_EQ(kField, new_location_);
357 
358     // Modify root map in-place. The GeneralizeField method is a no-op
359     // if the {old_map_} is already general enough to hold the requested
360     // {new_constness_} and {new_field_type_}.
361     GeneralizeField(old_map_, modified_descriptor_, new_constness_,
362                     old_details.representation(), new_field_type_);
363   }
364 
365   // From here on, use the map with correct elements kind as root map.
366   root_map_ = Map::AsElementsKind(isolate_, root_map_, to_kind);
367   state_ = kAtRootMap;
368   return state_;  // Not done yet.
369 }
370 
FindTargetMap()371 MapUpdater::State MapUpdater::FindTargetMap() {
372   DCHECK_EQ(kAtRootMap, state_);
373   target_map_ = root_map_;
374 
375   int root_nof = root_map_->NumberOfOwnDescriptors();
376   for (InternalIndex i : InternalIndex::Range(root_nof, old_nof_)) {
377     PropertyDetails old_details = GetDetails(i);
378     Map transition = TransitionsAccessor(isolate_, target_map_)
379                          .SearchTransition(GetKey(i), old_details.kind(),
380                                            old_details.attributes());
381     if (transition.is_null()) break;
382     Handle<Map> tmp_map(transition, isolate_);
383 
384     Handle<DescriptorArray> tmp_descriptors(
385         tmp_map->instance_descriptors(kRelaxedLoad), isolate_);
386 
387     // Check if target map is incompatible.
388     PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
389     DCHECK_EQ(old_details.kind(), tmp_details.kind());
390     DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
391     if (old_details.kind() == kAccessor &&
392         !EqualImmutableValues(GetValue(i),
393                               tmp_descriptors->GetStrongValue(i))) {
394       // TODO(ishell): mutable accessors are not implemented yet.
395       return Normalize("Normalize_Incompatible");
396     }
397     if (!IsGeneralizableTo(old_details.location(), tmp_details.location())) {
398       break;
399     }
400     Representation tmp_representation = tmp_details.representation();
401     if (!old_details.representation().fits_into(tmp_representation)) {
402       // Try updating the field in-place to a generalized type.
403       Representation generalized =
404           tmp_representation.generalize(old_details.representation());
405       if (!tmp_representation.CanBeInPlaceChangedTo(generalized)) {
406         break;
407       }
408       tmp_representation = generalized;
409     }
410 
411     if (tmp_details.location() == kField) {
412       Handle<FieldType> old_field_type =
413           GetOrComputeFieldType(i, old_details.location(), tmp_representation);
414       GeneralizeField(tmp_map, i, old_details.constness(), tmp_representation,
415                       old_field_type);
416     } else {
417       // kDescriptor: Check that the value matches.
418       if (!EqualImmutableValues(GetValue(i),
419                                 tmp_descriptors->GetStrongValue(i))) {
420         break;
421       }
422     }
423     DCHECK(!tmp_map->is_deprecated());
424     target_map_ = tmp_map;
425   }
426 
427   // Directly change the map if the target map is more general.
428   int target_nof = target_map_->NumberOfOwnDescriptors();
429   if (target_nof == old_nof_) {
430 #ifdef DEBUG
431     if (modified_descriptor_.is_found()) {
432       DescriptorArray target_descriptors =
433           target_map_->instance_descriptors(kRelaxedLoad);
434       PropertyDetails details =
435           target_descriptors.GetDetails(modified_descriptor_);
436       DCHECK_EQ(new_kind_, details.kind());
437       DCHECK_EQ(GetDetails(modified_descriptor_).attributes(),
438                 details.attributes());
439       DCHECK(IsGeneralizableTo(new_constness_, details.constness()));
440       DCHECK_EQ(new_location_, details.location());
441       DCHECK(new_representation_.fits_into(details.representation()));
442       if (new_location_ == kField) {
443         DCHECK_EQ(kField, details.location());
444         DCHECK(new_field_type_->NowIs(
445             target_descriptors.GetFieldType(modified_descriptor_)));
446       } else {
447         DCHECK(details.location() == kField ||
448                EqualImmutableValues(
449                    *new_value_,
450                    target_descriptors.GetStrongValue(modified_descriptor_)));
451       }
452     }
453 #endif
454     if (*target_map_ != *old_map_) {
455       old_map_->NotifyLeafMapLayoutChange(isolate_);
456     }
457     if (!has_integrity_level_transition_) {
458       result_map_ = target_map_;
459       state_ = kEnd;
460       return state_;  // Done.
461     }
462 
463     // We try to replay the integrity level transition here.
464     Map transition = TransitionsAccessor(isolate_, target_map_)
465                          .SearchSpecial(*integrity_level_symbol_);
466     if (!transition.is_null()) {
467       result_map_ = handle(transition, isolate_);
468       state_ = kEnd;
469       return state_;  // Done.
470     }
471   }
472 
473   // Find the last compatible target map in the transition tree.
474   for (InternalIndex i : InternalIndex::Range(target_nof, old_nof_)) {
475     PropertyDetails old_details = GetDetails(i);
476     Map transition = TransitionsAccessor(isolate_, target_map_)
477                          .SearchTransition(GetKey(i), old_details.kind(),
478                                            old_details.attributes());
479     if (transition.is_null()) break;
480     Handle<Map> tmp_map(transition, isolate_);
481     Handle<DescriptorArray> tmp_descriptors(
482         tmp_map->instance_descriptors(kRelaxedLoad), isolate_);
483 #ifdef DEBUG
484     // Check that target map is compatible.
485     PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
486     DCHECK_EQ(old_details.kind(), tmp_details.kind());
487     DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
488 #endif
489     if (old_details.kind() == kAccessor &&
490         !EqualImmutableValues(GetValue(i),
491                               tmp_descriptors->GetStrongValue(i))) {
492       return Normalize("Normalize_Incompatible");
493     }
494     DCHECK(!tmp_map->is_deprecated());
495     target_map_ = tmp_map;
496   }
497 
498   state_ = kAtTargetMap;
499   return state_;  // Not done yet.
500 }
501 
BuildDescriptorArray()502 Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() {
503   InstanceType instance_type = old_map_->instance_type();
504   int target_nof = target_map_->NumberOfOwnDescriptors();
505   Handle<DescriptorArray> target_descriptors(
506       target_map_->instance_descriptors(kRelaxedLoad), isolate_);
507 
508   // Allocate a new descriptor array large enough to hold the required
509   // descriptors, with minimally the exact same size as the old descriptor
510   // array.
511   int new_slack =
512       std::max<int>(old_nof_, old_descriptors_->number_of_descriptors()) -
513       old_nof_;
514   Handle<DescriptorArray> new_descriptors =
515       DescriptorArray::Allocate(isolate_, old_nof_, new_slack);
516   DCHECK(new_descriptors->number_of_all_descriptors() >
517              target_descriptors->number_of_all_descriptors() ||
518          new_descriptors->number_of_slack_descriptors() > 0 ||
519          new_descriptors->number_of_descriptors() ==
520              old_descriptors_->number_of_descriptors());
521   DCHECK(new_descriptors->number_of_descriptors() == old_nof_);
522 
523   int root_nof = root_map_->NumberOfOwnDescriptors();
524 
525   // Given that we passed root modification check in FindRootMap() so
526   // the root descriptors are either not modified at all or already more
527   // general than we requested. Take |root_nof| entries as is.
528   // 0 -> |root_nof|
529   int current_offset = 0;
530   for (InternalIndex i : InternalIndex::Range(root_nof)) {
531     PropertyDetails old_details = old_descriptors_->GetDetails(i);
532     if (old_details.location() == kField) {
533       current_offset += old_details.field_width_in_words();
534     }
535     Descriptor d(handle(GetKey(i), isolate_),
536                  MaybeObjectHandle(old_descriptors_->GetValue(i), isolate_),
537                  old_details);
538     new_descriptors->Set(i, &d);
539   }
540 
541   // Merge "updated" old_descriptor entries with target_descriptor entries.
542   // |root_nof| -> |target_nof|
543   for (InternalIndex i : InternalIndex::Range(root_nof, target_nof)) {
544     Handle<Name> key(GetKey(i), isolate_);
545     PropertyDetails old_details = GetDetails(i);
546     PropertyDetails target_details = target_descriptors->GetDetails(i);
547 
548     PropertyKind next_kind = old_details.kind();
549     PropertyAttributes next_attributes = old_details.attributes();
550     DCHECK_EQ(next_kind, target_details.kind());
551     DCHECK_EQ(next_attributes, target_details.attributes());
552 
553     PropertyConstness next_constness = GeneralizeConstness(
554         old_details.constness(), target_details.constness());
555 
556     // Note: failed values equality check does not invalidate per-object
557     // property constness.
558     PropertyLocation next_location =
559         old_details.location() == kField ||
560                 target_details.location() == kField ||
561                 !EqualImmutableValues(target_descriptors->GetStrongValue(i),
562                                       GetValue(i))
563             ? kField
564             : kDescriptor;
565 
566     // Ensure that mutable values are stored in fields.
567     DCHECK_IMPLIES(next_constness == PropertyConstness::kMutable,
568                    next_location == kField);
569 
570     Representation next_representation =
571         old_details.representation().generalize(
572             target_details.representation());
573 
574     if (next_location == kField) {
575       Handle<FieldType> old_field_type =
576           GetOrComputeFieldType(i, old_details.location(), next_representation);
577 
578       Handle<FieldType> target_field_type =
579           GetOrComputeFieldType(target_descriptors, i,
580                                 target_details.location(), next_representation);
581 
582       Handle<FieldType> next_field_type = Map::GeneralizeFieldType(
583           old_details.representation(), old_field_type, next_representation,
584           target_field_type, isolate_);
585 
586       Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
587           isolate_, instance_type, &next_representation, &next_field_type);
588 
589       MaybeObjectHandle wrapped_type(
590           Map::WrapFieldType(isolate_, next_field_type));
591       Descriptor d;
592       if (next_kind == kData) {
593         d = Descriptor::DataField(key, current_offset, next_attributes,
594                                   next_constness, next_representation,
595                                   wrapped_type);
596       } else {
597         // TODO(ishell): mutable accessors are not implemented yet.
598         UNIMPLEMENTED();
599       }
600       current_offset += d.GetDetails().field_width_in_words();
601       new_descriptors->Set(i, &d);
602     } else {
603       DCHECK_EQ(kDescriptor, next_location);
604       DCHECK_EQ(PropertyConstness::kConst, next_constness);
605 
606       Handle<Object> value(GetValue(i), isolate_);
607       DCHECK_EQ(kAccessor, next_kind);
608       Descriptor d = Descriptor::AccessorConstant(key, value, next_attributes);
609       new_descriptors->Set(i, &d);
610     }
611   }
612 
613   // Take "updated" old_descriptor entries.
614   // |target_nof| -> |old_nof|
615   for (InternalIndex i : InternalIndex::Range(target_nof, old_nof_)) {
616     PropertyDetails old_details = GetDetails(i);
617     Handle<Name> key(GetKey(i), isolate_);
618 
619     PropertyKind next_kind = old_details.kind();
620     PropertyAttributes next_attributes = old_details.attributes();
621     PropertyConstness next_constness = old_details.constness();
622     PropertyLocation next_location = old_details.location();
623     Representation next_representation = old_details.representation();
624 
625     Descriptor d;
626     if (next_location == kField) {
627       Handle<FieldType> next_field_type =
628           GetOrComputeFieldType(i, old_details.location(), next_representation);
629 
630       // If the |new_elements_kind_| is still transitionable then the old map's
631       // elements kind is also transitionable and therefore the old descriptors
632       // array must already have generalized field type.
633       CHECK_IMPLIES(
634           is_transitionable_fast_elements_kind_,
635           Map::IsMostGeneralFieldType(next_representation, *next_field_type));
636 
637       MaybeObjectHandle wrapped_type(
638           Map::WrapFieldType(isolate_, next_field_type));
639       Descriptor d;
640       if (next_kind == kData) {
641         d = Descriptor::DataField(key, current_offset, next_attributes,
642                                   next_constness, next_representation,
643                                   wrapped_type);
644       } else {
645         // TODO(ishell): mutable accessors are not implemented yet.
646         UNIMPLEMENTED();
647       }
648       current_offset += d.GetDetails().field_width_in_words();
649       new_descriptors->Set(i, &d);
650     } else {
651       DCHECK_EQ(kDescriptor, next_location);
652       DCHECK_EQ(PropertyConstness::kConst, next_constness);
653 
654       Handle<Object> value(GetValue(i), isolate_);
655       if (next_kind == kData) {
656         d = Descriptor::DataConstant(key, value, next_attributes);
657       } else {
658         DCHECK_EQ(kAccessor, next_kind);
659         d = Descriptor::AccessorConstant(key, value, next_attributes);
660       }
661       new_descriptors->Set(i, &d);
662     }
663   }
664 
665   new_descriptors->Sort();
666   return new_descriptors;
667 }
668 
FindSplitMap(Handle<DescriptorArray> descriptors)669 Handle<Map> MapUpdater::FindSplitMap(Handle<DescriptorArray> descriptors) {
670   DisallowHeapAllocation no_allocation;
671 
672   int root_nof = root_map_->NumberOfOwnDescriptors();
673   Map current = *root_map_;
674   for (InternalIndex i : InternalIndex::Range(root_nof, old_nof_)) {
675     Name name = descriptors->GetKey(i);
676     PropertyDetails details = descriptors->GetDetails(i);
677     Map next =
678         TransitionsAccessor(isolate_, current, &no_allocation)
679             .SearchTransition(name, details.kind(), details.attributes());
680     if (next.is_null()) break;
681     DescriptorArray next_descriptors = next.instance_descriptors(kRelaxedLoad);
682 
683     PropertyDetails next_details = next_descriptors.GetDetails(i);
684     DCHECK_EQ(details.kind(), next_details.kind());
685     DCHECK_EQ(details.attributes(), next_details.attributes());
686     if (details.constness() != next_details.constness()) break;
687     if (details.location() != next_details.location()) break;
688     if (!details.representation().Equals(next_details.representation())) break;
689 
690     if (next_details.location() == kField) {
691       FieldType next_field_type = next_descriptors.GetFieldType(i);
692       if (!descriptors->GetFieldType(i).NowIs(next_field_type)) {
693         break;
694       }
695     } else {
696       if (!EqualImmutableValues(descriptors->GetStrongValue(i),
697                                 next_descriptors.GetStrongValue(i))) {
698         break;
699       }
700     }
701     current = next;
702   }
703   return handle(current, isolate_);
704 }
705 
ConstructNewMap()706 MapUpdater::State MapUpdater::ConstructNewMap() {
707   Handle<DescriptorArray> new_descriptors = BuildDescriptorArray();
708 
709   Handle<Map> split_map = FindSplitMap(new_descriptors);
710   int split_nof = split_map->NumberOfOwnDescriptors();
711   if (old_nof_ == split_nof) {
712     CHECK(has_integrity_level_transition_);
713     state_ = kAtIntegrityLevelSource;
714     return state_;
715   }
716   InternalIndex split_index(split_nof);
717   PropertyDetails split_details = GetDetails(split_index);
718   TransitionsAccessor transitions(isolate_, split_map);
719 
720   // Invalidate a transition target at |key|.
721   Handle<Map> maybe_transition(
722       transitions.SearchTransition(GetKey(split_index), split_details.kind(),
723                                    split_details.attributes()),
724       isolate_);
725   if (!maybe_transition->is_null()) {
726     maybe_transition->DeprecateTransitionTree(isolate_);
727   }
728 
729   // If |maybe_transition| is not nullptr then the transition array already
730   // contains entry for given descriptor. This means that the transition
731   // could be inserted regardless of whether transitions array is full or not.
732   if (maybe_transition->is_null() && !transitions.CanHaveMoreTransitions()) {
733     return Normalize("Normalize_CantHaveMoreTransitions");
734   }
735 
736   old_map_->NotifyLeafMapLayoutChange(isolate_);
737 
738   if (FLAG_trace_generalization && modified_descriptor_.is_found()) {
739     PropertyDetails old_details =
740         old_descriptors_->GetDetails(modified_descriptor_);
741     PropertyDetails new_details =
742         new_descriptors->GetDetails(modified_descriptor_);
743     MaybeHandle<FieldType> old_field_type;
744     MaybeHandle<FieldType> new_field_type;
745     MaybeHandle<Object> old_value;
746     MaybeHandle<Object> new_value;
747     if (old_details.location() == kField) {
748       old_field_type = handle(
749           old_descriptors_->GetFieldType(modified_descriptor_), isolate_);
750     } else {
751       old_value = handle(old_descriptors_->GetStrongValue(modified_descriptor_),
752                          isolate_);
753     }
754     if (new_details.location() == kField) {
755       new_field_type =
756           handle(new_descriptors->GetFieldType(modified_descriptor_), isolate_);
757     } else {
758       new_value = handle(new_descriptors->GetStrongValue(modified_descriptor_),
759                          isolate_);
760     }
761 
762     old_map_->PrintGeneralization(
763         isolate_, stdout, "", modified_descriptor_, split_nof, old_nof_,
764         old_details.location() == kDescriptor && new_location_ == kField,
765         old_details.representation(), new_details.representation(),
766         old_details.constness(), new_details.constness(), old_field_type,
767         old_value, new_field_type, new_value);
768   }
769 
770   Handle<LayoutDescriptor> new_layout_descriptor =
771       LayoutDescriptor::New(isolate_, split_map, new_descriptors, old_nof_);
772 
773   Handle<Map> new_map = Map::AddMissingTransitions(
774       isolate_, split_map, new_descriptors, new_layout_descriptor);
775 
776   // Deprecated part of the transition tree is no longer reachable, so replace
777   // current instance descriptors in the "survived" part of the tree with
778   // the new descriptors to maintain descriptors sharing invariant.
779   split_map->ReplaceDescriptors(isolate_, *new_descriptors,
780                                 *new_layout_descriptor);
781 
782   if (has_integrity_level_transition_) {
783     target_map_ = new_map;
784     state_ = kAtIntegrityLevelSource;
785   } else {
786     result_map_ = new_map;
787     state_ = kEnd;
788   }
789   return state_;  // Done.
790 }
791 
ConstructNewMapWithIntegrityLevelTransition()792 MapUpdater::State MapUpdater::ConstructNewMapWithIntegrityLevelTransition() {
793   DCHECK_EQ(kAtIntegrityLevelSource, state_);
794 
795   TransitionsAccessor transitions(isolate_, target_map_);
796   if (!transitions.CanHaveMoreTransitions()) {
797     return Normalize("Normalize_CantHaveMoreTransitions");
798   }
799 
800   result_map_ = Map::CopyForPreventExtensions(
801       isolate_, target_map_, integrity_level_, integrity_level_symbol_,
802       "CopyForPreventExtensions",
803       old_map_->elements_kind() == DICTIONARY_ELEMENTS);
804   DCHECK_IMPLIES(old_map_->elements_kind() == DICTIONARY_ELEMENTS,
805                  result_map_->elements_kind() == DICTIONARY_ELEMENTS);
806 
807   state_ = kEnd;
808   return state_;
809 }
810 
811 }  // namespace internal
812 }  // namespace v8
813