Line data Source code
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 :
19 : inline bool EqualImmutableValues(Object obj1, Object obj2) {
20 16591 : 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 :
27 594308 : 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 4160182 : 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 :
41 646785 : Name MapUpdater::GetKey(int descriptor) const {
42 646785 : return old_descriptors_->GetKey(descriptor);
43 : }
44 :
45 645481 : PropertyDetails MapUpdater::GetDetails(int descriptor) const {
46 : DCHECK_LE(0, descriptor);
47 645481 : if (descriptor == modified_descriptor_) {
48 128002 : 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 128002 : if (integrity_level_ == SEALED || integrity_level_ == FROZEN) {
54 518 : attributes = old_descriptors_->GetDetails(descriptor).attributes();
55 : }
56 : return PropertyDetails(new_kind_, attributes, new_location_, new_constness_,
57 256004 : new_representation_);
58 : }
59 517480 : return old_descriptors_->GetDetails(descriptor);
60 : }
61 :
62 16683 : Object MapUpdater::GetValue(int descriptor) const {
63 : DCHECK_LE(0, descriptor);
64 16683 : if (descriptor == modified_descriptor_) {
65 : DCHECK_EQ(kDescriptor, new_location_);
66 : return *new_value_;
67 : }
68 : DCHECK_EQ(kDescriptor, GetDetails(descriptor).location());
69 33366 : return old_descriptors_->GetStrongValue(descriptor);
70 : }
71 :
72 579303 : FieldType MapUpdater::GetFieldType(int descriptor) const {
73 : DCHECK_LE(0, descriptor);
74 579303 : if (descriptor == modified_descriptor_) {
75 : DCHECK_EQ(kField, new_location_);
76 : return *new_field_type_;
77 : }
78 : DCHECK_EQ(kField, GetDetails(descriptor).location());
79 484238 : return old_descriptors_->GetFieldType(descriptor);
80 : }
81 :
82 579304 : Handle<FieldType> MapUpdater::GetOrComputeFieldType(
83 : int descriptor, PropertyLocation location,
84 : Representation representation) const {
85 : DCHECK_LE(0, descriptor);
86 : // |location| is just a pre-fetched GetDetails(descriptor).location().
87 : DCHECK_EQ(location, GetDetails(descriptor).location());
88 579304 : if (location == kField) {
89 579304 : return handle(GetFieldType(descriptor), isolate_);
90 : } else {
91 0 : return GetValue(descriptor)->OptimalType(isolate_, representation);
92 : }
93 : }
94 :
95 347732 : Handle<FieldType> MapUpdater::GetOrComputeFieldType(
96 : Handle<DescriptorArray> descriptors, int 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 347732 : if (location == kField) {
101 1043199 : return handle(descriptors->GetFieldType(descriptor), isolate_);
102 : } else {
103 0 : return descriptors->GetStrongValue(descriptor)
104 0 : ->OptimalType(isolate_, representation);
105 : }
106 : }
107 :
108 296064 : Handle<Map> MapUpdater::ReconfigureToDataField(int descriptor,
109 : PropertyAttributes attributes,
110 : PropertyConstness constness,
111 : Representation representation,
112 : Handle<FieldType> field_type) {
113 : DCHECK_EQ(kInitialized, state_);
114 : DCHECK_LE(0, descriptor);
115 : DCHECK(!old_map_->is_dictionary_map());
116 296064 : modified_descriptor_ = descriptor;
117 296064 : new_kind_ = kData;
118 296064 : new_attributes_ = attributes;
119 296064 : new_location_ = kField;
120 :
121 : PropertyDetails old_details =
122 296065 : 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 296069 : if (old_details.kind() == new_kind_) {
127 281047 : new_constness_ = GeneralizeConstness(constness, old_details.constness());
128 :
129 281047 : Representation old_representation = old_details.representation();
130 281047 : new_representation_ = representation.generalize(old_representation);
131 :
132 : Handle<FieldType> old_field_type =
133 : GetOrComputeFieldType(old_descriptors_, modified_descriptor_,
134 281044 : old_details.location(), new_representation_);
135 :
136 : new_field_type_ =
137 : Map::GeneralizeFieldType(old_representation, old_field_type,
138 281047 : 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 15022 : new_constness_ = PropertyConstness::kMutable;
144 15022 : new_representation_ = representation;
145 15022 : new_field_type_ = field_type;
146 : }
147 :
148 : Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
149 : isolate_, old_map_->instance_type(), &new_constness_,
150 592142 : &new_representation_, &new_field_type_);
151 :
152 296069 : if (TryReconfigureToDataFieldInplace() == kEnd) return result_map_;
153 121991 : if (FindRootMap() == kEnd) return result_map_;
154 106022 : if (FindTargetMap() == kEnd) return result_map_;
155 11055 : if (ConstructNewMap() == kAtIntegrityLevelSource) {
156 76 : ConstructNewMapWithIntegrityLevelTransition();
157 : }
158 : DCHECK_EQ(kEnd, state_);
159 11055 : return result_map_;
160 : }
161 :
162 295076 : Handle<Map> MapUpdater::ReconfigureElementsKind(ElementsKind elements_kind) {
163 : DCHECK_EQ(kInitialized, state_);
164 295076 : new_elements_kind_ = elements_kind;
165 : is_transitionable_fast_elements_kind_ =
166 295076 : IsTransitionableFastElementsKind(new_elements_kind_);
167 :
168 295076 : if (FindRootMap() == kEnd) return result_map_;
169 295076 : if (FindTargetMap() == kEnd) return result_map_;
170 854 : if (ConstructNewMap() == kAtIntegrityLevelSource) {
171 0 : ConstructNewMapWithIntegrityLevelTransition();
172 : }
173 : DCHECK_EQ(kEnd, state_);
174 854 : return result_map_;
175 : }
176 :
177 3168 : Handle<Map> MapUpdater::Update() {
178 : DCHECK_EQ(kInitialized, state_);
179 : DCHECK(old_map_->is_deprecated());
180 :
181 3168 : if (FindRootMap() == kEnd) return result_map_;
182 3168 : if (FindTargetMap() == kEnd) return result_map_;
183 353 : if (ConstructNewMap() == kAtIntegrityLevelSource) {
184 210 : ConstructNewMapWithIntegrityLevelTransition();
185 : }
186 : DCHECK_EQ(kEnd, state_);
187 : if (FLAG_fast_map_update) {
188 : TransitionsAccessor(isolate_, old_map_).SetMigrationTarget(*result_map_);
189 : }
190 353 : return result_map_;
191 : }
192 :
193 0 : void MapUpdater::GeneralizeField(Handle<Map> map, int 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 695375 : new_representation, new_field_type);
199 :
200 : DCHECK(*old_descriptors_ == old_map_->instance_descriptors() ||
201 : *old_descriptors_ == integrity_source_map_->instance_descriptors());
202 0 : }
203 :
204 16015 : MapUpdater::State MapUpdater::CopyGeneralizeAllFields(const char* reason) {
205 : result_map_ = Map::CopyGeneralizeAllFields(
206 : isolate_, old_map_, new_elements_kind_, modified_descriptor_, new_kind_,
207 16015 : new_attributes_, reason);
208 16015 : state_ = kEnd;
209 16015 : return state_; // Done.
210 : }
211 :
212 296066 : MapUpdater::State MapUpdater::TryReconfigureToDataFieldInplace() {
213 : // If it's just a representation generalization case (i.e. property kind and
214 : // attributes stays unchanged) it's fine to transition from None to anything
215 : // but double without any modification to the object, because the default
216 : // uninitialized value for representation None can be overwritten by both
217 : // smi and tagged values. Doubles, however, would require a box allocation.
218 296066 : if (new_representation_.IsNone() || new_representation_.IsDouble()) {
219 27628 : return state_; // Not done yet.
220 : }
221 :
222 : PropertyDetails old_details =
223 536878 : old_descriptors_->GetDetails(modified_descriptor_);
224 : Representation old_representation = old_details.representation();
225 268442 : if (!old_representation.IsNone()) {
226 94364 : return state_; // Not done yet.
227 : }
228 :
229 : DCHECK_EQ(new_kind_, old_details.kind());
230 : DCHECK_EQ(new_attributes_, old_details.attributes());
231 : DCHECK_EQ(kField, old_details.location());
232 174078 : if (FLAG_trace_generalization) {
233 : old_map_->PrintGeneralization(
234 : isolate_, stdout, "uninitialized field", modified_descriptor_, old_nof_,
235 : old_nof_, false, old_representation, new_representation_,
236 : handle(old_descriptors_->GetFieldType(modified_descriptor_), isolate_),
237 0 : MaybeHandle<Object>(), new_field_type_, MaybeHandle<Object>());
238 : }
239 : Handle<Map> field_owner(
240 522225 : old_map_->FindFieldOwner(isolate_, modified_descriptor_), isolate_);
241 :
242 : GeneralizeField(field_owner, modified_descriptor_, new_constness_,
243 174073 : new_representation_, new_field_type_);
244 : // Check that the descriptor array was updated.
245 : DCHECK(old_descriptors_->GetDetails(modified_descriptor_)
246 : .representation()
247 : .Equals(new_representation_));
248 : DCHECK(old_descriptors_->GetFieldType(modified_descriptor_)
249 : ->NowIs(new_field_type_));
250 :
251 174075 : result_map_ = old_map_;
252 174075 : state_ = kEnd;
253 174075 : return state_; // Done.
254 : }
255 :
256 414 : bool MapUpdater::TrySaveIntegrityLevelTransitions() {
257 : // Skip integrity level transitions.
258 414 : integrity_source_map_ = old_map_;
259 1269 : while (!integrity_source_map_->is_extensible()) {
260 : integrity_source_map_ =
261 1323 : handle(Map::cast(integrity_source_map_->GetBackPointer()), isolate_);
262 : }
263 :
264 : // If there are some non-integrity-level transitions after the first
265 : // non-extensible transitions, e.g., if there were private symbols transitions
266 : // after the first integrity level transition, then we just bail out and
267 : // generalize all the fields.
268 414 : if (old_map_->NumberOfOwnDescriptors() !=
269 : integrity_source_map_->NumberOfOwnDescriptors()) {
270 : return false;
271 : }
272 :
273 : // Figure out the most restrictive integrity level transition (it should
274 : // be the last one in the transition tree).
275 396 : ReadOnlyRoots roots(isolate_);
276 : TransitionsAccessor transitions(
277 792 : isolate_, handle(Map::cast(old_map_->GetBackPointer()), isolate_));
278 792 : if (transitions.SearchSpecial(roots.frozen_symbol()) == *old_map_) {
279 79 : integrity_level_ = FROZEN;
280 158 : integrity_level_symbol_ = isolate_->factory()->frozen_symbol();
281 634 : } else if (transitions.SearchSpecial(roots.sealed_symbol()) == *old_map_) {
282 193 : integrity_level_ = SEALED;
283 386 : integrity_level_symbol_ = isolate_->factory()->sealed_symbol();
284 : } else {
285 372 : CHECK_EQ(transitions.SearchSpecial(roots.nonextensible_symbol()),
286 : *old_map_);
287 124 : integrity_level_ = NONE;
288 248 : integrity_level_symbol_ = isolate_->factory()->nonextensible_symbol();
289 : }
290 :
291 396 : has_integrity_level_transition_ = true;
292 : old_descriptors_ =
293 1188 : handle(integrity_source_map_->instance_descriptors(), isolate_);
294 396 : return true;
295 : }
296 :
297 420235 : MapUpdater::State MapUpdater::FindRootMap() {
298 : DCHECK_EQ(kInitialized, state_);
299 : // Check the state of the root map.
300 1260707 : root_map_ = handle(old_map_->FindRootMap(isolate_), isolate_);
301 : ElementsKind from_kind = root_map_->elements_kind();
302 420237 : ElementsKind to_kind = new_elements_kind_;
303 :
304 420238 : if (root_map_->is_deprecated()) {
305 0 : state_ = kEnd;
306 : result_map_ = handle(
307 0 : JSFunction::cast(root_map_->GetConstructor())->initial_map(), isolate_);
308 0 : result_map_ = Map::AsElementsKind(isolate_, result_map_, to_kind);
309 : DCHECK(result_map_->is_dictionary_map());
310 0 : return state_;
311 : }
312 :
313 420237 : if (!old_map_->EquivalentToForTransition(*root_map_)) {
314 18 : return CopyGeneralizeAllFields("GenAll_NotEquivalent");
315 420220 : } else if (old_map_->is_extensible() != root_map_->is_extensible()) {
316 : DCHECK(!old_map_->is_extensible());
317 : DCHECK(root_map_->is_extensible());
318 : // We have an integrity level transition in the tree, let us make a note
319 : // of that transition to be able to replay it later.
320 414 : if (!TrySaveIntegrityLevelTransitions()) {
321 18 : return CopyGeneralizeAllFields("GenAll_PrivateSymbolsOnNonExtensible");
322 : }
323 :
324 : // We want to build transitions to the original element kind (before
325 : // the seal transitions), so change {to_kind} accordingly.
326 : DCHECK(to_kind == DICTIONARY_ELEMENTS ||
327 : to_kind == SLOW_STRING_WRAPPER_ELEMENTS ||
328 : IsFixedTypedArrayElementsKind(to_kind));
329 : to_kind = integrity_source_map_->elements_kind();
330 : }
331 :
332 : // TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
333 840404 : if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
334 12688 : to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
335 432697 : to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
336 : !(IsTransitionableFastElementsKind(from_kind) &&
337 6151 : IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
338 0 : return CopyGeneralizeAllFields("GenAll_InvalidElementsTransition");
339 : }
340 :
341 : int root_nof = root_map_->NumberOfOwnDescriptors();
342 420202 : if (modified_descriptor_ >= 0 && modified_descriptor_ < root_nof) {
343 : PropertyDetails old_details =
344 26938 : old_descriptors_->GetDetails(modified_descriptor_);
345 38932 : if (old_details.kind() != new_kind_ ||
346 11994 : old_details.attributes() != new_attributes_) {
347 30877 : return CopyGeneralizeAllFields("GenAll_RootModification1");
348 : }
349 11994 : if (old_details.location() != kField) {
350 0 : return CopyGeneralizeAllFields("GenAll_RootModification2");
351 : }
352 : if (new_constness_ != old_details.constness() && !FLAG_modify_map_inplace) {
353 : return CopyGeneralizeAllFields("GenAll_RootModification3");
354 : }
355 23988 : if (!new_representation_.fits_into(old_details.representation())) {
356 185 : return CopyGeneralizeAllFields("GenAll_RootModification4");
357 : }
358 :
359 : DCHECK_EQ(kData, old_details.kind());
360 : DCHECK_EQ(kData, new_kind_);
361 : DCHECK_EQ(kField, new_location_);
362 : FieldType old_field_type =
363 23618 : old_descriptors_->GetFieldType(modified_descriptor_);
364 11809 : if (!new_field_type_->NowIs(old_field_type)) {
365 804 : return CopyGeneralizeAllFields("GenAll_RootModification5");
366 : }
367 :
368 : // Modify root map in-place.
369 22010 : if (FLAG_modify_map_inplace && new_constness_ != old_details.constness()) {
370 : DCHECK(old_map_->is_stable());
371 : DCHECK(IsGeneralizableTo(old_details.constness(), new_constness_));
372 : GeneralizeField(old_map_, modified_descriptor_, new_constness_,
373 : old_details.representation(),
374 22010 : handle(old_field_type, isolate_));
375 : }
376 : }
377 :
378 : // From here on, use the map with correct elements kind as root map.
379 404269 : root_map_ = Map::AsElementsKind(isolate_, root_map_, to_kind);
380 404268 : state_ = kAtRootMap;
381 404268 : return state_; // Not done yet.
382 : }
383 :
384 404266 : MapUpdater::State MapUpdater::FindTargetMap() {
385 : DCHECK_EQ(kAtRootMap, state_);
386 404266 : target_map_ = root_map_;
387 :
388 : int root_nof = root_map_->NumberOfOwnDescriptors();
389 922785 : for (int i = root_nof; i < old_nof_; ++i) {
390 530609 : PropertyDetails old_details = GetDetails(i);
391 : Map transition = TransitionsAccessor(isolate_, target_map_)
392 : .SearchTransition(GetKey(i), old_details.kind(),
393 530611 : old_details.attributes());
394 530611 : if (transition.is_null()) break;
395 529033 : Handle<Map> tmp_map(transition, isolate_);
396 :
397 : Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
398 1587099 : isolate_);
399 :
400 : // Check if target map is incompatible.
401 529034 : PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
402 : DCHECK_EQ(old_details.kind(), tmp_details.kind());
403 : DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
404 1587102 : if (old_details.kind() == kAccessor &&
405 : !EqualImmutableValues(GetValue(i),
406 553823 : tmp_descriptors->GetStrongValue(i))) {
407 : // TODO(ishell): mutable accessors are not implemented yet.
408 41 : return CopyGeneralizeAllFields("GenAll_Incompatible");
409 : }
410 : PropertyConstness tmp_constness = tmp_details.constness();
411 : if (!FLAG_modify_map_inplace &&
412 : !IsGeneralizableTo(old_details.constness(), tmp_constness)) {
413 : break;
414 : }
415 528993 : if (!IsGeneralizableTo(old_details.location(), tmp_details.location())) {
416 : break;
417 : }
418 528992 : Representation tmp_representation = tmp_details.representation();
419 1057984 : if (!old_details.representation().fits_into(tmp_representation)) {
420 : break;
421 : }
422 :
423 518519 : if (tmp_details.location() == kField) {
424 : Handle<FieldType> old_field_type =
425 510297 : GetOrComputeFieldType(i, old_details.location(), tmp_representation);
426 : PropertyConstness constness =
427 : FLAG_modify_map_inplace ? old_details.constness() : tmp_constness;
428 : GeneralizeField(tmp_map, i, constness, tmp_representation,
429 : old_field_type);
430 : } else {
431 : // kDescriptor: Check that the value matches.
432 8222 : if (!EqualImmutableValues(GetValue(i),
433 24666 : tmp_descriptors->GetStrongValue(i))) {
434 : break;
435 : }
436 : }
437 : DCHECK(!tmp_map->is_deprecated());
438 518519 : target_map_ = tmp_map;
439 : }
440 :
441 : // Directly change the map if the target map is more general.
442 : int target_nof = target_map_->NumberOfOwnDescriptors();
443 404227 : if (target_nof == old_nof_) {
444 : #ifdef DEBUG
445 : if (modified_descriptor_ >= 0) {
446 : DescriptorArray target_descriptors = target_map_->instance_descriptors();
447 : PropertyDetails details =
448 : target_descriptors->GetDetails(modified_descriptor_);
449 : DCHECK_EQ(new_kind_, details.kind());
450 : DCHECK_EQ(GetDetails(modified_descriptor_).attributes(),
451 : details.attributes());
452 : DCHECK(IsGeneralizableTo(new_constness_, details.constness()));
453 : DCHECK_EQ(new_location_, details.location());
454 : DCHECK(new_representation_.fits_into(details.representation()));
455 : if (new_location_ == kField) {
456 : DCHECK_EQ(kField, details.location());
457 : DCHECK(new_field_type_->NowIs(
458 : target_descriptors->GetFieldType(modified_descriptor_)));
459 : } else {
460 : DCHECK(details.location() == kField ||
461 : EqualImmutableValues(
462 : *new_value_,
463 : target_descriptors->GetStrongValue(modified_descriptor_)));
464 : }
465 : }
466 : #endif
467 392174 : if (*target_map_ != *old_map_) {
468 596034 : old_map_->NotifyLeafMapLayoutChange(isolate_);
469 : }
470 392174 : if (!has_integrity_level_transition_) {
471 391854 : result_map_ = target_map_;
472 391854 : state_ = kEnd;
473 783818 : return state_; // Done.
474 : }
475 :
476 : // We try to replay the integrity level transition here.
477 : Map transition = TransitionsAccessor(isolate_, target_map_)
478 320 : .SearchSpecial(*integrity_level_symbol_);
479 320 : if (!transition.is_null()) {
480 220 : result_map_ = handle(transition, isolate_);
481 110 : state_ = kEnd;
482 110 : return state_; // Done.
483 : }
484 : }
485 :
486 : // Find the last compatible target map in the transition tree.
487 44366 : for (int i = target_nof; i < old_nof_; ++i) {
488 33683 : PropertyDetails old_details = GetDetails(i);
489 : Map transition = TransitionsAccessor(isolate_, target_map_)
490 : .SearchTransition(GetKey(i), old_details.kind(),
491 33683 : old_details.attributes());
492 33683 : if (transition.is_null()) break;
493 32104 : Handle<Map> tmp_map(transition, isolate_);
494 : Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
495 96312 : isolate_);
496 : #ifdef DEBUG
497 : // Check that target map is compatible.
498 : PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
499 : DCHECK_EQ(old_details.kind(), tmp_details.kind());
500 : DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
501 : #endif
502 96312 : if (old_details.kind() == kAccessor &&
503 : !EqualImmutableValues(GetValue(i),
504 32149 : tmp_descriptors->GetStrongValue(i))) {
505 0 : return CopyGeneralizeAllFields("GenAll_Incompatible");
506 : }
507 : DCHECK(!tmp_map->is_deprecated());
508 32104 : target_map_ = tmp_map;
509 : }
510 :
511 12262 : state_ = kAtTargetMap;
512 12262 : return state_; // Not done yet.
513 : }
514 :
515 12262 : Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() {
516 : InstanceType instance_type = old_map_->instance_type();
517 : int target_nof = target_map_->NumberOfOwnDescriptors();
518 : Handle<DescriptorArray> target_descriptors(
519 36786 : target_map_->instance_descriptors(), isolate_);
520 :
521 : // Allocate a new descriptor array large enough to hold the required
522 : // descriptors, with minimally the exact same size as the old descriptor
523 : // array.
524 : int new_slack =
525 36786 : std::max<int>(old_nof_, old_descriptors_->number_of_descriptors()) -
526 12262 : old_nof_;
527 : Handle<DescriptorArray> new_descriptors =
528 12262 : DescriptorArray::Allocate(isolate_, old_nof_, new_slack);
529 : DCHECK(new_descriptors->number_of_all_descriptors() >
530 : target_descriptors->number_of_all_descriptors() ||
531 : new_descriptors->number_of_slack_descriptors() > 0 ||
532 : new_descriptors->number_of_descriptors() ==
533 : old_descriptors_->number_of_descriptors());
534 : DCHECK(new_descriptors->number_of_descriptors() == old_nof_);
535 :
536 : int root_nof = root_map_->NumberOfOwnDescriptors();
537 :
538 : // Given that we passed root modification check in FindRootMap() so
539 : // the root descriptors are either not modified at all or already more
540 : // general than we requested. Take |root_nof| entries as is.
541 : // 0 -> |root_nof|
542 : int current_offset = 0;
543 13564 : for (int i = 0; i < root_nof; ++i) {
544 1302 : PropertyDetails old_details = old_descriptors_->GetDetails(i);
545 1302 : if (old_details.location() == kField) {
546 352 : current_offset += old_details.field_width_in_words();
547 : }
548 : Descriptor d(handle(GetKey(i), isolate_),
549 : MaybeObjectHandle(old_descriptors_->GetValue(i), isolate_),
550 5208 : old_details);
551 1302 : new_descriptors->Set(i, &d);
552 : }
553 :
554 : // Merge "updated" old_descriptor entries with target_descriptor entries.
555 : // |root_nof| -> |target_nof|
556 66741 : for (int i = root_nof; i < target_nof; ++i) {
557 66741 : Handle<Name> key(GetKey(i), isolate_);
558 66741 : PropertyDetails old_details = GetDetails(i);
559 66741 : PropertyDetails target_details = target_descriptors->GetDetails(i);
560 :
561 : PropertyKind next_kind = old_details.kind();
562 : PropertyAttributes next_attributes = old_details.attributes();
563 : DCHECK_EQ(next_kind, target_details.kind());
564 : DCHECK_EQ(next_attributes, target_details.attributes());
565 :
566 : PropertyConstness next_constness = GeneralizeConstness(
567 : old_details.constness(), target_details.constness());
568 :
569 : // Note: failed values equality check does not invalidate per-object
570 : // property constness.
571 : PropertyLocation next_location =
572 53 : old_details.location() == kField ||
573 : target_details.location() == kField ||
574 : !EqualImmutableValues(target_descriptors->GetStrongValue(i),
575 66900 : GetValue(i))
576 : ? kField
577 133482 : : kDescriptor;
578 :
579 : if (!FLAG_track_constant_fields && next_location == kField) {
580 : next_constness = PropertyConstness::kMutable;
581 : }
582 : // Ensure that mutable values are stored in fields.
583 : DCHECK_IMPLIES(next_constness == PropertyConstness::kMutable,
584 : next_location == kField);
585 :
586 : Representation next_representation =
587 : old_details.representation().generalize(
588 66741 : target_details.representation());
589 :
590 66741 : if (next_location == kField) {
591 : Handle<FieldType> old_field_type =
592 66688 : GetOrComputeFieldType(i, old_details.location(), next_representation);
593 :
594 : Handle<FieldType> target_field_type =
595 : GetOrComputeFieldType(target_descriptors, i,
596 66688 : target_details.location(), next_representation);
597 :
598 : Handle<FieldType> next_field_type = Map::GeneralizeFieldType(
599 : old_details.representation(), old_field_type, next_representation,
600 66688 : target_field_type, isolate_);
601 :
602 : Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
603 : isolate_, instance_type, &next_constness, &next_representation,
604 66688 : &next_field_type);
605 :
606 : MaybeObjectHandle wrapped_type(
607 66688 : Map::WrapFieldType(isolate_, next_field_type));
608 66688 : Descriptor d;
609 66688 : if (next_kind == kData) {
610 : d = Descriptor::DataField(key, current_offset, next_attributes,
611 : next_constness, next_representation,
612 66688 : wrapped_type);
613 : } else {
614 : // TODO(ishell): mutable accessors are not implemented yet.
615 0 : UNIMPLEMENTED();
616 : }
617 66688 : current_offset += d.GetDetails().field_width_in_words();
618 66688 : new_descriptors->Set(i, &d);
619 : } else {
620 : DCHECK_EQ(kDescriptor, next_location);
621 : DCHECK_EQ(PropertyConstness::kConst, next_constness);
622 :
623 53 : Handle<Object> value(GetValue(i), isolate_);
624 53 : Descriptor d;
625 53 : if (next_kind == kData) {
626 : DCHECK(!FLAG_track_constant_fields);
627 0 : d = Descriptor::DataConstant(key, value, next_attributes);
628 : } else {
629 : DCHECK_EQ(kAccessor, next_kind);
630 53 : d = Descriptor::AccessorConstant(key, value, next_attributes);
631 : }
632 53 : new_descriptors->Set(i, &d);
633 : }
634 : }
635 :
636 : // Take "updated" old_descriptor entries.
637 : // |target_nof| -> |old_nof|
638 2396 : for (int i = target_nof; i < old_nof_; ++i) {
639 2396 : PropertyDetails old_details = GetDetails(i);
640 2396 : Handle<Name> key(GetKey(i), isolate_);
641 :
642 : PropertyKind next_kind = old_details.kind();
643 : PropertyAttributes next_attributes = old_details.attributes();
644 : PropertyConstness next_constness = old_details.constness();
645 : PropertyLocation next_location = old_details.location();
646 : Representation next_representation = old_details.representation();
647 :
648 2396 : Descriptor d;
649 2396 : if (next_location == kField) {
650 : Handle<FieldType> next_field_type =
651 2319 : GetOrComputeFieldType(i, old_details.location(), next_representation);
652 :
653 : // If the |new_elements_kind_| is still transitionable then the old map's
654 : // elements kind is also transitionable and therefore the old descriptors
655 : // array must already have generalized field type.
656 2718 : CHECK_IMPLIES(
657 : is_transitionable_fast_elements_kind_,
658 : Map::IsMostGeneralFieldType(next_representation, *next_field_type));
659 :
660 : MaybeObjectHandle wrapped_type(
661 2319 : Map::WrapFieldType(isolate_, next_field_type));
662 2319 : Descriptor d;
663 2319 : if (next_kind == kData) {
664 : DCHECK_IMPLIES(!FLAG_track_constant_fields,
665 : next_constness == PropertyConstness::kMutable);
666 : d = Descriptor::DataField(key, current_offset, next_attributes,
667 : next_constness, next_representation,
668 2319 : wrapped_type);
669 : } else {
670 : // TODO(ishell): mutable accessors are not implemented yet.
671 0 : UNIMPLEMENTED();
672 : }
673 2319 : current_offset += d.GetDetails().field_width_in_words();
674 2319 : new_descriptors->Set(i, &d);
675 : } else {
676 : DCHECK_EQ(kDescriptor, next_location);
677 : DCHECK_EQ(PropertyConstness::kConst, next_constness);
678 :
679 77 : Handle<Object> value(GetValue(i), isolate_);
680 77 : if (next_kind == kData) {
681 0 : d = Descriptor::DataConstant(key, value, next_attributes);
682 : } else {
683 : DCHECK_EQ(kAccessor, next_kind);
684 77 : d = Descriptor::AccessorConstant(key, value, next_attributes);
685 : }
686 77 : new_descriptors->Set(i, &d);
687 : }
688 : }
689 :
690 12262 : new_descriptors->Sort();
691 12262 : return new_descriptors;
692 : }
693 :
694 12262 : Handle<Map> MapUpdater::FindSplitMap(Handle<DescriptorArray> descriptors) {
695 : DisallowHeapAllocation no_allocation;
696 :
697 : int root_nof = root_map_->NumberOfOwnDescriptors();
698 : Map current = *root_map_;
699 46899 : for (int i = root_nof; i < old_nof_; i++) {
700 46689 : Name name = descriptors->GetKey(i);
701 46689 : PropertyDetails details = descriptors->GetDetails(i);
702 : Map next =
703 : TransitionsAccessor(isolate_, current, &no_allocation)
704 93378 : .SearchTransition(name, details.kind(), details.attributes());
705 46689 : if (next.is_null()) break;
706 45110 : DescriptorArray next_descriptors = next->instance_descriptors();
707 :
708 45110 : PropertyDetails next_details = next_descriptors->GetDetails(i);
709 : DCHECK_EQ(details.kind(), next_details.kind());
710 : DCHECK_EQ(details.attributes(), next_details.attributes());
711 45110 : if (details.constness() != next_details.constness()) break;
712 43091 : if (details.location() != next_details.location()) break;
713 43091 : if (!details.representation().Equals(next_details.representation())) break;
714 :
715 34637 : if (next_details.location() == kField) {
716 34599 : FieldType next_field_type = next_descriptors->GetFieldType(i);
717 34599 : if (!descriptors->GetFieldType(i)->NowIs(next_field_type)) {
718 : break;
719 : }
720 : } else {
721 38 : if (!EqualImmutableValues(descriptors->GetStrongValue(i),
722 76 : next_descriptors->GetStrongValue(i))) {
723 : break;
724 : }
725 : }
726 34637 : current = next;
727 : }
728 24524 : return handle(current, isolate_);
729 : }
730 :
731 12262 : MapUpdater::State MapUpdater::ConstructNewMap() {
732 12262 : Handle<DescriptorArray> new_descriptors = BuildDescriptorArray();
733 :
734 12262 : Handle<Map> split_map = FindSplitMap(new_descriptors);
735 : int split_nof = split_map->NumberOfOwnDescriptors();
736 12262 : if (old_nof_ == split_nof) {
737 210 : CHECK(has_integrity_level_transition_);
738 210 : state_ = kAtIntegrityLevelSource;
739 210 : return state_;
740 : }
741 :
742 12052 : PropertyDetails split_details = GetDetails(split_nof);
743 12052 : TransitionsAccessor transitions(isolate_, split_map);
744 :
745 : // Invalidate a transition target at |key|.
746 : Map maybe_transition = transitions.SearchTransition(
747 12052 : GetKey(split_nof), split_details.kind(), split_details.attributes());
748 12052 : if (!maybe_transition.is_null()) {
749 10473 : maybe_transition->DeprecateTransitionTree(isolate_);
750 : }
751 :
752 : // If |maybe_transition| is not nullptr then the transition array already
753 : // contains entry for given descriptor. This means that the transition
754 : // could be inserted regardless of whether transitions array is full or not.
755 12052 : if (maybe_transition.is_null() && !transitions.CanHaveMoreTransitions()) {
756 5 : return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
757 : }
758 :
759 24094 : old_map_->NotifyLeafMapLayoutChange(isolate_);
760 :
761 12047 : if (FLAG_trace_generalization && modified_descriptor_ >= 0) {
762 : PropertyDetails old_details =
763 0 : old_descriptors_->GetDetails(modified_descriptor_);
764 : PropertyDetails new_details =
765 0 : new_descriptors->GetDetails(modified_descriptor_);
766 0 : MaybeHandle<FieldType> old_field_type;
767 0 : MaybeHandle<FieldType> new_field_type;
768 0 : MaybeHandle<Object> old_value;
769 0 : MaybeHandle<Object> new_value;
770 0 : if (old_details.location() == kField) {
771 : old_field_type = handle(
772 0 : old_descriptors_->GetFieldType(modified_descriptor_), isolate_);
773 : } else {
774 : old_value = handle(old_descriptors_->GetStrongValue(modified_descriptor_),
775 0 : isolate_);
776 : }
777 0 : if (new_details.location() == kField) {
778 : new_field_type =
779 0 : handle(new_descriptors->GetFieldType(modified_descriptor_), isolate_);
780 : } else {
781 : new_value = handle(new_descriptors->GetStrongValue(modified_descriptor_),
782 0 : isolate_);
783 : }
784 :
785 : old_map_->PrintGeneralization(
786 : isolate_, stdout, "", modified_descriptor_, split_nof, old_nof_,
787 0 : old_details.location() == kDescriptor && new_location_ == kField,
788 : old_details.representation(), new_details.representation(),
789 0 : old_field_type, old_value, new_field_type, new_value);
790 : }
791 :
792 : Handle<LayoutDescriptor> new_layout_descriptor =
793 12047 : LayoutDescriptor::New(isolate_, split_map, new_descriptors, old_nof_);
794 :
795 : Handle<Map> new_map = Map::AddMissingTransitions(
796 12047 : isolate_, split_map, new_descriptors, new_layout_descriptor);
797 :
798 : // Deprecated part of the transition tree is no longer reachable, so replace
799 : // current instance descriptors in the "survived" part of the tree with
800 : // the new descriptors to maintain descriptors sharing invariant.
801 : split_map->ReplaceDescriptors(isolate_, *new_descriptors,
802 24094 : *new_layout_descriptor);
803 :
804 12047 : if (has_integrity_level_transition_) {
805 76 : target_map_ = new_map;
806 76 : state_ = kAtIntegrityLevelSource;
807 : } else {
808 11971 : result_map_ = new_map;
809 11971 : state_ = kEnd;
810 : }
811 12047 : return state_; // Done.
812 : }
813 :
814 286 : MapUpdater::State MapUpdater::ConstructNewMapWithIntegrityLevelTransition() {
815 : DCHECK_EQ(kAtIntegrityLevelSource, state_);
816 :
817 286 : TransitionsAccessor transitions(isolate_, target_map_);
818 286 : if (!transitions.CanHaveMoreTransitions()) {
819 0 : return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
820 : }
821 :
822 : result_map_ = Map::CopyForPreventExtensions(
823 : isolate_, target_map_, integrity_level_, integrity_level_symbol_,
824 286 : "CopyForPreventExtensions");
825 :
826 286 : state_ = kEnd;
827 286 : return state_;
828 : }
829 :
830 : } // namespace internal
831 178779 : } // namespace v8
|