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 16574 : 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 580051 : 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 2900255 : IsTransitionableFastElementsKind(new_elements_kind_)) {
36 : // We shouldn't try to update remote objects.
37 : DCHECK(!old_map->FindRootMap(isolate)
38 : ->GetConstructor()
39 : ->IsFunctionTemplateInfo());
40 580051 : }
41 :
42 592033 : Name MapUpdater::GetKey(int descriptor) const {
43 592033 : return old_descriptors_->GetKey(descriptor);
44 : }
45 :
46 590994 : PropertyDetails MapUpdater::GetDetails(int descriptor) const {
47 : DCHECK_LE(0, descriptor);
48 590994 : if (descriptor == modified_descriptor_) {
49 99296 : 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 99296 : if (integrity_level_ == SEALED || integrity_level_ == FROZEN) {
55 566 : attributes = old_descriptors_->GetDetails(descriptor).attributes();
56 : }
57 99296 : return PropertyDetails(new_kind_, attributes, new_location_, new_constness_,
58 99296 : new_representation_);
59 : }
60 491698 : return old_descriptors_->GetDetails(descriptor);
61 : }
62 :
63 16662 : Object MapUpdater::GetValue(int descriptor) const {
64 : DCHECK_LE(0, descriptor);
65 16662 : if (descriptor == modified_descriptor_) {
66 : DCHECK_EQ(kDescriptor, new_location_);
67 : return *new_value_;
68 : }
69 : DCHECK_EQ(kDescriptor, GetDetails(descriptor).location());
70 : return old_descriptors_->GetStrongValue(descriptor);
71 : }
72 :
73 549729 : FieldType MapUpdater::GetFieldType(int descriptor) const {
74 : DCHECK_LE(0, descriptor);
75 549729 : if (descriptor == modified_descriptor_) {
76 : DCHECK_EQ(kField, new_location_);
77 : return *new_field_type_;
78 : }
79 : DCHECK_EQ(kField, GetDetails(descriptor).location());
80 460935 : return old_descriptors_->GetFieldType(descriptor);
81 : }
82 :
83 549727 : 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 549727 : if (location == kField) {
90 549727 : return handle(GetFieldType(descriptor), isolate_);
91 : } else {
92 0 : return GetValue(descriptor)->OptimalType(isolate_, representation);
93 : }
94 : }
95 :
96 336680 : 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 336680 : if (location == kField) {
102 1010044 : return handle(descriptors->GetFieldType(descriptor), isolate_);
103 : } else {
104 0 : return descriptors->GetStrongValue(descriptor)
105 0 : ->OptimalType(isolate_, representation);
106 : }
107 : }
108 :
109 286400 : 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 286400 : modified_descriptor_ = descriptor;
118 286400 : new_kind_ = kData;
119 286400 : new_attributes_ = attributes;
120 286400 : new_location_ = kField;
121 :
122 : PropertyDetails old_details =
123 286400 : 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 286401 : if (old_details.kind() == new_kind_) {
128 286243 : new_constness_ = GeneralizeConstness(constness, old_details.constness());
129 :
130 286243 : Representation old_representation = old_details.representation();
131 286243 : new_representation_ = representation.generalize(old_representation);
132 :
133 : Handle<FieldType> old_field_type =
134 : GetOrComputeFieldType(old_descriptors_, modified_descriptor_,
135 286247 : old_details.location(), new_representation_);
136 :
137 : new_field_type_ =
138 : Map::GeneralizeFieldType(old_representation, old_field_type,
139 286247 : 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 158 : new_constness_ = PropertyConstness::kMutable;
145 158 : new_representation_ = representation;
146 158 : new_field_type_ = field_type;
147 : }
148 :
149 572804 : Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
150 : isolate_, old_map_->instance_type(), &new_constness_,
151 286402 : &new_representation_, &new_field_type_);
152 :
153 286402 : if (TryReconfigureToDataFieldInplace() == kEnd) return result_map_;
154 100496 : if (FindRootMap() == kEnd) return result_map_;
155 100361 : if (FindTargetMap() == kEnd) return result_map_;
156 3569 : if (ConstructNewMap() == kAtIntegrityLevelSource) {
157 81 : ConstructNewMapWithIntegrityLevelTransition();
158 : }
159 : DCHECK_EQ(kEnd, state_);
160 3569 : return result_map_;
161 : }
162 :
163 292535 : Handle<Map> MapUpdater::ReconfigureElementsKind(ElementsKind elements_kind) {
164 : DCHECK_EQ(kInitialized, state_);
165 292535 : new_elements_kind_ = elements_kind;
166 : is_transitionable_fast_elements_kind_ =
167 292535 : IsTransitionableFastElementsKind(new_elements_kind_);
168 :
169 292535 : if (FindRootMap() == kEnd) return result_map_;
170 292535 : if (FindTargetMap() == kEnd) return result_map_;
171 851 : if (ConstructNewMap() == kAtIntegrityLevelSource) {
172 0 : ConstructNewMapWithIntegrityLevelTransition();
173 : }
174 : DCHECK_EQ(kEnd, state_);
175 851 : return result_map_;
176 : }
177 :
178 1114 : Handle<Map> MapUpdater::Update() {
179 : DCHECK_EQ(kInitialized, state_);
180 : DCHECK(old_map_->is_deprecated());
181 :
182 1114 : if (FindRootMap() == kEnd) return result_map_;
183 1114 : if (FindTargetMap() == kEnd) return result_map_;
184 226 : 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 226 : 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 694604 : Map::GeneralizeField(isolate_, map, modify_index, new_constness,
199 694604 : 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 180 : MapUpdater::State MapUpdater::CopyGeneralizeAllFields(const char* reason) {
206 : result_map_ = Map::CopyGeneralizeAllFields(
207 180 : isolate_, old_map_, new_elements_kind_, modified_descriptor_, new_kind_,
208 360 : new_attributes_, reason);
209 180 : state_ = kEnd;
210 180 : return state_; // Done.
211 : }
212 :
213 286399 : MapUpdater::State MapUpdater::TryReconfigureToDataFieldInplace() {
214 : // Updating deprecated maps in-place doesn't make sense.
215 286399 : if (old_map_->is_deprecated()) return state_;
216 :
217 : // If it's just a representation generalization case (i.e. property kind and
218 : // attributes stays unchanged) it's fine to transition from None to anything
219 : // but double without any modification to the object, because the default
220 : // uninitialized value for representation None can be overwritten by both
221 : // smi and tagged values. Doubles, however, would require a box allocation.
222 286139 : if (new_representation_.IsNone() || new_representation_.IsDouble()) {
223 9892 : return state_; // Not done yet.
224 : }
225 :
226 : PropertyDetails old_details =
227 552494 : old_descriptors_->GetDetails(modified_descriptor_);
228 : Representation old_representation = old_details.representation();
229 276249 : if (!old_representation.CanBeInPlaceChangedTo(new_representation_)) {
230 90344 : return state_; // Not done yet.
231 : }
232 :
233 : DCHECK_EQ(new_kind_, old_details.kind());
234 : DCHECK_EQ(new_attributes_, old_details.attributes());
235 : DCHECK_EQ(kField, old_details.location());
236 185905 : if (FLAG_trace_generalization) {
237 0 : old_map_->PrintGeneralization(
238 : isolate_, stdout, "uninitialized field", modified_descriptor_, old_nof_,
239 : old_nof_, false, old_representation, new_representation_,
240 : handle(old_descriptors_->GetFieldType(modified_descriptor_), isolate_),
241 0 : MaybeHandle<Object>(), new_field_type_, MaybeHandle<Object>());
242 : }
243 : Handle<Map> field_owner(
244 557716 : old_map_->FindFieldOwner(isolate_, modified_descriptor_), isolate_);
245 :
246 185906 : GeneralizeField(field_owner, modified_descriptor_, new_constness_,
247 : new_representation_, new_field_type_);
248 : // Check that the descriptor array was updated.
249 : DCHECK(old_descriptors_->GetDetails(modified_descriptor_)
250 : .representation()
251 : .Equals(new_representation_));
252 : DCHECK(old_descriptors_->GetFieldType(modified_descriptor_)
253 : ->NowIs(new_field_type_));
254 :
255 185908 : result_map_ = old_map_;
256 185908 : state_ = kEnd;
257 185908 : return state_; // Done.
258 : }
259 :
260 401 : bool MapUpdater::TrySaveIntegrityLevelTransitions() {
261 : // Figure out the most restrictive integrity level transition (it should
262 : // be the last one in the transition tree).
263 : Handle<Map> previous =
264 1203 : handle(Map::cast(old_map_->GetBackPointer()), isolate_);
265 401 : Symbol integrity_level_symbol;
266 401 : TransitionsAccessor last_transitions(isolate_, previous);
267 802 : if (!last_transitions.HasIntegrityLevelTransitionTo(
268 : *old_map_, &integrity_level_symbol, &integrity_level_)) {
269 : // The last transition was not integrity level transition - just bail out.
270 : // This can happen in the following cases:
271 : // - there are private symbol transitions following the integrity level
272 : // transitions (see crbug.com/v8/8854).
273 : // - there is a getter added in addition to an existing setter (or a setter
274 : // in addition to an existing getter).
275 : return false;
276 : }
277 748 : integrity_level_symbol_ = handle(integrity_level_symbol, isolate_);
278 374 : integrity_source_map_ = previous;
279 :
280 : // Now walk up the back pointer chain and skip all integrity level
281 : // transitions. If we encounter any non-integrity level transition interleaved
282 : // with integrity level transitions, just bail out.
283 432 : while (!integrity_source_map_->is_extensible()) {
284 : previous =
285 58 : handle(Map::cast(integrity_source_map_->GetBackPointer()), isolate_);
286 29 : TransitionsAccessor transitions(isolate_, previous);
287 29 : if (!transitions.HasIntegrityLevelTransitionTo(*integrity_source_map_)) {
288 0 : return false;
289 : }
290 29 : integrity_source_map_ = previous;
291 : }
292 :
293 : // Integrity-level transitions never change number of descriptors.
294 374 : CHECK_EQ(old_map_->NumberOfOwnDescriptors(),
295 : integrity_source_map_->NumberOfOwnDescriptors());
296 :
297 374 : has_integrity_level_transition_ = true;
298 : old_descriptors_ =
299 748 : handle(integrity_source_map_->instance_descriptors(), isolate_);
300 374 : return true;
301 : }
302 :
303 394144 : MapUpdater::State MapUpdater::FindRootMap() {
304 : DCHECK_EQ(kInitialized, state_);
305 : // Check the state of the root map.
306 1182435 : root_map_ = handle(old_map_->FindRootMap(isolate_), isolate_);
307 : ElementsKind from_kind = root_map_->elements_kind();
308 394147 : ElementsKind to_kind = new_elements_kind_;
309 :
310 394147 : if (root_map_->is_deprecated()) {
311 0 : state_ = kEnd;
312 : result_map_ = handle(
313 0 : JSFunction::cast(root_map_->GetConstructor())->initial_map(), isolate_);
314 0 : result_map_ = Map::AsElementsKind(isolate_, result_map_, to_kind);
315 : DCHECK(result_map_->is_dictionary_map());
316 0 : return state_;
317 : }
318 :
319 394147 : if (!old_map_->EquivalentToForTransition(*root_map_)) {
320 18 : return CopyGeneralizeAllFields("GenAll_NotEquivalent");
321 394128 : } else if (old_map_->is_extensible() != root_map_->is_extensible()) {
322 : DCHECK(!old_map_->is_extensible());
323 : DCHECK(root_map_->is_extensible());
324 : // We have an integrity level transition in the tree, let us make a note
325 : // of that transition to be able to replay it later.
326 401 : if (!TrySaveIntegrityLevelTransitions()) {
327 27 : return CopyGeneralizeAllFields("GenAll_PrivateSymbolsOnNonExtensible");
328 : }
329 :
330 : // We want to build transitions to the original element kind (before
331 : // the seal transitions), so change {to_kind} accordingly.
332 : DCHECK(to_kind == DICTIONARY_ELEMENTS ||
333 : to_kind == SLOW_STRING_WRAPPER_ELEMENTS ||
334 : IsFixedTypedArrayElementsKind(to_kind) ||
335 : IsPackedFrozenOrSealedElementsKind(to_kind));
336 : to_kind = integrity_source_map_->elements_kind();
337 : }
338 :
339 : // TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
340 788202 : if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
341 7410 : to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
342 401318 : to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
343 : !(IsTransitionableFastElementsKind(from_kind) &&
344 3512 : IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
345 0 : return CopyGeneralizeAllFields("GenAll_InvalidElementsTransition");
346 : }
347 :
348 : int root_nof = root_map_->NumberOfOwnDescriptors();
349 394101 : if (modified_descriptor_ >= 0 && modified_descriptor_ < root_nof) {
350 : PropertyDetails old_details =
351 11700 : old_descriptors_->GetDetails(modified_descriptor_);
352 23318 : if (old_details.kind() != new_kind_ ||
353 11618 : old_details.attributes() != new_attributes_) {
354 82 : return CopyGeneralizeAllFields("GenAll_RootModification1");
355 : }
356 11618 : if (old_details.location() != kField) {
357 0 : return CopyGeneralizeAllFields("GenAll_RootModification2");
358 : }
359 11618 : if (!new_representation_.fits_into(old_details.representation())) {
360 9 : return CopyGeneralizeAllFields("GenAll_RootModification4");
361 : }
362 :
363 : DCHECK_EQ(kData, old_details.kind());
364 : DCHECK_EQ(kData, new_kind_);
365 : DCHECK_EQ(kField, new_location_);
366 :
367 : // Modify root map in-place. The GeneralizeField method is a no-op
368 : // if the {old_map_} is already general enough to hold the requested
369 : // {new_constness_} and {new_field_type_}.
370 11609 : GeneralizeField(old_map_, modified_descriptor_, new_constness_,
371 : old_details.representation(), new_field_type_);
372 : }
373 :
374 : // From here on, use the map with correct elements kind as root map.
375 394010 : root_map_ = Map::AsElementsKind(isolate_, root_map_, to_kind);
376 394009 : state_ = kAtRootMap;
377 394009 : return state_; // Not done yet.
378 : }
379 :
380 394003 : MapUpdater::State MapUpdater::FindTargetMap() {
381 : DCHECK_EQ(kAtRootMap, state_);
382 394003 : target_map_ = root_map_;
383 :
384 : int root_nof = root_map_->NumberOfOwnDescriptors();
385 1404635 : for (int i = root_nof; i < old_nof_; ++i) {
386 509830 : PropertyDetails old_details = GetDetails(i);
387 : Map transition = TransitionsAccessor(isolate_, target_map_)
388 : .SearchTransition(GetKey(i), old_details.kind(),
389 1019660 : old_details.attributes());
390 509831 : if (transition.is_null()) break;
391 508316 : Handle<Map> tmp_map(transition, isolate_);
392 :
393 : Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
394 508316 : isolate_);
395 :
396 : // Check if target map is incompatible.
397 508316 : PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
398 : DCHECK_EQ(old_details.kind(), tmp_details.kind());
399 : DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
400 516581 : if (old_details.kind() == kAccessor &&
401 8265 : !EqualImmutableValues(GetValue(i),
402 : tmp_descriptors->GetStrongValue(i))) {
403 : // TODO(ishell): mutable accessors are not implemented yet.
404 40 : return CopyGeneralizeAllFields("GenAll_Incompatible");
405 : }
406 508276 : if (!IsGeneralizableTo(old_details.location(), tmp_details.location())) {
407 : break;
408 : }
409 : Representation tmp_representation = tmp_details.representation();
410 508277 : if (!old_details.representation().fits_into(tmp_representation)) {
411 : break;
412 : }
413 :
414 505313 : if (tmp_details.location() == kField) {
415 : Handle<FieldType> old_field_type =
416 497088 : GetOrComputeFieldType(i, old_details.location(), tmp_representation);
417 : GeneralizeField(tmp_map, i, old_details.constness(), tmp_representation,
418 : old_field_type);
419 : } else {
420 : // kDescriptor: Check that the value matches.
421 16450 : if (!EqualImmutableValues(GetValue(i),
422 : tmp_descriptors->GetStrongValue(i))) {
423 : break;
424 : }
425 : }
426 : DCHECK(!tmp_map->is_deprecated());
427 505316 : target_map_ = tmp_map;
428 : }
429 :
430 : // Directly change the map if the target map is more general.
431 : int target_nof = target_map_->NumberOfOwnDescriptors();
432 393967 : if (target_nof == old_nof_) {
433 : #ifdef DEBUG
434 : if (modified_descriptor_ >= 0) {
435 : DescriptorArray target_descriptors = target_map_->instance_descriptors();
436 : PropertyDetails details =
437 : target_descriptors->GetDetails(modified_descriptor_);
438 : DCHECK_EQ(new_kind_, details.kind());
439 : DCHECK_EQ(GetDetails(modified_descriptor_).attributes(),
440 : details.attributes());
441 : DCHECK(IsGeneralizableTo(new_constness_, details.constness()));
442 : DCHECK_EQ(new_location_, details.location());
443 : DCHECK(new_representation_.fits_into(details.representation()));
444 : if (new_location_ == kField) {
445 : DCHECK_EQ(kField, details.location());
446 : DCHECK(new_field_type_->NowIs(
447 : target_descriptors->GetFieldType(modified_descriptor_)));
448 : } else {
449 : DCHECK(details.location() == kField ||
450 : EqualImmutableValues(
451 : *new_value_,
452 : target_descriptors->GetStrongValue(modified_descriptor_)));
453 : }
454 : }
455 : #endif
456 389489 : if (*target_map_ != *old_map_) {
457 293501 : old_map_->NotifyLeafMapLayoutChange(isolate_);
458 : }
459 389489 : if (!has_integrity_level_transition_) {
460 389196 : result_map_ = target_map_;
461 389196 : state_ = kEnd;
462 778517 : return state_; // Done.
463 : }
464 :
465 : // We try to replay the integrity level transition here.
466 : Map transition = TransitionsAccessor(isolate_, target_map_)
467 586 : .SearchSpecial(*integrity_level_symbol_);
468 293 : if (!transition.is_null()) {
469 250 : result_map_ = handle(transition, isolate_);
470 125 : state_ = kEnd;
471 125 : return state_; // Done.
472 : }
473 : }
474 :
475 : // Find the last compatible target map in the transition tree.
476 49470 : for (int i = target_nof; i < old_nof_; ++i) {
477 23927 : PropertyDetails old_details = GetDetails(i);
478 : Map transition = TransitionsAccessor(isolate_, target_map_)
479 : .SearchTransition(GetKey(i), old_details.kind(),
480 47854 : old_details.attributes());
481 23927 : if (transition.is_null()) break;
482 22412 : Handle<Map> tmp_map(transition, isolate_);
483 : Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
484 22412 : isolate_);
485 : #ifdef DEBUG
486 : // Check that target map is compatible.
487 : PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
488 : DCHECK_EQ(old_details.kind(), tmp_details.kind());
489 : DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
490 : #endif
491 22424 : if (old_details.kind() == kAccessor &&
492 12 : !EqualImmutableValues(GetValue(i),
493 : tmp_descriptors->GetStrongValue(i))) {
494 0 : return CopyGeneralizeAllFields("GenAll_Incompatible");
495 : }
496 : DCHECK(!tmp_map->is_deprecated());
497 22412 : target_map_ = tmp_map;
498 : }
499 :
500 4646 : state_ = kAtTargetMap;
501 4646 : return state_; // Not done yet.
502 : }
503 :
504 4646 : Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() {
505 : InstanceType instance_type = old_map_->instance_type();
506 : int target_nof = target_map_->NumberOfOwnDescriptors();
507 : Handle<DescriptorArray> target_descriptors(
508 4646 : target_map_->instance_descriptors(), isolate_);
509 :
510 : // Allocate a new descriptor array large enough to hold the required
511 : // descriptors, with minimally the exact same size as the old descriptor
512 : // array.
513 : int new_slack =
514 13938 : std::max<int>(old_nof_, old_descriptors_->number_of_descriptors()) -
515 4646 : old_nof_;
516 : Handle<DescriptorArray> new_descriptors =
517 4646 : DescriptorArray::Allocate(isolate_, old_nof_, new_slack);
518 : DCHECK(new_descriptors->number_of_all_descriptors() >
519 : target_descriptors->number_of_all_descriptors() ||
520 : new_descriptors->number_of_slack_descriptors() > 0 ||
521 : new_descriptors->number_of_descriptors() ==
522 : old_descriptors_->number_of_descriptors());
523 : DCHECK(new_descriptors->number_of_descriptors() == old_nof_);
524 :
525 : int root_nof = root_map_->NumberOfOwnDescriptors();
526 :
527 : // Given that we passed root modification check in FindRootMap() so
528 : // the root descriptors are either not modified at all or already more
529 : // general than we requested. Take |root_nof| entries as is.
530 : // 0 -> |root_nof|
531 : int current_offset = 0;
532 6724 : for (int i = 0; i < root_nof; ++i) {
533 1039 : PropertyDetails old_details = old_descriptors_->GetDetails(i);
534 1039 : if (old_details.location() == kField) {
535 306 : current_offset += old_details.field_width_in_words();
536 : }
537 : Descriptor d(handle(GetKey(i), isolate_),
538 : MaybeObjectHandle(old_descriptors_->GetValue(i), isolate_),
539 3117 : old_details);
540 1039 : new_descriptors->Set(i, &d);
541 : }
542 :
543 : // Merge "updated" old_descriptor entries with target_descriptor entries.
544 : // |root_nof| -> |target_nof|
545 105604 : for (int i = root_nof; i < target_nof; ++i) {
546 50479 : Handle<Name> key(GetKey(i), isolate_);
547 50479 : PropertyDetails old_details = GetDetails(i);
548 50479 : PropertyDetails target_details = target_descriptors->GetDetails(i);
549 :
550 : PropertyKind next_kind = old_details.kind();
551 : PropertyAttributes next_attributes = old_details.attributes();
552 : DCHECK_EQ(next_kind, target_details.kind());
553 : DCHECK_EQ(next_attributes, target_details.attributes());
554 :
555 : PropertyConstness next_constness = GeneralizeConstness(
556 50479 : old_details.constness(), target_details.constness());
557 :
558 : // Note: failed values equality check does not invalidate per-object
559 : // property constness.
560 : PropertyLocation next_location =
561 42 : old_details.location() == kField ||
562 : target_details.location() == kField ||
563 42 : !EqualImmutableValues(target_descriptors->GetStrongValue(i),
564 : GetValue(i))
565 : ? kField
566 50521 : : kDescriptor;
567 :
568 : if (!FLAG_track_constant_fields && next_location == kField) {
569 : next_constness = PropertyConstness::kMutable;
570 : }
571 : // Ensure that mutable values are stored in fields.
572 : DCHECK_IMPLIES(next_constness == PropertyConstness::kMutable,
573 : next_location == kField);
574 :
575 : Representation next_representation =
576 100958 : old_details.representation().generalize(
577 50479 : target_details.representation());
578 :
579 50479 : if (next_location == kField) {
580 : Handle<FieldType> old_field_type =
581 50437 : GetOrComputeFieldType(i, old_details.location(), next_representation);
582 :
583 : Handle<FieldType> target_field_type =
584 : GetOrComputeFieldType(target_descriptors, i,
585 50437 : target_details.location(), next_representation);
586 :
587 : Handle<FieldType> next_field_type = Map::GeneralizeFieldType(
588 : old_details.representation(), old_field_type, next_representation,
589 50437 : target_field_type, isolate_);
590 :
591 50437 : Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
592 : isolate_, instance_type, &next_constness, &next_representation,
593 50437 : &next_field_type);
594 :
595 : MaybeObjectHandle wrapped_type(
596 50437 : Map::WrapFieldType(isolate_, next_field_type));
597 50437 : Descriptor d;
598 50437 : if (next_kind == kData) {
599 100874 : d = Descriptor::DataField(key, current_offset, next_attributes,
600 : next_constness, next_representation,
601 100874 : wrapped_type);
602 : } else {
603 : // TODO(ishell): mutable accessors are not implemented yet.
604 0 : UNIMPLEMENTED();
605 : }
606 50437 : current_offset += d.GetDetails().field_width_in_words();
607 50437 : new_descriptors->Set(i, &d);
608 : } else {
609 : DCHECK_EQ(kDescriptor, next_location);
610 : DCHECK_EQ(PropertyConstness::kConst, next_constness);
611 :
612 42 : Handle<Object> value(GetValue(i), isolate_);
613 42 : Descriptor d;
614 42 : if (next_kind == kData) {
615 : DCHECK(!FLAG_track_constant_fields);
616 0 : d = Descriptor::DataConstant(key, value, next_attributes);
617 : } else {
618 : DCHECK_EQ(kAccessor, next_kind);
619 42 : d = Descriptor::AccessorConstant(key, value, next_attributes);
620 : }
621 42 : new_descriptors->Set(i, &d);
622 : }
623 : }
624 :
625 : // Take "updated" old_descriptor entries.
626 : // |target_nof| -> |old_nof|
627 9206 : for (int i = target_nof; i < old_nof_; ++i) {
628 2280 : PropertyDetails old_details = GetDetails(i);
629 2280 : Handle<Name> key(GetKey(i), isolate_);
630 :
631 : PropertyKind next_kind = old_details.kind();
632 : PropertyAttributes next_attributes = old_details.attributes();
633 : PropertyConstness next_constness = old_details.constness();
634 : PropertyLocation next_location = old_details.location();
635 : Representation next_representation = old_details.representation();
636 :
637 2280 : Descriptor d;
638 2280 : if (next_location == kField) {
639 : Handle<FieldType> next_field_type =
640 2204 : GetOrComputeFieldType(i, old_details.location(), next_representation);
641 :
642 : // If the |new_elements_kind_| is still transitionable then the old map's
643 : // elements kind is also transitionable and therefore the old descriptors
644 : // array must already have generalized field type.
645 2616 : CHECK_IMPLIES(
646 : is_transitionable_fast_elements_kind_,
647 : Map::IsMostGeneralFieldType(next_representation, *next_field_type));
648 :
649 : MaybeObjectHandle wrapped_type(
650 2204 : Map::WrapFieldType(isolate_, next_field_type));
651 2204 : Descriptor d;
652 2204 : if (next_kind == kData) {
653 : DCHECK_IMPLIES(!FLAG_track_constant_fields,
654 : next_constness == PropertyConstness::kMutable);
655 4408 : d = Descriptor::DataField(key, current_offset, next_attributes,
656 : next_constness, next_representation,
657 2204 : wrapped_type);
658 : } else {
659 : // TODO(ishell): mutable accessors are not implemented yet.
660 0 : UNIMPLEMENTED();
661 : }
662 2204 : current_offset += d.GetDetails().field_width_in_words();
663 2204 : new_descriptors->Set(i, &d);
664 : } else {
665 : DCHECK_EQ(kDescriptor, next_location);
666 : DCHECK_EQ(PropertyConstness::kConst, next_constness);
667 :
668 76 : Handle<Object> value(GetValue(i), isolate_);
669 76 : if (next_kind == kData) {
670 0 : d = Descriptor::DataConstant(key, value, next_attributes);
671 : } else {
672 : DCHECK_EQ(kAccessor, next_kind);
673 76 : d = Descriptor::AccessorConstant(key, value, next_attributes);
674 : }
675 76 : new_descriptors->Set(i, &d);
676 : }
677 : }
678 :
679 4646 : new_descriptors->Sort();
680 4646 : return new_descriptors;
681 : }
682 :
683 4646 : Handle<Map> MapUpdater::FindSplitMap(Handle<DescriptorArray> descriptors) {
684 : DisallowHeapAllocation no_allocation;
685 :
686 : int root_nof = root_map_->NumberOfOwnDescriptors();
687 : Map current = *root_map_;
688 60780 : for (int i = root_nof; i < old_nof_; i++) {
689 32545 : Name name = descriptors->GetKey(i);
690 32545 : PropertyDetails details = descriptors->GetDetails(i);
691 : Map next =
692 : TransitionsAccessor(isolate_, current, &no_allocation)
693 65090 : .SearchTransition(name, details.kind(), details.attributes());
694 32545 : if (next.is_null()) break;
695 31030 : DescriptorArray next_descriptors = next->instance_descriptors();
696 :
697 31030 : PropertyDetails next_details = next_descriptors->GetDetails(i);
698 : DCHECK_EQ(details.kind(), next_details.kind());
699 : DCHECK_EQ(details.attributes(), next_details.attributes());
700 31030 : if (details.constness() != next_details.constness()) break;
701 30074 : if (details.location() != next_details.location()) break;
702 30074 : if (!details.representation().Equals(next_details.representation())) break;
703 :
704 28067 : if (next_details.location() == kField) {
705 28037 : FieldType next_field_type = next_descriptors->GetFieldType(i);
706 28037 : if (!descriptors->GetFieldType(i)->NowIs(next_field_type)) {
707 : break;
708 : }
709 : } else {
710 30 : if (!EqualImmutableValues(descriptors->GetStrongValue(i),
711 : next_descriptors->GetStrongValue(i))) {
712 : break;
713 : }
714 : }
715 : current = next;
716 : }
717 9292 : return handle(current, isolate_);
718 : }
719 :
720 4646 : MapUpdater::State MapUpdater::ConstructNewMap() {
721 4646 : Handle<DescriptorArray> new_descriptors = BuildDescriptorArray();
722 :
723 4646 : Handle<Map> split_map = FindSplitMap(new_descriptors);
724 : int split_nof = split_map->NumberOfOwnDescriptors();
725 4646 : if (old_nof_ == split_nof) {
726 168 : CHECK(has_integrity_level_transition_);
727 168 : state_ = kAtIntegrityLevelSource;
728 168 : return state_;
729 : }
730 :
731 4478 : PropertyDetails split_details = GetDetails(split_nof);
732 4478 : TransitionsAccessor transitions(isolate_, split_map);
733 :
734 : // Invalidate a transition target at |key|.
735 : Map maybe_transition = transitions.SearchTransition(
736 4478 : GetKey(split_nof), split_details.kind(), split_details.attributes());
737 4478 : if (!maybe_transition.is_null()) {
738 2963 : maybe_transition->DeprecateTransitionTree(isolate_);
739 : }
740 :
741 : // If |maybe_transition| is not nullptr then the transition array already
742 : // contains entry for given descriptor. This means that the transition
743 : // could be inserted regardless of whether transitions array is full or not.
744 4478 : if (maybe_transition.is_null() && !transitions.CanHaveMoreTransitions()) {
745 4 : return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
746 : }
747 :
748 8948 : old_map_->NotifyLeafMapLayoutChange(isolate_);
749 :
750 4474 : if (FLAG_trace_generalization && modified_descriptor_ >= 0) {
751 : PropertyDetails old_details =
752 0 : old_descriptors_->GetDetails(modified_descriptor_);
753 : PropertyDetails new_details =
754 0 : new_descriptors->GetDetails(modified_descriptor_);
755 0 : MaybeHandle<FieldType> old_field_type;
756 0 : MaybeHandle<FieldType> new_field_type;
757 0 : MaybeHandle<Object> old_value;
758 0 : MaybeHandle<Object> new_value;
759 0 : if (old_details.location() == kField) {
760 : old_field_type = handle(
761 0 : old_descriptors_->GetFieldType(modified_descriptor_), isolate_);
762 : } else {
763 : old_value = handle(old_descriptors_->GetStrongValue(modified_descriptor_),
764 0 : isolate_);
765 : }
766 0 : if (new_details.location() == kField) {
767 : new_field_type =
768 0 : handle(new_descriptors->GetFieldType(modified_descriptor_), isolate_);
769 : } else {
770 : new_value = handle(new_descriptors->GetStrongValue(modified_descriptor_),
771 0 : isolate_);
772 : }
773 :
774 0 : old_map_->PrintGeneralization(
775 : isolate_, stdout, "", modified_descriptor_, split_nof, old_nof_,
776 0 : old_details.location() == kDescriptor && new_location_ == kField,
777 : old_details.representation(), new_details.representation(),
778 0 : old_field_type, old_value, new_field_type, new_value);
779 : }
780 :
781 : Handle<LayoutDescriptor> new_layout_descriptor =
782 4474 : LayoutDescriptor::New(isolate_, split_map, new_descriptors, old_nof_);
783 :
784 : Handle<Map> new_map = Map::AddMissingTransitions(
785 4474 : isolate_, split_map, new_descriptors, new_layout_descriptor);
786 :
787 : // Deprecated part of the transition tree is no longer reachable, so replace
788 : // current instance descriptors in the "survived" part of the tree with
789 : // the new descriptors to maintain descriptors sharing invariant.
790 13422 : split_map->ReplaceDescriptors(isolate_, *new_descriptors,
791 4474 : *new_layout_descriptor);
792 :
793 4474 : if (has_integrity_level_transition_) {
794 81 : target_map_ = new_map;
795 81 : state_ = kAtIntegrityLevelSource;
796 : } else {
797 4393 : result_map_ = new_map;
798 4393 : state_ = kEnd;
799 : }
800 4474 : return state_; // Done.
801 : }
802 :
803 249 : MapUpdater::State MapUpdater::ConstructNewMapWithIntegrityLevelTransition() {
804 : DCHECK_EQ(kAtIntegrityLevelSource, state_);
805 :
806 249 : TransitionsAccessor transitions(isolate_, target_map_);
807 249 : if (!transitions.CanHaveMoreTransitions()) {
808 0 : return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
809 : }
810 :
811 : result_map_ = Map::CopyForPreventExtensions(
812 : isolate_, target_map_, integrity_level_, integrity_level_symbol_,
813 249 : "CopyForPreventExtensions");
814 :
815 249 : state_ = kEnd;
816 249 : return state_;
817 : }
818 :
819 : } // namespace internal
820 121996 : } // namespace v8
|