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