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