Line data Source code
1 : // Copyright 2015 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 <stdlib.h>
6 : #include <utility>
7 :
8 : #include "test/cctest/test-api.h"
9 :
10 : #include "src/v8.h"
11 :
12 : #include "src/compilation-cache.h"
13 : #include "src/compiler/compilation-dependencies.h"
14 : #include "src/compiler/js-heap-broker.h"
15 : #include "src/execution.h"
16 : #include "src/field-type.h"
17 : #include "src/global-handles.h"
18 : #include "src/heap/factory.h"
19 : #include "src/ic/stub-cache.h"
20 : #include "src/macro-assembler.h"
21 : #include "src/objects-inl.h"
22 : #include "src/objects/heap-number-inl.h"
23 : #include "src/objects/struct-inl.h"
24 : #include "src/optimized-compilation-info.h"
25 : #include "src/property.h"
26 : #include "src/transitions.h"
27 :
28 : namespace v8 {
29 : namespace internal {
30 : namespace compiler {
31 : namespace test_field_type_tracking {
32 :
33 : // TODO(ishell): fix this once TransitionToPrototype stops generalizing
34 : // all field representations (similar to crbug/448711 where elements kind
35 : // and observed transitions caused generalization of all fields).
36 : const bool IS_PROTO_TRANS_ISSUE_FIXED = false;
37 :
38 :
39 : // TODO(ishell): fix this once TransitionToAccessorProperty is able to always
40 : // keep map in fast mode.
41 : const bool IS_ACCESSOR_FIELD_SUPPORTED = false;
42 :
43 :
44 : // Number of properties used in the tests.
45 : const int kPropCount = 7;
46 :
47 :
48 : //
49 : // Helper functions.
50 : //
51 :
52 : static Handle<String> MakeString(const char* str) {
53 : Isolate* isolate = CcTest::i_isolate();
54 : Factory* factory = isolate->factory();
55 11005 : return factory->InternalizeUtf8String(str);
56 : }
57 :
58 :
59 10995 : static Handle<String> MakeName(const char* str, int suffix) {
60 : EmbeddedVector<char, 128> buffer;
61 10995 : SNPrintF(buffer, "%s%d", str, suffix);
62 21990 : return MakeString(buffer.start());
63 : }
64 :
65 :
66 40 : static Handle<AccessorPair> CreateAccessorPair(bool with_getter,
67 : bool with_setter) {
68 : Isolate* isolate = CcTest::i_isolate();
69 : Factory* factory = isolate->factory();
70 40 : Handle<AccessorPair> pair = factory->NewAccessorPair();
71 40 : Handle<String> empty_string = factory->empty_string();
72 40 : if (with_getter) {
73 40 : Handle<JSFunction> func = factory->NewFunctionForTest(empty_string);
74 80 : pair->set_getter(*func);
75 : }
76 40 : if (with_setter) {
77 40 : Handle<JSFunction> func = factory->NewFunctionForTest(empty_string);
78 80 : pair->set_setter(*func);
79 : }
80 40 : return pair;
81 : }
82 :
83 : // Check cached migration target map after Map::Update() and Map::TryUpdate()
84 500 : static void CheckMigrationTarget(Isolate* isolate, Map old_map, Map new_map) {
85 : Map target = TransitionsAccessor(isolate, handle(old_map, isolate))
86 500 : .GetMigrationTarget();
87 1000 : if (target.is_null()) return;
88 0 : CHECK_EQ(new_map, target);
89 0 : CHECK_EQ(Map::TryUpdateSlow(isolate, old_map), target);
90 : }
91 :
92 : class Expectations {
93 : static const int MAX_PROPERTIES = 10;
94 : Isolate* isolate_;
95 : ElementsKind elements_kind_;
96 : PropertyKind kinds_[MAX_PROPERTIES];
97 : PropertyLocation locations_[MAX_PROPERTIES];
98 : PropertyConstness constnesses_[MAX_PROPERTIES];
99 : PropertyAttributes attributes_[MAX_PROPERTIES];
100 : Representation representations_[MAX_PROPERTIES];
101 : // FieldType for kField, value for DATA_CONSTANT and getter for
102 : // ACCESSOR_CONSTANT.
103 : Handle<Object> values_[MAX_PROPERTIES];
104 : // Setter for ACCESSOR_CONSTANT.
105 : Handle<Object> setter_values_[MAX_PROPERTIES];
106 : int number_of_properties_;
107 :
108 : public:
109 450 : explicit Expectations(Isolate* isolate, ElementsKind elements_kind)
110 : : isolate_(isolate),
111 : elements_kind_(elements_kind),
112 13950 : number_of_properties_(0) {}
113 :
114 390 : explicit Expectations(Isolate* isolate)
115 : : Expectations(
116 : isolate,
117 1170 : isolate->object_function()->initial_map()->elements_kind()) {}
118 :
119 3990 : void Init(int index, PropertyKind kind, PropertyAttributes attributes,
120 : PropertyConstness constness, PropertyLocation location,
121 : Representation representation, Handle<Object> value) {
122 3990 : CHECK(index < MAX_PROPERTIES);
123 3990 : kinds_[index] = kind;
124 3990 : locations_[index] = location;
125 11865 : if (kind == kData && location == kField &&
126 4440 : IsTransitionableFastElementsKind(elements_kind_) &&
127 : Map::IsInplaceGeneralizableField(constness, representation,
128 450 : FieldType::cast(*value))) {
129 : // Maps with transitionable elements kinds must have non in-place
130 : // generalizable fields.
131 : if (FLAG_track_constant_fields && FLAG_modify_map_inplace &&
132 : constness == PropertyConstness::kConst) {
133 : constness = PropertyConstness::kMutable;
134 : }
135 450 : if (representation.IsHeapObject() && !FieldType::cast(*value)->IsAny()) {
136 150 : value = FieldType::Any(isolate_);
137 : }
138 : }
139 3990 : constnesses_[index] = constness;
140 3990 : attributes_[index] = attributes;
141 3990 : representations_[index] = representation;
142 3990 : values_[index] = value;
143 3990 : }
144 :
145 0 : void Print() const {
146 0 : StdoutStream os;
147 0 : os << "Expectations: #" << number_of_properties_ << "\n";
148 0 : for (int i = 0; i < number_of_properties_; i++) {
149 0 : os << " " << i << ": ";
150 0 : os << "Descriptor @ ";
151 :
152 0 : if (kinds_[i] == kData) {
153 0 : os << Brief(*values_[i]);
154 : } else {
155 : // kAccessor
156 0 : os << "(get: " << Brief(*values_[i])
157 0 : << ", set: " << Brief(*setter_values_[i]) << ") ";
158 : }
159 :
160 0 : os << " (";
161 0 : if (constnesses_[i] == PropertyConstness::kConst) os << "const ";
162 0 : os << (kinds_[i] == kData ? "data " : "accessor ");
163 0 : if (locations_[i] == kField) {
164 0 : os << "field"
165 0 : << ": " << representations_[i].Mnemonic();
166 : } else {
167 0 : os << "descriptor";
168 : }
169 0 : os << ", attrs: " << attributes_[i] << ")\n";
170 : }
171 0 : os << "\n";
172 0 : }
173 :
174 : void SetElementsKind(ElementsKind elements_kind) {
175 10 : elements_kind_ = elements_kind;
176 : }
177 :
178 : Handle<FieldType> GetFieldType(int index) {
179 : CHECK(index < MAX_PROPERTIES);
180 : CHECK_EQ(kField, locations_[index]);
181 : return Handle<FieldType>::cast(values_[index]);
182 : }
183 :
184 : void SetDataField(int index, PropertyAttributes attrs,
185 : PropertyConstness constness, Representation representation,
186 : Handle<FieldType> field_type) {
187 3885 : Init(index, kData, attrs, constness, kField, representation, field_type);
188 : }
189 :
190 665 : void SetDataField(int index, PropertyConstness constness,
191 : Representation representation,
192 : Handle<FieldType> field_type) {
193 : SetDataField(index, attributes_[index], constness, representation,
194 665 : field_type);
195 665 : }
196 :
197 : void SetAccessorField(int index, PropertyAttributes attrs) {
198 : Init(index, kAccessor, attrs, PropertyConstness::kConst, kDescriptor,
199 : Representation::Tagged(), FieldType::Any(isolate_));
200 : }
201 :
202 : void SetAccessorField(int index) {
203 : SetAccessorField(index, attributes_[index]);
204 : }
205 :
206 50 : void SetDataConstant(int index, PropertyAttributes attrs,
207 : Handle<JSFunction> value) {
208 : if (FLAG_track_constant_fields) {
209 : Handle<FieldType> field_type(FieldType::Class(value->map()), isolate_);
210 : Init(index, kData, attrs, PropertyConstness::kConst, kField,
211 : Representation::HeapObject(), field_type);
212 :
213 : } else {
214 : Init(index, kData, attrs, PropertyConstness::kConst, kDescriptor,
215 50 : Representation::HeapObject(), value);
216 : }
217 50 : }
218 :
219 : void SetDataConstant(int index, Handle<JSFunction> value) {
220 : SetDataConstant(index, attributes_[index], value);
221 : }
222 :
223 : void SetAccessorConstant(int index, PropertyAttributes attrs,
224 : Handle<Object> getter, Handle<Object> setter) {
225 : Init(index, kAccessor, attrs, PropertyConstness::kConst, kDescriptor,
226 55 : Representation::Tagged(), getter);
227 55 : setter_values_[index] = setter;
228 : }
229 :
230 : void SetAccessorConstantComponent(int index, PropertyAttributes attrs,
231 : AccessorComponent component,
232 : Handle<Object> accessor) {
233 : CHECK_EQ(kAccessor, kinds_[index]);
234 : CHECK_EQ(kDescriptor, locations_[index]);
235 : CHECK(index < number_of_properties_);
236 : if (component == ACCESSOR_GETTER) {
237 : values_[index] = accessor;
238 : } else {
239 : setter_values_[index] = accessor;
240 : }
241 : }
242 :
243 50 : void SetAccessorConstant(int index, PropertyAttributes attrs,
244 : Handle<AccessorPair> pair) {
245 150 : Handle<Object> getter = handle(pair->getter(), isolate_);
246 150 : Handle<Object> setter = handle(pair->setter(), isolate_);
247 : SetAccessorConstant(index, attrs, getter, setter);
248 50 : }
249 :
250 5 : void SetAccessorConstant(int index, Handle<Object> getter,
251 : Handle<Object> setter) {
252 5 : SetAccessorConstant(index, attributes_[index], getter, setter);
253 5 : }
254 :
255 5 : void SetAccessorConstant(int index, Handle<AccessorPair> pair) {
256 15 : Handle<Object> getter = handle(pair->getter(), isolate_);
257 15 : Handle<Object> setter = handle(pair->setter(), isolate_);
258 5 : SetAccessorConstant(index, getter, setter);
259 5 : }
260 :
261 1085 : void GeneralizeField(int index) {
262 1085 : CHECK(index < number_of_properties_);
263 1085 : representations_[index] = Representation::Tagged();
264 1085 : if (locations_[index] == kField) {
265 1080 : values_[index] = FieldType::Any(isolate_);
266 : }
267 1085 : }
268 :
269 9395 : bool Check(DescriptorArray descriptors, int descriptor) const {
270 9395 : PropertyDetails details = descriptors->GetDetails(descriptor);
271 :
272 9395 : if (details.kind() != kinds_[descriptor]) return false;
273 9395 : if (details.location() != locations_[descriptor]) return false;
274 9395 : if (details.constness() != constnesses_[descriptor]) return false;
275 :
276 9395 : PropertyAttributes expected_attributes = attributes_[descriptor];
277 9395 : if (details.attributes() != expected_attributes) return false;
278 :
279 9395 : Representation expected_representation = representations_[descriptor];
280 9395 : if (!details.representation().Equals(expected_representation)) return false;
281 :
282 9395 : Object expected_value = *values_[descriptor];
283 9395 : if (details.location() == kField) {
284 9215 : if (details.kind() == kData) {
285 9215 : FieldType type = descriptors->GetFieldType(descriptor);
286 18430 : return FieldType::cast(expected_value) == type;
287 : } else {
288 : // kAccessor
289 0 : UNREACHABLE();
290 : }
291 : } else {
292 180 : Object value = descriptors->GetStrongValue(descriptor);
293 : // kDescriptor
294 180 : if (details.kind() == kData) {
295 : CHECK(!FLAG_track_constant_fields);
296 65 : return value == expected_value;
297 : } else {
298 : // kAccessor
299 115 : if (value == expected_value) return true;
300 115 : if (!value->IsAccessorPair()) return false;
301 115 : AccessorPair pair = AccessorPair::cast(value);
302 230 : return pair->Equals(expected_value, *setter_values_[descriptor]);
303 : }
304 : }
305 : UNREACHABLE();
306 : }
307 :
308 1415 : bool Check(Map map, int expected_nof) const {
309 1415 : CHECK_EQ(elements_kind_, map->elements_kind());
310 1415 : CHECK(number_of_properties_ <= MAX_PROPERTIES);
311 1415 : CHECK_EQ(expected_nof, map->NumberOfOwnDescriptors());
312 1415 : CHECK(!map->is_dictionary_map());
313 :
314 1415 : DescriptorArray descriptors = map->instance_descriptors();
315 1415 : CHECK(expected_nof <= number_of_properties_);
316 9395 : for (int i = 0; i < expected_nof; i++) {
317 9395 : if (!Check(descriptors, i)) {
318 0 : Print();
319 : #ifdef OBJECT_PRINT
320 : descriptors->Print();
321 : #endif
322 0 : Check(descriptors, i);
323 0 : return false;
324 : }
325 : }
326 : return true;
327 : }
328 :
329 1345 : bool Check(Map map) const { return Check(map, number_of_properties_); }
330 :
331 : //
332 : // Helper methods for initializing expectations and adding properties to
333 : // given |map|.
334 : //
335 :
336 30 : Handle<Map> AsElementsKind(Handle<Map> map, ElementsKind elements_kind) {
337 30 : elements_kind_ = elements_kind;
338 30 : map = Map::AsElementsKind(isolate_, map, elements_kind);
339 30 : CHECK_EQ(elements_kind_, map->elements_kind());
340 30 : return map;
341 : }
342 :
343 3090 : Handle<Map> AddDataField(Handle<Map> map, PropertyAttributes attributes,
344 : PropertyConstness constness,
345 : Representation representation,
346 : Handle<FieldType> field_type) {
347 3090 : CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
348 3090 : int property_index = number_of_properties_++;
349 : SetDataField(property_index, attributes, constness, representation,
350 : field_type);
351 :
352 3090 : Handle<String> name = MakeName("prop", property_index);
353 : return Map::CopyWithField(isolate_, map, name, field_type, attributes,
354 6180 : constness, representation, INSERT_TRANSITION)
355 9270 : .ToHandleChecked();
356 : }
357 :
358 25 : Handle<Map> AddDataConstant(Handle<Map> map, PropertyAttributes attributes,
359 : Handle<JSFunction> value) {
360 25 : CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
361 25 : int property_index = number_of_properties_++;
362 25 : SetDataConstant(property_index, attributes, value);
363 :
364 25 : Handle<String> name = MakeName("prop", property_index);
365 : return Map::CopyWithConstant(isolate_, map, name, value, attributes,
366 50 : INSERT_TRANSITION)
367 75 : .ToHandleChecked();
368 : }
369 :
370 15 : Handle<Map> TransitionToDataField(Handle<Map> map,
371 : PropertyAttributes attributes,
372 : PropertyConstness constness,
373 : Representation representation,
374 : Handle<FieldType> heap_type,
375 : Handle<Object> value) {
376 15 : CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
377 15 : int property_index = number_of_properties_++;
378 : SetDataField(property_index, attributes, constness, representation,
379 : heap_type);
380 :
381 15 : Handle<String> name = MakeName("prop", property_index);
382 : return Map::TransitionToDataProperty(isolate_, map, name, value, attributes,
383 15 : constness, StoreOrigin::kNamed);
384 : }
385 :
386 25 : Handle<Map> TransitionToDataConstant(Handle<Map> map,
387 : PropertyAttributes attributes,
388 : Handle<JSFunction> value) {
389 25 : CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
390 25 : int property_index = number_of_properties_++;
391 25 : SetDataConstant(property_index, attributes, value);
392 :
393 25 : Handle<String> name = MakeName("prop", property_index);
394 : return Map::TransitionToDataProperty(isolate_, map, name, value, attributes,
395 : PropertyConstness::kConst,
396 25 : StoreOrigin::kNamed);
397 : }
398 :
399 90 : Handle<Map> FollowDataTransition(Handle<Map> map,
400 : PropertyAttributes attributes,
401 : PropertyConstness constness,
402 : Representation representation,
403 : Handle<FieldType> heap_type) {
404 90 : CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
405 90 : int property_index = number_of_properties_++;
406 : SetDataField(property_index, attributes, constness, representation,
407 : heap_type);
408 :
409 90 : Handle<String> name = MakeName("prop", property_index);
410 : Map target = TransitionsAccessor(isolate_, map)
411 90 : .SearchTransition(*name, kData, attributes);
412 90 : CHECK(!target.is_null());
413 180 : return handle(target, isolate_);
414 : }
415 :
416 40 : Handle<Map> AddAccessorConstant(Handle<Map> map,
417 : PropertyAttributes attributes,
418 : Handle<AccessorPair> pair) {
419 40 : CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
420 40 : int property_index = number_of_properties_++;
421 40 : SetAccessorConstant(property_index, attributes, pair);
422 :
423 40 : Handle<String> name = MakeName("prop", property_index);
424 :
425 40 : Descriptor d = Descriptor::AccessorConstant(name, pair, attributes);
426 40 : return Map::CopyInsertDescriptor(isolate_, map, &d, INSERT_TRANSITION);
427 : }
428 :
429 : Handle<Map> AddAccessorConstant(Handle<Map> map,
430 : PropertyAttributes attributes,
431 : Handle<Object> getter,
432 : Handle<Object> setter) {
433 : CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
434 : int property_index = number_of_properties_++;
435 : SetAccessorConstant(property_index, attributes, getter, setter);
436 :
437 : Handle<String> name = MakeName("prop", property_index);
438 :
439 : CHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
440 : Factory* factory = isolate_->factory();
441 :
442 : if (!getter->IsNull(isolate_)) {
443 : Handle<AccessorPair> pair = factory->NewAccessorPair();
444 : pair->SetComponents(*getter, *factory->null_value());
445 : Descriptor d = Descriptor::AccessorConstant(name, pair, attributes);
446 : map = Map::CopyInsertDescriptor(isolate_, map, &d, INSERT_TRANSITION);
447 : }
448 : if (!setter->IsNull(isolate_)) {
449 : Handle<AccessorPair> pair = factory->NewAccessorPair();
450 : pair->SetComponents(*getter, *setter);
451 : Descriptor d = Descriptor::AccessorConstant(name, pair, attributes);
452 : map = Map::CopyInsertDescriptor(isolate_, map, &d, INSERT_TRANSITION);
453 : }
454 : return map;
455 : }
456 :
457 10 : Handle<Map> TransitionToAccessorConstant(Handle<Map> map,
458 : PropertyAttributes attributes,
459 : Handle<AccessorPair> pair) {
460 10 : CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
461 10 : int property_index = number_of_properties_++;
462 10 : SetAccessorConstant(property_index, attributes, pair);
463 :
464 10 : Handle<String> name = MakeName("prop", property_index);
465 :
466 : Isolate* isolate = CcTest::i_isolate();
467 20 : Handle<Object> getter(pair->getter(), isolate);
468 20 : Handle<Object> setter(pair->setter(), isolate);
469 :
470 : int descriptor =
471 20 : map->instance_descriptors()->SearchWithCache(isolate, *name, *map);
472 : map = Map::TransitionToAccessorProperty(isolate, map, name, descriptor,
473 10 : getter, setter, attributes);
474 10 : CHECK(!map->is_deprecated());
475 10 : CHECK(!map->is_dictionary_map());
476 10 : return map;
477 : }
478 : };
479 :
480 :
481 : ////////////////////////////////////////////////////////////////////////////////
482 : // A set of tests for property reconfiguration that makes new transition tree
483 : // branch.
484 : //
485 :
486 28342 : TEST(ReconfigureAccessorToNonExistingDataField) {
487 5 : CcTest::InitializeVM();
488 5 : v8::HandleScope scope(CcTest::isolate());
489 : Isolate* isolate = CcTest::i_isolate();
490 :
491 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
492 5 : Handle<FieldType> none_type = FieldType::None(isolate);
493 5 : Handle<AccessorPair> pair = CreateAccessorPair(true, true);
494 :
495 5 : Expectations expectations(isolate);
496 :
497 : // Create a map, add required properties to it and initialize expectations.
498 5 : Handle<Map> initial_map = Map::Create(isolate, 0);
499 5 : Handle<Map> map = initial_map;
500 5 : map = expectations.AddAccessorConstant(map, NONE, pair);
501 :
502 5 : CHECK(!map->is_deprecated());
503 5 : CHECK(map->is_stable());
504 5 : CHECK(expectations.Check(*map));
505 :
506 : Handle<Map> new_map = Map::ReconfigureProperty(
507 5 : isolate, map, 0, kData, NONE, Representation::None(), none_type);
508 : // |map| did not change except marked unstable.
509 5 : CHECK(!map->is_deprecated());
510 5 : CHECK(!map->is_stable());
511 5 : CHECK(expectations.Check(*map));
512 :
513 : // Property kind reconfiguration always makes the field mutable.
514 : expectations.SetDataField(0, NONE, PropertyConstness::kMutable,
515 : Representation::None(), none_type);
516 :
517 5 : CHECK(!new_map->is_deprecated());
518 5 : CHECK(new_map->is_stable());
519 5 : CHECK(expectations.Check(*new_map));
520 :
521 : Handle<Map> new_map2 = Map::ReconfigureProperty(
522 5 : isolate, map, 0, kData, NONE, Representation::None(), none_type);
523 15 : CHECK_EQ(*new_map, *new_map2);
524 :
525 : Handle<Object> value(Smi::kZero, isolate);
526 : Handle<Map> prepared_map = Map::PrepareForDataProperty(
527 5 : isolate, new_map, 0, PropertyConstness::kConst, value);
528 : // None to Smi generalization is trivial, map does not change.
529 15 : CHECK_EQ(*new_map, *prepared_map);
530 :
531 : expectations.SetDataField(0, NONE, PropertyConstness::kMutable,
532 : Representation::Smi(), any_type);
533 5 : CHECK(prepared_map->is_stable());
534 5 : CHECK(expectations.Check(*prepared_map));
535 :
536 : // Now create an object with |map|, migrate it to |prepared_map| and ensure
537 : // that the data property is uninitialized.
538 : Factory* factory = isolate->factory();
539 5 : Handle<JSObject> obj = factory->NewJSObjectFromMap(map);
540 5 : JSObject::MigrateToMap(obj, prepared_map);
541 5 : FieldIndex index = FieldIndex::ForDescriptor(*prepared_map, 0);
542 10 : CHECK(obj->RawFastPropertyAt(index)->IsUninitialized(isolate));
543 : #ifdef VERIFY_HEAP
544 : obj->ObjectVerify(isolate);
545 : #endif
546 5 : }
547 :
548 :
549 : // This test checks that the LookupIterator machinery involved in
550 : // JSObject::SetOwnPropertyIgnoreAttributes() does not try to migrate object
551 : // to a map with a property with None representation.
552 28342 : TEST(ReconfigureAccessorToNonExistingDataFieldHeavy) {
553 5 : CcTest::InitializeVM();
554 5 : v8::HandleScope scope(CcTest::isolate());
555 : Isolate* isolate = CcTest::i_isolate();
556 : Factory* factory = isolate->factory();
557 :
558 : CompileRun(
559 : "function getter() { return 1; };"
560 : "function setter() {};"
561 : "var o = {};"
562 : "Object.defineProperty(o, 'foo', "
563 : " { get: getter, set: setter, "
564 : " configurable: true, enumerable: true});");
565 :
566 5 : Handle<String> foo_str = factory->InternalizeUtf8String("foo");
567 5 : Handle<String> obj_name = factory->InternalizeUtf8String("o");
568 :
569 : Handle<Object> obj_value =
570 10 : Object::GetProperty(isolate, isolate->global_object(), obj_name)
571 10 : .ToHandleChecked();
572 10 : CHECK(obj_value->IsJSObject());
573 5 : Handle<JSObject> obj = Handle<JSObject>::cast(obj_value);
574 :
575 5 : CHECK_EQ(1, obj->map()->NumberOfOwnDescriptors());
576 10 : CHECK(
577 : obj->map()->instance_descriptors()->GetStrongValue(0)->IsAccessorPair());
578 :
579 : Handle<Object> value(Smi::FromInt(42), isolate);
580 10 : JSObject::SetOwnPropertyIgnoreAttributes(obj, foo_str, value, NONE).Check();
581 :
582 : // Check that the property contains |value|.
583 5 : CHECK_EQ(1, obj->map()->NumberOfOwnDescriptors());
584 5 : FieldIndex index = FieldIndex::ForDescriptor(obj->map(), 0);
585 5 : Object the_value = obj->RawFastPropertyAt(index);
586 5 : CHECK(the_value->IsSmi());
587 5 : CHECK_EQ(42, Smi::ToInt(the_value));
588 5 : }
589 :
590 :
591 : ////////////////////////////////////////////////////////////////////////////////
592 : // A set of tests for field generalization case.
593 : //
594 :
595 : // <Constness, Representation, FieldType> data.
596 : struct CRFTData {
597 : PropertyConstness constness;
598 : Representation representation;
599 : Handle<FieldType> type;
600 : };
601 :
602 : // This test ensures that field generalization at |property_index| is done
603 : // correctly independently of the fact that the |map| is detached from
604 : // transition tree or not.
605 : //
606 : // {} - p0 - p1 - p2: |detach_point_map|
607 : // |
608 : // X - detached at |detach_property_at_index|
609 : // |
610 : // + - p3 - p4: |map|
611 : //
612 : // Detaching does not happen if |detach_property_at_index| is -1.
613 : //
614 240 : static void TestGeneralizeField(int detach_property_at_index,
615 : int property_index, const CRFTData& from,
616 : const CRFTData& to, const CRFTData& expected,
617 : bool expected_deprecation,
618 : bool expected_field_type_dependency) {
619 240 : Isolate* isolate = CcTest::i_isolate();
620 240 : Handle<FieldType> any_type = FieldType::Any(isolate);
621 :
622 240 : CHECK(detach_property_at_index >= -1 &&
623 : detach_property_at_index < kPropCount);
624 240 : CHECK_LT(property_index, kPropCount);
625 240 : CHECK_NE(detach_property_at_index, property_index);
626 :
627 240 : const bool is_detached_map = detach_property_at_index >= 0;
628 :
629 240 : Expectations expectations(isolate);
630 :
631 : // Create a map, add required properties to it and initialize expectations.
632 240 : Handle<Map> initial_map = Map::Create(isolate, 0);
633 240 : Handle<Map> map = initial_map;
634 : Handle<Map> detach_point_map;
635 1920 : for (int i = 0; i < kPropCount; i++) {
636 1680 : if (i == property_index) {
637 : map = expectations.AddDataField(map, NONE, from.constness,
638 240 : from.representation, from.type);
639 : } else {
640 : map = expectations.AddDataField(map, NONE, kDefaultFieldConstness,
641 1440 : Representation::Smi(), any_type);
642 1440 : if (i == detach_property_at_index) {
643 : detach_point_map = map;
644 : }
645 : }
646 : }
647 240 : CHECK(!map->is_deprecated());
648 240 : CHECK(map->is_stable());
649 240 : CHECK(expectations.Check(*map));
650 :
651 240 : Zone zone(isolate->allocator(), ZONE_NAME);
652 :
653 240 : if (is_detached_map) {
654 : detach_point_map = Map::ReconfigureProperty(
655 : isolate, detach_point_map, detach_property_at_index, kData, NONE,
656 60 : Representation::Tagged(), any_type);
657 : expectations.SetDataField(detach_property_at_index, kDefaultFieldConstness,
658 60 : Representation::Tagged(), any_type);
659 60 : CHECK(map->is_deprecated());
660 60 : CHECK(expectations.Check(*detach_point_map,
661 : detach_point_map->NumberOfOwnDescriptors()));
662 : }
663 :
664 : // Create new maps by generalizing representation of propX field.
665 480 : CanonicalHandleScope canonical(isolate);
666 240 : JSHeapBroker broker(isolate, &zone);
667 240 : CompilationDependencies dependencies(isolate, &zone);
668 : MapRef map_ref(&broker, map);
669 240 : map_ref.SerializeOwnDescriptors();
670 240 : dependencies.DependOnFieldType(map_ref, property_index);
671 :
672 : Handle<Map> field_owner(map->FindFieldOwner(isolate, property_index),
673 480 : isolate);
674 : Handle<Map> new_map = Map::ReconfigureProperty(
675 240 : isolate, map, property_index, kData, NONE, to.representation, to.type);
676 :
677 : expectations.SetDataField(property_index, expected.constness,
678 240 : expected.representation, expected.type);
679 :
680 240 : CHECK(!new_map->is_deprecated());
681 240 : CHECK(expectations.Check(*new_map));
682 :
683 240 : if (is_detached_map) {
684 60 : CHECK(!map->is_stable());
685 60 : CHECK(map->is_deprecated());
686 180 : CHECK_NE(*map, *new_map);
687 70 : CHECK_EQ(expected_field_type_dependency && !field_owner->is_deprecated(),
688 : !dependencies.AreValid());
689 :
690 180 : } else if (expected_deprecation) {
691 75 : CHECK(!map->is_stable());
692 75 : CHECK(map->is_deprecated());
693 75 : CHECK(field_owner->is_deprecated());
694 225 : CHECK_NE(*map, *new_map);
695 75 : CHECK(dependencies.AreValid());
696 :
697 : } else {
698 105 : CHECK(!field_owner->is_deprecated());
699 105 : CHECK(map->is_stable()); // Map did not change, must be left stable.
700 315 : CHECK_EQ(*map, *new_map);
701 :
702 105 : CHECK_EQ(expected_field_type_dependency, !dependencies.AreValid());
703 : }
704 :
705 : {
706 : // Check that all previous maps are not stable.
707 240 : Map tmp = *new_map;
708 : while (true) {
709 1920 : Object back = tmp->GetBackPointer();
710 1920 : if (back->IsUndefined(isolate)) break;
711 1680 : tmp = Map::cast(back);
712 1680 : CHECK(!tmp->is_stable());
713 1680 : }
714 : }
715 :
716 : // Update all deprecated maps and check that they are now the same.
717 240 : Handle<Map> updated_map = Map::Update(isolate, map);
718 720 : CHECK_EQ(*new_map, *updated_map);
719 480 : CheckMigrationTarget(isolate, *map, *updated_map);
720 240 : }
721 :
722 50 : static void TestGeneralizeField(const CRFTData& from, const CRFTData& to,
723 : const CRFTData& expected,
724 : bool expected_deprecation,
725 : bool expected_field_type_dependency) {
726 : // Check the cases when the map being reconfigured is a part of the
727 : // transition tree.
728 : STATIC_ASSERT(kPropCount > 4);
729 50 : int indices[] = {0, 2, kPropCount - 1};
730 200 : for (int i = 0; i < static_cast<int>(arraysize(indices)); i++) {
731 : TestGeneralizeField(-1, indices[i], from, to, expected,
732 150 : expected_deprecation, expected_field_type_dependency);
733 : }
734 :
735 50 : if (!from.representation.IsNone()) {
736 : // Check the cases when the map being reconfigured is NOT a part of the
737 : // transition tree. "None -> anything" representation changes make sense
738 : // only for "attached" maps.
739 30 : int indices[] = {0, kPropCount - 1};
740 90 : for (int i = 0; i < static_cast<int>(arraysize(indices)); i++) {
741 : TestGeneralizeField(indices[i], 2, from, to, expected,
742 60 : expected_deprecation, expected_field_type_dependency);
743 : }
744 :
745 : // Check that reconfiguration to the very same field works correctly.
746 30 : CRFTData data = from;
747 30 : TestGeneralizeField(-1, 2, data, data, data, false, false);
748 : }
749 50 : }
750 :
751 : static void TestGeneralizeField(const CRFTData& from, const CRFTData& to,
752 : const CRFTData& expected) {
753 : const bool expected_deprecation = true;
754 : const bool expected_field_type_dependency = false;
755 :
756 : TestGeneralizeField(from, to, expected, expected_deprecation,
757 25 : expected_field_type_dependency);
758 : }
759 :
760 : static void TestGeneralizeFieldTrivial(
761 : const CRFTData& from, const CRFTData& to, const CRFTData& expected,
762 : bool expected_field_type_dependency = true) {
763 : const bool expected_deprecation = false;
764 :
765 : TestGeneralizeField(from, to, expected, expected_deprecation,
766 25 : expected_field_type_dependency);
767 : }
768 :
769 28342 : TEST(GeneralizeSmiFieldToDouble) {
770 5 : CcTest::InitializeVM();
771 5 : v8::HandleScope scope(CcTest::isolate());
772 : Isolate* isolate = CcTest::i_isolate();
773 :
774 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
775 :
776 : TestGeneralizeField(
777 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
778 : {PropertyConstness::kMutable, Representation::Double(), any_type},
779 10 : {PropertyConstness::kMutable, Representation::Double(), any_type});
780 5 : }
781 :
782 28342 : TEST(GeneralizeSmiFieldToTagged) {
783 5 : CcTest::InitializeVM();
784 5 : v8::HandleScope scope(CcTest::isolate());
785 : Isolate* isolate = CcTest::i_isolate();
786 :
787 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
788 : Handle<FieldType> value_type =
789 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
790 :
791 : TestGeneralizeField(
792 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
793 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
794 10 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
795 5 : }
796 :
797 28342 : TEST(GeneralizeDoubleFieldToTagged) {
798 5 : CcTest::InitializeVM();
799 5 : v8::HandleScope scope(CcTest::isolate());
800 : Isolate* isolate = CcTest::i_isolate();
801 :
802 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
803 : Handle<FieldType> value_type =
804 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
805 :
806 : TestGeneralizeField(
807 : {PropertyConstness::kMutable, Representation::Double(), any_type},
808 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
809 10 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
810 5 : }
811 :
812 28342 : TEST(GeneralizeHeapObjectFieldToTagged) {
813 5 : CcTest::InitializeVM();
814 5 : v8::HandleScope scope(CcTest::isolate());
815 : Isolate* isolate = CcTest::i_isolate();
816 :
817 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
818 : Handle<FieldType> value_type =
819 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
820 :
821 : TestGeneralizeField(
822 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
823 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
824 10 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
825 5 : }
826 :
827 28342 : TEST(GeneralizeHeapObjectFieldToHeapObject) {
828 5 : CcTest::InitializeVM();
829 5 : v8::HandleScope scope(CcTest::isolate());
830 : Isolate* isolate = CcTest::i_isolate();
831 :
832 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
833 :
834 : Handle<FieldType> current_type =
835 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
836 :
837 : Handle<FieldType> new_type =
838 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
839 :
840 5 : Handle<FieldType> expected_type = any_type;
841 :
842 : TestGeneralizeFieldTrivial(
843 : {PropertyConstness::kMutable, Representation::HeapObject(), current_type},
844 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
845 : {PropertyConstness::kMutable, Representation::HeapObject(),
846 10 : expected_type});
847 : current_type = expected_type;
848 :
849 5 : new_type = FieldType::Class(Map::Create(isolate, 0), isolate);
850 :
851 : TestGeneralizeFieldTrivial(
852 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type},
853 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
854 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type},
855 10 : false);
856 5 : }
857 :
858 28342 : TEST(GeneralizeNoneFieldToSmi) {
859 5 : CcTest::InitializeVM();
860 5 : v8::HandleScope scope(CcTest::isolate());
861 : Isolate* isolate = CcTest::i_isolate();
862 :
863 5 : Handle<FieldType> none_type = FieldType::None(isolate);
864 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
865 :
866 : // None -> Smi representation change is trivial.
867 : TestGeneralizeFieldTrivial(
868 : {PropertyConstness::kMutable, Representation::None(), none_type},
869 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
870 10 : {PropertyConstness::kMutable, Representation::Smi(), any_type});
871 5 : }
872 :
873 28342 : TEST(GeneralizeNoneFieldToDouble) {
874 5 : CcTest::InitializeVM();
875 5 : v8::HandleScope scope(CcTest::isolate());
876 : Isolate* isolate = CcTest::i_isolate();
877 :
878 5 : Handle<FieldType> none_type = FieldType::None(isolate);
879 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
880 :
881 : // None -> Double representation change is NOT trivial.
882 : TestGeneralizeField(
883 : {PropertyConstness::kMutable, Representation::None(), none_type},
884 : {PropertyConstness::kMutable, Representation::Double(), any_type},
885 10 : {PropertyConstness::kMutable, Representation::Double(), any_type});
886 5 : }
887 :
888 28342 : TEST(GeneralizeNoneFieldToHeapObject) {
889 5 : CcTest::InitializeVM();
890 5 : v8::HandleScope scope(CcTest::isolate());
891 : Isolate* isolate = CcTest::i_isolate();
892 :
893 5 : Handle<FieldType> none_type = FieldType::None(isolate);
894 : Handle<FieldType> value_type =
895 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
896 :
897 : // None -> HeapObject representation change is trivial.
898 : TestGeneralizeFieldTrivial(
899 : {PropertyConstness::kMutable, Representation::None(), none_type},
900 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
901 10 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type});
902 5 : }
903 :
904 28342 : TEST(GeneralizeNoneFieldToTagged) {
905 5 : CcTest::InitializeVM();
906 5 : v8::HandleScope scope(CcTest::isolate());
907 : Isolate* isolate = CcTest::i_isolate();
908 :
909 5 : Handle<FieldType> none_type = FieldType::None(isolate);
910 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
911 :
912 : // None -> HeapObject representation change is trivial.
913 : TestGeneralizeFieldTrivial(
914 : {PropertyConstness::kMutable, Representation::None(), none_type},
915 : {PropertyConstness::kMutable, Representation::Tagged(), any_type},
916 10 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
917 5 : }
918 :
919 :
920 : ////////////////////////////////////////////////////////////////////////////////
921 : // A set of tests for field generalization case with kAccessor properties.
922 : //
923 :
924 28342 : TEST(GeneralizeFieldWithAccessorProperties) {
925 5 : CcTest::InitializeVM();
926 5 : v8::HandleScope scope(CcTest::isolate());
927 : Isolate* isolate = CcTest::i_isolate();
928 :
929 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
930 5 : Handle<AccessorPair> pair = CreateAccessorPair(true, true);
931 :
932 : const int kAccessorProp = kPropCount / 2;
933 5 : Expectations expectations(isolate);
934 :
935 : // Create a map, add required properties to it and initialize expectations.
936 5 : Handle<Map> initial_map = Map::Create(isolate, 0);
937 5 : Handle<Map> map = initial_map;
938 40 : for (int i = 0; i < kPropCount; i++) {
939 35 : if (i == kAccessorProp) {
940 5 : map = expectations.AddAccessorConstant(map, NONE, pair);
941 : } else {
942 : map = expectations.AddDataField(map, NONE, PropertyConstness::kMutable,
943 30 : Representation::Smi(), any_type);
944 : }
945 : }
946 5 : CHECK(!map->is_deprecated());
947 5 : CHECK(map->is_stable());
948 5 : CHECK(expectations.Check(*map));
949 :
950 : // Create new maps by generalizing representation of propX field.
951 35 : Handle<Map> maps[kPropCount];
952 35 : for (int i = 0; i < kPropCount; i++) {
953 35 : if (i == kAccessorProp) {
954 : // Skip accessor property reconfiguration.
955 5 : maps[i] = maps[i - 1];
956 : continue;
957 : }
958 : Handle<Map> new_map = Map::ReconfigureProperty(
959 30 : isolate, map, i, kData, NONE, Representation::Double(), any_type);
960 30 : maps[i] = new_map;
961 :
962 : expectations.SetDataField(i, PropertyConstness::kMutable,
963 30 : Representation::Double(), any_type);
964 :
965 30 : CHECK(!map->is_stable());
966 30 : CHECK(map->is_deprecated());
967 90 : CHECK_NE(*map, *new_map);
968 55 : CHECK(i == 0 || maps[i - 1]->is_deprecated());
969 :
970 30 : CHECK(!new_map->is_deprecated());
971 30 : CHECK(expectations.Check(*new_map));
972 : }
973 :
974 5 : Handle<Map> active_map = maps[kPropCount - 1];
975 5 : CHECK(!active_map->is_deprecated());
976 :
977 : // Update all deprecated maps and check that they are now the same.
978 5 : Handle<Map> updated_map = Map::Update(isolate, map);
979 15 : CHECK_EQ(*active_map, *updated_map);
980 5 : CheckMigrationTarget(isolate, *map, *updated_map);
981 40 : for (int i = 0; i < kPropCount; i++) {
982 35 : updated_map = Map::Update(isolate, maps[i]);
983 105 : CHECK_EQ(*active_map, *updated_map);
984 70 : CheckMigrationTarget(isolate, *maps[i], *updated_map);
985 5 : }
986 5 : }
987 :
988 :
989 : ////////////////////////////////////////////////////////////////////////////////
990 : // A set of tests for attribute reconfiguration case.
991 : //
992 :
993 : // This test ensures that field generalization is correctly propagated from one
994 : // branch of transition tree (|map2|) to another (|map|).
995 : //
996 : // + - p2B - p3 - p4: |map2|
997 : // |
998 : // {} - p0 - p1 - p2A - p3 - p4: |map|
999 : //
1000 : // where "p2A" and "p2B" differ only in the attributes.
1001 : //
1002 20 : static void TestReconfigureDataFieldAttribute_GeneralizeField(
1003 : const CRFTData& from, const CRFTData& to, const CRFTData& expected) {
1004 20 : Isolate* isolate = CcTest::i_isolate();
1005 :
1006 20 : Expectations expectations(isolate);
1007 :
1008 : // Create a map, add required properties to it and initialize expectations.
1009 20 : Handle<Map> initial_map = Map::Create(isolate, 0);
1010 20 : Handle<Map> map = initial_map;
1011 160 : for (int i = 0; i < kPropCount; i++) {
1012 : map = expectations.AddDataField(map, NONE, from.constness,
1013 140 : from.representation, from.type);
1014 : }
1015 20 : CHECK(!map->is_deprecated());
1016 20 : CHECK(map->is_stable());
1017 20 : CHECK(expectations.Check(*map));
1018 :
1019 :
1020 : // Create another branch in transition tree (property at index |kSplitProp|
1021 : // has different attributes), initialize expectations.
1022 : const int kSplitProp = kPropCount / 2;
1023 20 : Expectations expectations2(isolate);
1024 :
1025 : Handle<Map> map2 = initial_map;
1026 80 : for (int i = 0; i < kSplitProp; i++) {
1027 : map2 = expectations2.FollowDataTransition(map2, NONE, from.constness,
1028 60 : from.representation, from.type);
1029 : }
1030 : map2 = expectations2.AddDataField(map2, READ_ONLY, to.constness,
1031 20 : to.representation, to.type);
1032 :
1033 80 : for (int i = kSplitProp + 1; i < kPropCount; i++) {
1034 : map2 = expectations2.AddDataField(map2, NONE, to.constness,
1035 60 : to.representation, to.type);
1036 : }
1037 20 : CHECK(!map2->is_deprecated());
1038 20 : CHECK(map2->is_stable());
1039 20 : CHECK(expectations2.Check(*map2));
1040 :
1041 20 : Zone zone(isolate->allocator(), ZONE_NAME);
1042 40 : CanonicalHandleScope canonical(isolate);
1043 20 : JSHeapBroker broker(isolate, &zone);
1044 20 : CompilationDependencies dependencies(isolate, &zone);
1045 : MapRef map_ref(&broker, map);
1046 20 : map_ref.SerializeOwnDescriptors();
1047 20 : dependencies.DependOnFieldType(map_ref, kSplitProp);
1048 :
1049 : // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which
1050 : // should generalize representations in |map1|.
1051 : Handle<Map> new_map =
1052 20 : Map::ReconfigureExistingProperty(isolate, map2, kSplitProp, kData, NONE);
1053 :
1054 : // |map2| should be left unchanged but marked unstable.
1055 20 : CHECK(!map2->is_stable());
1056 20 : CHECK(!map2->is_deprecated());
1057 60 : CHECK_NE(*map2, *new_map);
1058 20 : CHECK(expectations2.Check(*map2));
1059 :
1060 : // |map| should be deprecated and |new_map| should match new expectations.
1061 80 : for (int i = kSplitProp; i < kPropCount; i++) {
1062 : expectations.SetDataField(i, expected.constness, expected.representation,
1063 80 : expected.type);
1064 : }
1065 20 : CHECK(map->is_deprecated());
1066 20 : CHECK(dependencies.AreValid());
1067 60 : CHECK_NE(*map, *new_map);
1068 :
1069 20 : CHECK(!new_map->is_deprecated());
1070 20 : CHECK(expectations.Check(*new_map));
1071 :
1072 : // Update deprecated |map|, it should become |new_map|.
1073 20 : Handle<Map> updated_map = Map::Update(isolate, map);
1074 60 : CHECK_EQ(*new_map, *updated_map);
1075 40 : CheckMigrationTarget(isolate, *map, *updated_map);
1076 20 : }
1077 :
1078 : // This test ensures that trivial field generalization (from HeapObject to
1079 : // HeapObject) is correctly propagated from one branch of transition tree
1080 : // (|map2|) to another (|map|).
1081 : //
1082 : // + - p2B - p3 - p4: |map2|
1083 : // |
1084 : // {} - p0 - p1 - p2A - p3 - p4: |map|
1085 : //
1086 : // where "p2A" and "p2B" differ only in the attributes.
1087 : //
1088 10 : static void TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1089 : const CRFTData& from, const CRFTData& to, const CRFTData& expected,
1090 : bool expected_field_type_dependency = true) {
1091 10 : Isolate* isolate = CcTest::i_isolate();
1092 :
1093 10 : Expectations expectations(isolate);
1094 :
1095 : // Create a map, add required properties to it and initialize expectations.
1096 10 : Handle<Map> initial_map = Map::Create(isolate, 0);
1097 10 : Handle<Map> map = initial_map;
1098 80 : for (int i = 0; i < kPropCount; i++) {
1099 : map = expectations.AddDataField(map, NONE, from.constness,
1100 70 : from.representation, from.type);
1101 : }
1102 10 : CHECK(!map->is_deprecated());
1103 10 : CHECK(map->is_stable());
1104 10 : CHECK(expectations.Check(*map));
1105 :
1106 :
1107 : // Create another branch in transition tree (property at index |kSplitProp|
1108 : // has different attributes), initialize expectations.
1109 : const int kSplitProp = kPropCount / 2;
1110 10 : Expectations expectations2(isolate);
1111 :
1112 : Handle<Map> map2 = initial_map;
1113 40 : for (int i = 0; i < kSplitProp; i++) {
1114 : map2 = expectations2.FollowDataTransition(map2, NONE, from.constness,
1115 30 : from.representation, from.type);
1116 : }
1117 : map2 = expectations2.AddDataField(map2, READ_ONLY, to.constness,
1118 10 : to.representation, to.type);
1119 :
1120 40 : for (int i = kSplitProp + 1; i < kPropCount; i++) {
1121 : map2 = expectations2.AddDataField(map2, NONE, to.constness,
1122 30 : to.representation, to.type);
1123 : }
1124 10 : CHECK(!map2->is_deprecated());
1125 10 : CHECK(map2->is_stable());
1126 10 : CHECK(expectations2.Check(*map2));
1127 :
1128 10 : Zone zone(isolate->allocator(), ZONE_NAME);
1129 20 : CanonicalHandleScope canonical(isolate);
1130 10 : JSHeapBroker broker(isolate, &zone);
1131 10 : CompilationDependencies dependencies(isolate, &zone);
1132 : MapRef map_ref(&broker, map);
1133 10 : map_ref.SerializeOwnDescriptors();
1134 10 : dependencies.DependOnFieldType(map_ref, kSplitProp);
1135 :
1136 : // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which
1137 : // should generalize representations in |map1|.
1138 : Handle<Map> new_map =
1139 10 : Map::ReconfigureExistingProperty(isolate, map2, kSplitProp, kData, NONE);
1140 :
1141 : // |map2| should be left unchanged but marked unstable.
1142 10 : CHECK(!map2->is_stable());
1143 10 : CHECK(!map2->is_deprecated());
1144 30 : CHECK_NE(*map2, *new_map);
1145 10 : CHECK(expectations2.Check(*map2));
1146 :
1147 : // In trivial case |map| should be returned as a result of the property
1148 : // reconfiguration, respective field types should be generalized and
1149 : // respective code dependencies should be invalidated. |map| should be NOT
1150 : // deprecated and it should match new expectations.
1151 40 : for (int i = kSplitProp; i < kPropCount; i++) {
1152 : expectations.SetDataField(i, expected.constness, expected.representation,
1153 40 : expected.type);
1154 : }
1155 10 : CHECK(!map->is_deprecated());
1156 30 : CHECK_EQ(*map, *new_map);
1157 10 : CHECK_EQ(expected_field_type_dependency, !dependencies.AreValid());
1158 :
1159 10 : CHECK(!new_map->is_deprecated());
1160 10 : CHECK(expectations.Check(*new_map));
1161 :
1162 10 : Handle<Map> updated_map = Map::Update(isolate, map);
1163 40 : CHECK_EQ(*new_map, *updated_map);
1164 10 : }
1165 :
1166 28342 : TEST(ReconfigureDataFieldAttribute_GeneralizeSmiFieldToDouble) {
1167 5 : CcTest::InitializeVM();
1168 5 : v8::HandleScope scope(CcTest::isolate());
1169 : Isolate* isolate = CcTest::i_isolate();
1170 :
1171 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1172 :
1173 : if (FLAG_track_constant_fields) {
1174 : TestReconfigureDataFieldAttribute_GeneralizeField(
1175 : {PropertyConstness::kConst, Representation::Smi(), any_type},
1176 : {PropertyConstness::kConst, Representation::Double(), any_type},
1177 : {PropertyConstness::kConst, Representation::Double(), any_type});
1178 :
1179 : TestReconfigureDataFieldAttribute_GeneralizeField(
1180 : {PropertyConstness::kConst, Representation::Smi(), any_type},
1181 : {PropertyConstness::kMutable, Representation::Double(), any_type},
1182 : {PropertyConstness::kMutable, Representation::Double(), any_type});
1183 :
1184 : TestReconfigureDataFieldAttribute_GeneralizeField(
1185 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
1186 : {PropertyConstness::kConst, Representation::Double(), any_type},
1187 : {PropertyConstness::kMutable, Representation::Double(), any_type});
1188 : }
1189 :
1190 : TestReconfigureDataFieldAttribute_GeneralizeField(
1191 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
1192 : {PropertyConstness::kMutable, Representation::Double(), any_type},
1193 5 : {PropertyConstness::kMutable, Representation::Double(), any_type});
1194 5 : }
1195 :
1196 28342 : TEST(ReconfigureDataFieldAttribute_GeneralizeSmiFieldToTagged) {
1197 5 : CcTest::InitializeVM();
1198 5 : v8::HandleScope scope(CcTest::isolate());
1199 : Isolate* isolate = CcTest::i_isolate();
1200 :
1201 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1202 : Handle<FieldType> value_type =
1203 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
1204 :
1205 : if (FLAG_track_constant_fields) {
1206 : TestReconfigureDataFieldAttribute_GeneralizeField(
1207 : {PropertyConstness::kConst, Representation::Smi(), any_type},
1208 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
1209 : {PropertyConstness::kConst, Representation::Tagged(), any_type});
1210 :
1211 : TestReconfigureDataFieldAttribute_GeneralizeField(
1212 : {PropertyConstness::kConst, Representation::Smi(), any_type},
1213 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
1214 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
1215 :
1216 : TestReconfigureDataFieldAttribute_GeneralizeField(
1217 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
1218 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
1219 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
1220 : }
1221 :
1222 : TestReconfigureDataFieldAttribute_GeneralizeField(
1223 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
1224 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
1225 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
1226 5 : }
1227 :
1228 28342 : TEST(ReconfigureDataFieldAttribute_GeneralizeDoubleFieldToTagged) {
1229 5 : CcTest::InitializeVM();
1230 5 : v8::HandleScope scope(CcTest::isolate());
1231 : Isolate* isolate = CcTest::i_isolate();
1232 :
1233 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1234 : Handle<FieldType> value_type =
1235 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
1236 :
1237 : if (FLAG_track_constant_fields) {
1238 : TestReconfigureDataFieldAttribute_GeneralizeField(
1239 : {PropertyConstness::kConst, Representation::Double(), any_type},
1240 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
1241 : {PropertyConstness::kConst, Representation::Tagged(), any_type});
1242 :
1243 : TestReconfigureDataFieldAttribute_GeneralizeField(
1244 : {PropertyConstness::kConst, Representation::Double(), any_type},
1245 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
1246 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
1247 :
1248 : TestReconfigureDataFieldAttribute_GeneralizeField(
1249 : {PropertyConstness::kMutable, Representation::Double(), any_type},
1250 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
1251 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
1252 : }
1253 :
1254 : TestReconfigureDataFieldAttribute_GeneralizeField(
1255 : {PropertyConstness::kMutable, Representation::Double(), any_type},
1256 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
1257 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
1258 5 : }
1259 :
1260 28342 : TEST(ReconfigureDataFieldAttribute_GeneralizeHeapObjFieldToHeapObj) {
1261 5 : CcTest::InitializeVM();
1262 5 : v8::HandleScope scope(CcTest::isolate());
1263 : Isolate* isolate = CcTest::i_isolate();
1264 :
1265 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1266 :
1267 : Handle<FieldType> current_type =
1268 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
1269 :
1270 : Handle<FieldType> new_type =
1271 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
1272 :
1273 5 : Handle<FieldType> expected_type = any_type;
1274 :
1275 : // Check generalizations that trigger deopts.
1276 : if (FLAG_track_constant_fields) {
1277 : TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1278 : {PropertyConstness::kConst, Representation::HeapObject(), current_type},
1279 : {PropertyConstness::kConst, Representation::HeapObject(), new_type},
1280 : {PropertyConstness::kConst, Representation::HeapObject(),
1281 : expected_type});
1282 :
1283 : if (FLAG_modify_map_inplace) {
1284 : // PropertyConstness::kConst to PropertyConstness::kMutable migration does
1285 : // not create a new map, therefore trivial generalization.
1286 : TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1287 : {PropertyConstness::kConst, Representation::HeapObject(),
1288 : current_type},
1289 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
1290 : {PropertyConstness::kMutable, Representation::HeapObject(),
1291 : expected_type});
1292 : } else {
1293 : // PropertyConstness::kConst to PropertyConstness::kMutable migration
1294 : // causes map change, therefore non-trivial generalization.
1295 : TestReconfigureDataFieldAttribute_GeneralizeField(
1296 : {PropertyConstness::kConst, Representation::HeapObject(),
1297 : current_type},
1298 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
1299 : {PropertyConstness::kMutable, Representation::HeapObject(),
1300 : expected_type});
1301 : }
1302 : TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1303 : {PropertyConstness::kMutable, Representation::HeapObject(),
1304 : current_type},
1305 : {PropertyConstness::kConst, Representation::HeapObject(), new_type},
1306 : {PropertyConstness::kMutable, Representation::HeapObject(),
1307 : expected_type});
1308 : }
1309 : TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1310 : {PropertyConstness::kMutable, Representation::HeapObject(), current_type},
1311 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
1312 : {PropertyConstness::kMutable, Representation::HeapObject(),
1313 5 : expected_type});
1314 : current_type = expected_type;
1315 :
1316 : // Check generalizations that do not trigger deopts.
1317 5 : new_type = FieldType::Class(Map::Create(isolate, 0), isolate);
1318 :
1319 : if (FLAG_track_constant_fields) {
1320 : TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1321 : {PropertyConstness::kConst, Representation::HeapObject(), any_type},
1322 : {PropertyConstness::kConst, Representation::HeapObject(), new_type},
1323 : {PropertyConstness::kConst, Representation::HeapObject(), any_type},
1324 : false);
1325 :
1326 : if (FLAG_modify_map_inplace) {
1327 : // PropertyConstness::kConst to PropertyConstness::kMutable migration does
1328 : // not create a new map, therefore trivial generalization.
1329 : TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1330 : {PropertyConstness::kConst, Representation::HeapObject(), any_type},
1331 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
1332 : {PropertyConstness::kMutable, Representation::HeapObject(),
1333 : any_type});
1334 : } else {
1335 : // PropertyConstness::kConst to PropertyConstness::kMutable migration
1336 : // causes map change, therefore non-trivial generalization.
1337 : TestReconfigureDataFieldAttribute_GeneralizeField(
1338 : {PropertyConstness::kConst, Representation::HeapObject(), any_type},
1339 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
1340 : {PropertyConstness::kMutable, Representation::HeapObject(),
1341 : any_type});
1342 : }
1343 : TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1344 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type},
1345 : {PropertyConstness::kConst, Representation::HeapObject(), new_type},
1346 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type},
1347 : false);
1348 : }
1349 : TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
1350 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type},
1351 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
1352 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type},
1353 5 : false);
1354 5 : }
1355 :
1356 28342 : TEST(ReconfigureDataFieldAttribute_GeneralizeHeapObjectFieldToTagged) {
1357 5 : CcTest::InitializeVM();
1358 5 : v8::HandleScope scope(CcTest::isolate());
1359 : Isolate* isolate = CcTest::i_isolate();
1360 :
1361 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1362 : Handle<FieldType> value_type =
1363 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
1364 :
1365 : TestReconfigureDataFieldAttribute_GeneralizeField(
1366 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
1367 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
1368 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
1369 5 : }
1370 :
1371 :
1372 : // Checks that given |map| is deprecated and that it updates to given |new_map|
1373 : // which in turn should match expectations.
1374 : struct CheckDeprecated {
1375 5 : void Check(Isolate* isolate, Handle<Map> map, Handle<Map> new_map,
1376 : const Expectations& expectations) {
1377 5 : CHECK(map->is_deprecated());
1378 15 : CHECK_NE(*map, *new_map);
1379 :
1380 5 : CHECK(!new_map->is_deprecated());
1381 5 : CHECK(expectations.Check(*new_map));
1382 :
1383 : // Update deprecated |map|, it should become |new_map|.
1384 5 : Handle<Map> updated_map = Map::Update(isolate, map);
1385 15 : CHECK_EQ(*new_map, *updated_map);
1386 5 : CheckMigrationTarget(isolate, *map, *updated_map);
1387 5 : }
1388 : };
1389 :
1390 :
1391 : // Checks that given |map| is NOT deprecated, equals to given |new_map| and
1392 : // matches expectations.
1393 : struct CheckSameMap {
1394 10 : void Check(Isolate* isolate, Handle<Map> map, Handle<Map> new_map,
1395 : const Expectations& expectations) {
1396 : // |map| was not reconfigured, therefore it should stay stable.
1397 10 : CHECK(map->is_stable());
1398 10 : CHECK(!map->is_deprecated());
1399 30 : CHECK_EQ(*map, *new_map);
1400 :
1401 10 : CHECK(!new_map->is_deprecated());
1402 10 : CHECK(expectations.Check(*new_map));
1403 :
1404 : // Update deprecated |map|, it should become |new_map|.
1405 10 : Handle<Map> updated_map = Map::Update(isolate, map);
1406 30 : CHECK_EQ(*new_map, *updated_map);
1407 10 : }
1408 : };
1409 :
1410 :
1411 : // Checks that given |map| is NOT deprecated and matches expectations.
1412 : // |new_map| is unrelated to |map|.
1413 : struct CheckUnrelated {
1414 10 : void Check(Isolate* isolate, Handle<Map> map, Handle<Map> new_map,
1415 : const Expectations& expectations) {
1416 10 : CHECK(!map->is_deprecated());
1417 30 : CHECK_NE(*map, *new_map);
1418 10 : CHECK(expectations.Check(*map));
1419 :
1420 10 : CHECK(new_map->is_stable());
1421 10 : CHECK(!new_map->is_deprecated());
1422 10 : }
1423 : };
1424 :
1425 :
1426 : // Checks that given |map| is NOT deprecated, and |new_map| is a result of
1427 : // copy-generalize-all-representations.
1428 : struct CheckCopyGeneralizeAllFields {
1429 5 : void Check(Isolate* isolate, Handle<Map> map, Handle<Map> new_map,
1430 : Expectations& expectations) {
1431 5 : CHECK(!map->is_deprecated());
1432 15 : CHECK_NE(*map, *new_map);
1433 :
1434 10 : CHECK(new_map->GetBackPointer()->IsUndefined(isolate));
1435 35 : for (int i = 0; i < kPropCount; i++) {
1436 35 : expectations.GeneralizeField(i);
1437 : }
1438 :
1439 5 : CHECK(!new_map->is_deprecated());
1440 5 : CHECK(expectations.Check(*new_map));
1441 5 : }
1442 : };
1443 :
1444 : // This test ensures that field generalization is correctly propagated from one
1445 : // branch of transition tree (|map2|) to another (|map1|).
1446 : //
1447 : // + - p2B - p3 - p4: |map2|
1448 : // |
1449 : // {} - p0 - p1: |map|
1450 : // |
1451 : // + - p2A - p3 - p4: |map1|
1452 : // |
1453 : // + - the property customized by the TestConfig provided
1454 : //
1455 : // where "p2A" and "p2B" differ only in the attributes.
1456 : //
1457 : template <typename TestConfig, typename Checker>
1458 30 : static void TestReconfigureProperty_CustomPropertyAfterTargetMap(
1459 : TestConfig& config, Checker& checker) {
1460 : Isolate* isolate = CcTest::i_isolate();
1461 30 : Handle<FieldType> any_type = FieldType::Any(isolate);
1462 :
1463 : const int kCustomPropIndex = kPropCount - 2;
1464 30 : Expectations expectations(isolate);
1465 :
1466 : const int kSplitProp = 2;
1467 : CHECK_LT(kSplitProp, kCustomPropIndex);
1468 :
1469 : const PropertyConstness constness = PropertyConstness::kMutable;
1470 30 : const Representation representation = Representation::Smi();
1471 :
1472 : // Create common part of transition tree.
1473 30 : Handle<Map> initial_map = Map::Create(isolate, 0);
1474 30 : Handle<Map> map = initial_map;
1475 90 : for (int i = 0; i < kSplitProp; i++) {
1476 60 : map = expectations.AddDataField(map, NONE, constness, representation,
1477 : any_type);
1478 : }
1479 30 : CHECK(!map->is_deprecated());
1480 30 : CHECK(map->is_stable());
1481 30 : CHECK(expectations.Check(*map));
1482 :
1483 :
1484 : // Create branch to |map1|.
1485 : Handle<Map> map1 = map;
1486 30 : Expectations expectations1 = expectations;
1487 120 : for (int i = kSplitProp; i < kCustomPropIndex; i++) {
1488 90 : map1 = expectations1.AddDataField(map1, NONE, constness, representation,
1489 : any_type);
1490 : }
1491 30 : map1 = config.AddPropertyAtBranch(1, expectations1, map1);
1492 60 : for (int i = kCustomPropIndex + 1; i < kPropCount; i++) {
1493 30 : map1 = expectations1.AddDataField(map1, NONE, constness, representation,
1494 : any_type);
1495 : }
1496 30 : CHECK(!map1->is_deprecated());
1497 30 : CHECK(map1->is_stable());
1498 30 : CHECK(expectations1.Check(*map1));
1499 :
1500 :
1501 : // Create another branch in transition tree (property at index |kSplitProp|
1502 : // has different attributes), initialize expectations.
1503 : Handle<Map> map2 = map;
1504 30 : Expectations expectations2 = expectations;
1505 30 : map2 = expectations2.AddDataField(map2, READ_ONLY, constness, representation,
1506 : any_type);
1507 90 : for (int i = kSplitProp + 1; i < kCustomPropIndex; i++) {
1508 60 : map2 = expectations2.AddDataField(map2, NONE, constness, representation,
1509 : any_type);
1510 : }
1511 30 : map2 = config.AddPropertyAtBranch(2, expectations2, map2);
1512 60 : for (int i = kCustomPropIndex + 1; i < kPropCount; i++) {
1513 30 : map2 = expectations2.AddDataField(map2, NONE, constness, representation,
1514 : any_type);
1515 : }
1516 30 : CHECK(!map2->is_deprecated());
1517 30 : CHECK(map2->is_stable());
1518 30 : CHECK(expectations2.Check(*map2));
1519 :
1520 :
1521 : // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which
1522 : // should generalize representations in |map1|.
1523 : Handle<Map> new_map =
1524 30 : Map::ReconfigureExistingProperty(isolate, map2, kSplitProp, kData, NONE);
1525 :
1526 : // |map2| should be left unchanged but marked unstable.
1527 30 : CHECK(!map2->is_stable());
1528 30 : CHECK(!map2->is_deprecated());
1529 90 : CHECK_NE(*map2, *new_map);
1530 30 : CHECK(expectations2.Check(*map2));
1531 :
1532 : config.UpdateExpectations(kCustomPropIndex, expectations1);
1533 30 : checker.Check(isolate, map1, new_map, expectations1);
1534 30 : }
1535 :
1536 :
1537 28342 : TEST(ReconfigureDataFieldAttribute_SameDataConstantAfterTargetMap) {
1538 5 : CcTest::InitializeVM();
1539 5 : v8::HandleScope scope(CcTest::isolate());
1540 :
1541 : struct TestConfig {
1542 : Handle<JSFunction> js_func_;
1543 5 : TestConfig() {
1544 : Isolate* isolate = CcTest::i_isolate();
1545 : Factory* factory = isolate->factory();
1546 5 : js_func_ = factory->NewFunctionForTest(factory->empty_string());
1547 5 : }
1548 :
1549 10 : Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1550 : Handle<Map> map) {
1551 10 : CHECK(branch_id == 1 || branch_id == 2);
1552 : // Add the same data constant property at both transition tree branches.
1553 10 : return expectations.AddDataConstant(map, NONE, js_func_);
1554 : }
1555 :
1556 : void UpdateExpectations(int property_index, Expectations& expectations) {
1557 : // Expectations stay the same.
1558 : }
1559 : };
1560 :
1561 5 : TestConfig config;
1562 : // Two branches are "compatible" so the |map1| should NOT be deprecated.
1563 : CheckSameMap checker;
1564 5 : TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1565 5 : }
1566 :
1567 :
1568 28342 : TEST(ReconfigureDataFieldAttribute_DataConstantToDataFieldAfterTargetMap) {
1569 5 : CcTest::InitializeVM();
1570 5 : v8::HandleScope scope(CcTest::isolate());
1571 :
1572 : struct TestConfig {
1573 : Handle<JSFunction> js_func1_;
1574 : Handle<JSFunction> js_func2_;
1575 : Handle<FieldType> function_type_;
1576 5 : TestConfig() {
1577 : Isolate* isolate = CcTest::i_isolate();
1578 : Factory* factory = isolate->factory();
1579 : Handle<String> name = factory->empty_string();
1580 : Handle<Map> sloppy_map =
1581 5 : Map::CopyInitialMap(isolate, isolate->sloppy_function_map());
1582 : Handle<SharedFunctionInfo> info =
1583 5 : factory->NewSharedFunctionInfoForBuiltin(name, Builtins::kIllegal);
1584 5 : function_type_ = FieldType::Class(sloppy_map, isolate);
1585 5 : CHECK(sloppy_map->is_stable());
1586 :
1587 : js_func1_ =
1588 10 : factory->NewFunction(sloppy_map, info, isolate->native_context());
1589 :
1590 : js_func2_ =
1591 10 : factory->NewFunction(sloppy_map, info, isolate->native_context());
1592 5 : }
1593 :
1594 10 : Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1595 : Handle<Map> map) {
1596 10 : CHECK(branch_id == 1 || branch_id == 2);
1597 10 : Handle<JSFunction> js_func = branch_id == 1 ? js_func1_ : js_func2_;
1598 10 : return expectations.AddDataConstant(map, NONE, js_func);
1599 : }
1600 :
1601 : void UpdateExpectations(int property_index, Expectations& expectations) {
1602 : PropertyConstness expected_constness = FLAG_track_constant_fields
1603 : ? PropertyConstness::kConst
1604 : : PropertyConstness::kMutable;
1605 : expectations.SetDataField(property_index, expected_constness,
1606 5 : Representation::HeapObject(), function_type_);
1607 : }
1608 : };
1609 :
1610 5 : TestConfig config;
1611 : if (FLAG_track_constant_fields) {
1612 : CheckSameMap checker;
1613 : TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1614 :
1615 : } else {
1616 : // Two branches are "incompatible" so the |map1| should be deprecated.
1617 : CheckDeprecated checker;
1618 5 : TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1619 5 : }
1620 5 : }
1621 :
1622 :
1623 28342 : TEST(ReconfigureDataFieldAttribute_DataConstantToAccConstantAfterTargetMap) {
1624 5 : CcTest::InitializeVM();
1625 5 : v8::HandleScope scope(CcTest::isolate());
1626 :
1627 : struct TestConfig {
1628 : Handle<JSFunction> js_func_;
1629 : Handle<AccessorPair> pair_;
1630 5 : TestConfig() {
1631 : Isolate* isolate = CcTest::i_isolate();
1632 : Factory* factory = isolate->factory();
1633 5 : js_func_ = factory->NewFunctionForTest(factory->empty_string());
1634 5 : pair_ = CreateAccessorPair(true, true);
1635 5 : }
1636 :
1637 10 : Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1638 : Handle<Map> map) {
1639 10 : CHECK(branch_id == 1 || branch_id == 2);
1640 10 : if (branch_id == 1) {
1641 5 : return expectations.AddDataConstant(map, NONE, js_func_);
1642 : } else {
1643 5 : return expectations.AddAccessorConstant(map, NONE, pair_);
1644 : }
1645 : }
1646 :
1647 : void UpdateExpectations(int property_index, Expectations& expectations) {}
1648 : };
1649 :
1650 5 : TestConfig config;
1651 : // These are completely separate branches in transition tree.
1652 : CheckUnrelated checker;
1653 5 : TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1654 5 : }
1655 :
1656 :
1657 28342 : TEST(ReconfigureDataFieldAttribute_SameAccessorConstantAfterTargetMap) {
1658 5 : CcTest::InitializeVM();
1659 5 : v8::HandleScope scope(CcTest::isolate());
1660 :
1661 : struct TestConfig {
1662 : Handle<AccessorPair> pair_;
1663 5 : TestConfig() { pair_ = CreateAccessorPair(true, true); }
1664 :
1665 10 : Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1666 : Handle<Map> map) {
1667 10 : CHECK(branch_id == 1 || branch_id == 2);
1668 : // Add the same accessor constant property at both transition tree
1669 : // branches.
1670 10 : return expectations.AddAccessorConstant(map, NONE, pair_);
1671 : }
1672 :
1673 : void UpdateExpectations(int property_index, Expectations& expectations) {
1674 : // Two branches are "compatible" so the |map1| should NOT be deprecated.
1675 : }
1676 : };
1677 :
1678 : TestConfig config;
1679 : CheckSameMap checker;
1680 5 : TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1681 5 : }
1682 :
1683 :
1684 28342 : TEST(ReconfigureDataFieldAttribute_AccConstantToAccFieldAfterTargetMap) {
1685 5 : CcTest::InitializeVM();
1686 5 : v8::HandleScope scope(CcTest::isolate());
1687 :
1688 : struct TestConfig {
1689 : Handle<AccessorPair> pair1_;
1690 : Handle<AccessorPair> pair2_;
1691 5 : TestConfig() {
1692 5 : pair1_ = CreateAccessorPair(true, true);
1693 5 : pair2_ = CreateAccessorPair(true, true);
1694 5 : }
1695 :
1696 10 : Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1697 : Handle<Map> map) {
1698 10 : CHECK(branch_id == 1 || branch_id == 2);
1699 10 : Handle<AccessorPair> pair = branch_id == 1 ? pair1_ : pair2_;
1700 10 : return expectations.AddAccessorConstant(map, NONE, pair);
1701 : }
1702 :
1703 : void UpdateExpectations(int property_index, Expectations& expectations) {
1704 : if (IS_ACCESSOR_FIELD_SUPPORTED) {
1705 : expectations.SetAccessorField(property_index);
1706 : } else {
1707 : // Currently we have a copy-generalize-all-representations case and
1708 : // ACCESSOR property becomes ACCESSOR_CONSTANT.
1709 5 : expectations.SetAccessorConstant(property_index, pair2_);
1710 : }
1711 : }
1712 : };
1713 :
1714 5 : TestConfig config;
1715 : if (IS_ACCESSOR_FIELD_SUPPORTED) {
1716 : CheckCopyGeneralizeAllFields checker;
1717 : TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1718 : } else {
1719 : // Currently we have a copy-generalize-all-representations case.
1720 : CheckCopyGeneralizeAllFields checker;
1721 5 : TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1722 5 : }
1723 5 : }
1724 :
1725 :
1726 28342 : TEST(ReconfigureDataFieldAttribute_AccConstantToDataFieldAfterTargetMap) {
1727 5 : CcTest::InitializeVM();
1728 5 : v8::HandleScope scope(CcTest::isolate());
1729 :
1730 : struct TestConfig {
1731 : Handle<AccessorPair> pair_;
1732 5 : TestConfig() { pair_ = CreateAccessorPair(true, true); }
1733 :
1734 10 : Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1735 : Handle<Map> map) {
1736 10 : CHECK(branch_id == 1 || branch_id == 2);
1737 10 : if (branch_id == 1) {
1738 5 : return expectations.AddAccessorConstant(map, NONE, pair_);
1739 : } else {
1740 : Isolate* isolate = CcTest::i_isolate();
1741 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1742 : return expectations.AddDataField(map, NONE, kDefaultFieldConstness,
1743 5 : Representation::Smi(), any_type);
1744 : }
1745 : }
1746 :
1747 : void UpdateExpectations(int property_index, Expectations& expectations) {}
1748 : };
1749 :
1750 : TestConfig config;
1751 : // These are completely separate branches in transition tree.
1752 : CheckUnrelated checker;
1753 5 : TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1754 5 : }
1755 :
1756 :
1757 : ////////////////////////////////////////////////////////////////////////////////
1758 : // A set of tests for elements kind reconfiguration case.
1759 : //
1760 :
1761 : // This test ensures that field generalization is correctly propagated from one
1762 : // branch of transition tree (|map2) to another (|map|).
1763 : //
1764 : // + - p0 - p1 - p2A - p3 - p4: |map|
1765 : // |
1766 : // ek
1767 : // |
1768 : // {} - p0 - p1 - p2B - p3 - p4: |map2|
1769 : //
1770 : // where "p2A" and "p2B" differ only in the representation/field type.
1771 : //
1772 20 : static void TestReconfigureElementsKind_GeneralizeField(
1773 : const CRFTData& from, const CRFTData& to, const CRFTData& expected) {
1774 20 : Isolate* isolate = CcTest::i_isolate();
1775 :
1776 20 : Expectations expectations(isolate, PACKED_SMI_ELEMENTS);
1777 :
1778 : // Create a map, add required properties to it and initialize expectations.
1779 20 : Handle<Map> initial_map = Map::Create(isolate, 0);
1780 : initial_map->set_instance_type(JS_ARRAY_TYPE);
1781 20 : initial_map->set_elements_kind(PACKED_SMI_ELEMENTS);
1782 :
1783 : Handle<Map> map = initial_map;
1784 20 : map = expectations.AsElementsKind(map, PACKED_ELEMENTS);
1785 160 : for (int i = 0; i < kPropCount; i++) {
1786 : map = expectations.AddDataField(map, NONE, from.constness,
1787 140 : from.representation, from.type);
1788 : }
1789 20 : CHECK(!map->is_deprecated());
1790 20 : CHECK(map->is_stable());
1791 20 : CHECK(expectations.Check(*map));
1792 :
1793 : // Create another branch in transition tree (property at index |kDiffProp|
1794 : // has different representatio/field type), initialize expectations.
1795 : const int kDiffProp = kPropCount / 2;
1796 20 : Expectations expectations2(isolate, PACKED_SMI_ELEMENTS);
1797 :
1798 : Handle<Map> map2 = initial_map;
1799 160 : for (int i = 0; i < kPropCount; i++) {
1800 140 : if (i == kDiffProp) {
1801 : map2 = expectations2.AddDataField(map2, NONE, to.constness,
1802 20 : to.representation, to.type);
1803 : } else {
1804 : map2 = expectations2.AddDataField(map2, NONE, from.constness,
1805 120 : from.representation, from.type);
1806 : }
1807 : }
1808 20 : CHECK(!map2->is_deprecated());
1809 20 : CHECK(map2->is_stable());
1810 20 : CHECK(expectations2.Check(*map2));
1811 :
1812 20 : Zone zone(isolate->allocator(), ZONE_NAME);
1813 40 : CanonicalHandleScope canonical(isolate);
1814 20 : JSHeapBroker broker(isolate, &zone);
1815 20 : CompilationDependencies dependencies(isolate, &zone);
1816 : MapRef map_ref(&broker, map);
1817 20 : map_ref.SerializeOwnDescriptors();
1818 20 : dependencies.DependOnFieldType(map_ref, kDiffProp);
1819 :
1820 : // Reconfigure elements kinds of |map2|, which should generalize
1821 : // representations in |map|.
1822 : Handle<Map> new_map =
1823 20 : Map::ReconfigureElementsKind(isolate, map2, PACKED_ELEMENTS);
1824 :
1825 : // |map2| should be left unchanged but marked unstable.
1826 20 : CHECK(!map2->is_stable());
1827 20 : CHECK(!map2->is_deprecated());
1828 60 : CHECK_NE(*map2, *new_map);
1829 20 : CHECK(expectations2.Check(*map2));
1830 :
1831 : // |map| should be deprecated and |new_map| should match new expectations.
1832 : expectations.SetDataField(kDiffProp, expected.constness,
1833 20 : expected.representation, expected.type);
1834 :
1835 20 : CHECK(map->is_deprecated());
1836 20 : CHECK(dependencies.AreValid());
1837 60 : CHECK_NE(*map, *new_map);
1838 :
1839 20 : CHECK(!new_map->is_deprecated());
1840 20 : CHECK(expectations.Check(*new_map));
1841 :
1842 : // Update deprecated |map|, it should become |new_map|.
1843 20 : Handle<Map> updated_map = Map::Update(isolate, map);
1844 60 : CHECK_EQ(*new_map, *updated_map);
1845 20 : CheckMigrationTarget(isolate, *map, *updated_map);
1846 :
1847 : // Ensure Map::FindElementsKindTransitionedMap() is able to find the
1848 : // transitioned map.
1849 : {
1850 : MapHandles map_list;
1851 20 : map_list.push_back(updated_map);
1852 : Map transitioned_map =
1853 20 : map2->FindElementsKindTransitionedMap(isolate, map_list);
1854 40 : CHECK_EQ(*updated_map, transitioned_map);
1855 20 : }
1856 20 : }
1857 :
1858 : // This test ensures that trivial field generalization (from HeapObject to
1859 : // HeapObject) is correctly propagated from one branch of transition tree
1860 : // (|map2|) to another (|map|).
1861 : //
1862 : // + - p0 - p1 - p2A - p3 - p4: |map|
1863 : // |
1864 : // ek
1865 : // |
1866 : // {} - p0 - p1 - p2B - p3 - p4: |map2|
1867 : //
1868 : // where "p2A" and "p2B" differ only in the representation/field type.
1869 : //
1870 10 : static void TestReconfigureElementsKind_GeneralizeFieldTrivial(
1871 : const CRFTData& from, const CRFTData& to, const CRFTData& expected) {
1872 10 : Isolate* isolate = CcTest::i_isolate();
1873 :
1874 10 : Expectations expectations(isolate, PACKED_SMI_ELEMENTS);
1875 :
1876 : // Create a map, add required properties to it and initialize expectations.
1877 10 : Handle<Map> initial_map = Map::Create(isolate, 0);
1878 : initial_map->set_instance_type(JS_ARRAY_TYPE);
1879 10 : initial_map->set_elements_kind(PACKED_SMI_ELEMENTS);
1880 :
1881 : Handle<Map> map = initial_map;
1882 10 : map = expectations.AsElementsKind(map, PACKED_ELEMENTS);
1883 80 : for (int i = 0; i < kPropCount; i++) {
1884 : map = expectations.AddDataField(map, NONE, from.constness,
1885 70 : from.representation, from.type);
1886 : }
1887 10 : CHECK(!map->is_deprecated());
1888 10 : CHECK(map->is_stable());
1889 10 : CHECK(expectations.Check(*map));
1890 :
1891 : // Create another branch in transition tree (property at index |kDiffProp|
1892 : // has different attributes), initialize expectations.
1893 : const int kDiffProp = kPropCount / 2;
1894 10 : Expectations expectations2(isolate, PACKED_SMI_ELEMENTS);
1895 :
1896 : Handle<Map> map2 = initial_map;
1897 80 : for (int i = 0; i < kPropCount; i++) {
1898 70 : if (i == kDiffProp) {
1899 : map2 = expectations2.AddDataField(map2, NONE, to.constness,
1900 10 : to.representation, to.type);
1901 : } else {
1902 : map2 = expectations2.AddDataField(map2, NONE, from.constness,
1903 60 : from.representation, from.type);
1904 : }
1905 : }
1906 10 : CHECK(!map2->is_deprecated());
1907 10 : CHECK(map2->is_stable());
1908 10 : CHECK(expectations2.Check(*map2));
1909 :
1910 10 : Zone zone(isolate->allocator(), ZONE_NAME);
1911 20 : CanonicalHandleScope canonical(isolate);
1912 10 : JSHeapBroker broker(isolate, &zone);
1913 10 : CompilationDependencies dependencies(isolate, &zone);
1914 : MapRef map_ref(&broker, map);
1915 10 : map_ref.SerializeOwnDescriptors();
1916 10 : dependencies.DependOnFieldType(map_ref, kDiffProp);
1917 :
1918 : // Reconfigure elements kinds of |map2|, which should generalize
1919 : // representations in |map|.
1920 : Handle<Map> new_map =
1921 10 : Map::ReconfigureElementsKind(isolate, map2, PACKED_ELEMENTS);
1922 :
1923 : // |map2| should be left unchanged but marked unstable.
1924 10 : CHECK(!map2->is_stable());
1925 10 : CHECK(!map2->is_deprecated());
1926 30 : CHECK_NE(*map2, *new_map);
1927 10 : CHECK(expectations2.Check(*map2));
1928 :
1929 : // In trivial case |map| should be returned as a result of the elements
1930 : // kind reconfiguration, respective field types should be generalized and
1931 : // respective code dependencies should be invalidated. |map| should be NOT
1932 : // deprecated and it should match new expectations.
1933 : expectations.SetDataField(kDiffProp, expected.constness,
1934 10 : expected.representation, expected.type);
1935 10 : CHECK(!map->is_deprecated());
1936 30 : CHECK_EQ(*map, *new_map);
1937 10 : CHECK(dependencies.AreValid());
1938 :
1939 10 : CHECK(!new_map->is_deprecated());
1940 10 : CHECK(expectations.Check(*new_map));
1941 :
1942 10 : Handle<Map> updated_map = Map::Update(isolate, map);
1943 30 : CHECK_EQ(*new_map, *updated_map);
1944 :
1945 : // Ensure Map::FindElementsKindTransitionedMap() is able to find the
1946 : // transitioned map.
1947 : {
1948 : MapHandles map_list;
1949 10 : map_list.push_back(updated_map);
1950 : Map transitioned_map =
1951 10 : map2->FindElementsKindTransitionedMap(isolate, map_list);
1952 20 : CHECK_EQ(*updated_map, transitioned_map);
1953 10 : }
1954 10 : }
1955 :
1956 28342 : TEST(ReconfigureElementsKind_GeneralizeSmiFieldToDouble) {
1957 5 : CcTest::InitializeVM();
1958 5 : v8::HandleScope scope(CcTest::isolate());
1959 : Isolate* isolate = CcTest::i_isolate();
1960 :
1961 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1962 :
1963 : if (FLAG_track_constant_fields) {
1964 : TestReconfigureElementsKind_GeneralizeField(
1965 : {PropertyConstness::kConst, Representation::Smi(), any_type},
1966 : {PropertyConstness::kConst, Representation::Double(), any_type},
1967 : {PropertyConstness::kConst, Representation::Double(), any_type});
1968 :
1969 : TestReconfigureElementsKind_GeneralizeField(
1970 : {PropertyConstness::kConst, Representation::Smi(), any_type},
1971 : {PropertyConstness::kMutable, Representation::Double(), any_type},
1972 : {PropertyConstness::kMutable, Representation::Double(), any_type});
1973 :
1974 : TestReconfigureElementsKind_GeneralizeField(
1975 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
1976 : {PropertyConstness::kConst, Representation::Double(), any_type},
1977 : {PropertyConstness::kMutable, Representation::Double(), any_type});
1978 : }
1979 : TestReconfigureElementsKind_GeneralizeField(
1980 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
1981 : {PropertyConstness::kMutable, Representation::Double(), any_type},
1982 5 : {PropertyConstness::kMutable, Representation::Double(), any_type});
1983 5 : }
1984 :
1985 28342 : TEST(ReconfigureElementsKind_GeneralizeSmiFieldToTagged) {
1986 5 : CcTest::InitializeVM();
1987 5 : v8::HandleScope scope(CcTest::isolate());
1988 : Isolate* isolate = CcTest::i_isolate();
1989 :
1990 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1991 : Handle<FieldType> value_type =
1992 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
1993 :
1994 : if (FLAG_track_constant_fields) {
1995 : TestReconfigureElementsKind_GeneralizeField(
1996 : {PropertyConstness::kConst, Representation::Smi(), any_type},
1997 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
1998 : {PropertyConstness::kConst, Representation::Tagged(), any_type});
1999 :
2000 : TestReconfigureElementsKind_GeneralizeField(
2001 : {PropertyConstness::kConst, Representation::Smi(), any_type},
2002 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2003 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2004 :
2005 : TestReconfigureElementsKind_GeneralizeField(
2006 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
2007 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
2008 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2009 : }
2010 : TestReconfigureElementsKind_GeneralizeField(
2011 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
2012 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2013 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2014 5 : }
2015 :
2016 28342 : TEST(ReconfigureElementsKind_GeneralizeDoubleFieldToTagged) {
2017 5 : CcTest::InitializeVM();
2018 5 : v8::HandleScope scope(CcTest::isolate());
2019 : Isolate* isolate = CcTest::i_isolate();
2020 :
2021 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2022 : Handle<FieldType> value_type =
2023 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
2024 :
2025 : if (FLAG_track_constant_fields) {
2026 : TestReconfigureElementsKind_GeneralizeField(
2027 : {PropertyConstness::kConst, Representation::Double(), any_type},
2028 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
2029 : {PropertyConstness::kConst, Representation::Tagged(), any_type});
2030 :
2031 : TestReconfigureElementsKind_GeneralizeField(
2032 : {PropertyConstness::kConst, Representation::Double(), any_type},
2033 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2034 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2035 :
2036 : TestReconfigureElementsKind_GeneralizeField(
2037 : {PropertyConstness::kMutable, Representation::Double(), any_type},
2038 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
2039 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2040 : }
2041 : TestReconfigureElementsKind_GeneralizeField(
2042 : {PropertyConstness::kMutable, Representation::Double(), any_type},
2043 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2044 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2045 5 : }
2046 :
2047 28342 : TEST(ReconfigureElementsKind_GeneralizeHeapObjFieldToHeapObj) {
2048 5 : CcTest::InitializeVM();
2049 5 : v8::HandleScope scope(CcTest::isolate());
2050 : Isolate* isolate = CcTest::i_isolate();
2051 :
2052 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2053 :
2054 : Handle<FieldType> current_type =
2055 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
2056 :
2057 : Handle<FieldType> new_type =
2058 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
2059 :
2060 5 : Handle<FieldType> expected_type = any_type;
2061 :
2062 : // Check generalizations that trigger deopts.
2063 : if (FLAG_track_constant_fields) {
2064 : TestReconfigureElementsKind_GeneralizeFieldTrivial(
2065 : {PropertyConstness::kConst, Representation::HeapObject(), current_type},
2066 : {PropertyConstness::kConst, Representation::HeapObject(), new_type},
2067 : {PropertyConstness::kConst, Representation::HeapObject(),
2068 : expected_type});
2069 : if (FLAG_modify_map_inplace) {
2070 : // PropertyConstness::kConst to PropertyConstness::kMutable migration does
2071 : // not create a new map, therefore trivial generalization.
2072 : TestReconfigureElementsKind_GeneralizeFieldTrivial(
2073 : {PropertyConstness::kConst, Representation::HeapObject(),
2074 : current_type},
2075 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
2076 : {PropertyConstness::kMutable, Representation::HeapObject(),
2077 : expected_type});
2078 : } else {
2079 : // PropertyConstness::kConst to PropertyConstness::kMutable migration
2080 : // causes map change, therefore non-trivial generalization.
2081 : TestReconfigureElementsKind_GeneralizeField(
2082 : {PropertyConstness::kConst, Representation::HeapObject(),
2083 : current_type},
2084 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
2085 : {PropertyConstness::kMutable, Representation::HeapObject(),
2086 : expected_type});
2087 : }
2088 : TestReconfigureElementsKind_GeneralizeFieldTrivial(
2089 : {PropertyConstness::kMutable, Representation::HeapObject(),
2090 : current_type},
2091 : {PropertyConstness::kConst, Representation::HeapObject(), new_type},
2092 : {PropertyConstness::kMutable, Representation::HeapObject(),
2093 : expected_type});
2094 : }
2095 : TestReconfigureElementsKind_GeneralizeFieldTrivial(
2096 : {PropertyConstness::kMutable, Representation::HeapObject(), current_type},
2097 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
2098 : {PropertyConstness::kMutable, Representation::HeapObject(),
2099 5 : expected_type});
2100 : current_type = expected_type;
2101 :
2102 : // Check generalizations that do not trigger deopts.
2103 5 : new_type = FieldType::Class(Map::Create(isolate, 0), isolate);
2104 :
2105 : if (FLAG_track_constant_fields) {
2106 : TestReconfigureElementsKind_GeneralizeFieldTrivial(
2107 : {PropertyConstness::kConst, Representation::HeapObject(), any_type},
2108 : {PropertyConstness::kConst, Representation::HeapObject(), new_type},
2109 : {PropertyConstness::kConst, Representation::HeapObject(), any_type});
2110 :
2111 : if (FLAG_modify_map_inplace) {
2112 : // PropertyConstness::kConst to PropertyConstness::kMutable migration does
2113 : // not create a new map, therefore trivial generalization.
2114 : TestReconfigureElementsKind_GeneralizeFieldTrivial(
2115 : {PropertyConstness::kConst, Representation::HeapObject(), any_type},
2116 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
2117 : {PropertyConstness::kMutable, Representation::HeapObject(),
2118 : any_type});
2119 : } else {
2120 : // PropertyConstness::kConst to PropertyConstness::kMutable migration
2121 : // causes map change, therefore non-trivial generalization.
2122 : TestReconfigureElementsKind_GeneralizeField(
2123 : {PropertyConstness::kConst, Representation::HeapObject(), any_type},
2124 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
2125 : {PropertyConstness::kMutable, Representation::HeapObject(),
2126 : any_type});
2127 : }
2128 :
2129 : TestReconfigureElementsKind_GeneralizeFieldTrivial(
2130 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type},
2131 : {PropertyConstness::kConst, Representation::HeapObject(), new_type},
2132 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type});
2133 : }
2134 : TestReconfigureElementsKind_GeneralizeFieldTrivial(
2135 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type},
2136 : {PropertyConstness::kMutable, Representation::HeapObject(), new_type},
2137 5 : {PropertyConstness::kMutable, Representation::HeapObject(), any_type});
2138 5 : }
2139 :
2140 28342 : TEST(ReconfigureElementsKind_GeneralizeHeapObjectFieldToTagged) {
2141 5 : CcTest::InitializeVM();
2142 5 : v8::HandleScope scope(CcTest::isolate());
2143 : Isolate* isolate = CcTest::i_isolate();
2144 :
2145 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2146 : Handle<FieldType> value_type =
2147 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
2148 :
2149 : if (FLAG_track_constant_fields) {
2150 : TestReconfigureElementsKind_GeneralizeField(
2151 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
2152 : {PropertyConstness::kConst, Representation::Smi(), any_type},
2153 : {PropertyConstness::kConst, Representation::Tagged(), any_type});
2154 :
2155 : TestReconfigureElementsKind_GeneralizeField(
2156 : {PropertyConstness::kConst, Representation::HeapObject(), value_type},
2157 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
2158 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2159 :
2160 : TestReconfigureElementsKind_GeneralizeField(
2161 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2162 : {PropertyConstness::kConst, Representation::Smi(), any_type},
2163 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2164 : }
2165 : TestReconfigureElementsKind_GeneralizeField(
2166 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2167 : {PropertyConstness::kMutable, Representation::Smi(), any_type},
2168 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2169 5 : }
2170 :
2171 : ////////////////////////////////////////////////////////////////////////////////
2172 : // A set of tests checking split map deprecation.
2173 : //
2174 :
2175 28342 : TEST(ReconfigurePropertySplitMapTransitionsOverflow) {
2176 5 : CcTest::InitializeVM();
2177 5 : v8::HandleScope scope(CcTest::isolate());
2178 : Isolate* isolate = CcTest::i_isolate();
2179 :
2180 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2181 :
2182 5 : Expectations expectations(isolate);
2183 :
2184 : // Create a map, add required properties to it and initialize expectations.
2185 5 : Handle<Map> initial_map = Map::Create(isolate, 0);
2186 5 : Handle<Map> map = initial_map;
2187 40 : for (int i = 0; i < kPropCount; i++) {
2188 : map = expectations.AddDataField(map, NONE, PropertyConstness::kMutable,
2189 35 : Representation::Smi(), any_type);
2190 : }
2191 5 : CHECK(!map->is_deprecated());
2192 5 : CHECK(map->is_stable());
2193 :
2194 : // Generalize representation of property at index |kSplitProp|.
2195 : const int kSplitProp = kPropCount / 2;
2196 : Handle<Map> split_map;
2197 : Handle<Map> map2 = initial_map;
2198 : {
2199 20 : for (int i = 0; i < kSplitProp + 1; i++) {
2200 20 : if (i == kSplitProp) {
2201 : split_map = map2;
2202 : }
2203 :
2204 20 : Handle<String> name = MakeName("prop", i);
2205 : Map target = TransitionsAccessor(isolate, map2)
2206 20 : .SearchTransition(*name, kData, NONE);
2207 20 : CHECK(!target.is_null());
2208 : map2 = handle(target, isolate);
2209 : }
2210 :
2211 : map2 = Map::ReconfigureProperty(isolate, map2, kSplitProp, kData, NONE,
2212 5 : Representation::Double(), any_type);
2213 : expectations.SetDataField(kSplitProp, PropertyConstness::kMutable,
2214 5 : Representation::Double(), any_type);
2215 :
2216 5 : CHECK(expectations.Check(*split_map, kSplitProp));
2217 5 : CHECK(expectations.Check(*map2, kSplitProp + 1));
2218 : }
2219 :
2220 : // At this point |map| should be deprecated and disconnected from the
2221 : // transition tree.
2222 5 : CHECK(map->is_deprecated());
2223 5 : CHECK(!split_map->is_deprecated());
2224 5 : CHECK(map2->is_stable());
2225 5 : CHECK(!map2->is_deprecated());
2226 :
2227 : // Fill in transition tree of |map2| so that it can't have more transitions.
2228 7680 : for (int i = 0; i < TransitionsAccessor::kMaxNumberOfTransitions; i++) {
2229 7680 : CHECK(TransitionsAccessor(isolate, map2).CanHaveMoreTransitions());
2230 7680 : Handle<String> name = MakeName("foo", i);
2231 : Map::CopyWithField(isolate, map2, name, any_type, NONE,
2232 : PropertyConstness::kMutable, Representation::Smi(),
2233 7680 : INSERT_TRANSITION)
2234 15360 : .ToHandleChecked();
2235 : }
2236 5 : CHECK(!TransitionsAccessor(isolate, map2).CanHaveMoreTransitions());
2237 :
2238 : // Try to update |map|, since there is no place for propX transition at |map2|
2239 : // |map| should become "copy-generalized".
2240 5 : Handle<Map> updated_map = Map::Update(isolate, map);
2241 10 : CHECK(updated_map->GetBackPointer()->IsUndefined(isolate));
2242 :
2243 35 : for (int i = 0; i < kPropCount; i++) {
2244 : expectations.SetDataField(i, PropertyConstness::kMutable,
2245 35 : Representation::Tagged(), any_type);
2246 : }
2247 5 : CHECK(expectations.Check(*updated_map));
2248 5 : }
2249 :
2250 :
2251 : ////////////////////////////////////////////////////////////////////////////////
2252 : // A set of tests involving special transitions (such as elements kind
2253 : // transition, observed transition or prototype transition).
2254 : //
2255 :
2256 : // This test ensures that field generalization is correctly propagated from one
2257 : // branch of transition tree (|map2|) to another (|map|).
2258 : //
2259 : // p4B: |map2|
2260 : // |
2261 : // * - special transition
2262 : // |
2263 : // {} - p0 - p1 - p2A - p3 - p4A: |map|
2264 : //
2265 : // where "p4A" and "p4B" are exactly the same properties.
2266 : //
2267 : // TODO(ishell): unify this test template with
2268 : // TestReconfigureDataFieldAttribute_GeneralizeField once
2269 : // IS_PROTO_TRANS_ISSUE_FIXED and IS_NON_EQUIVALENT_TRANSITION_SUPPORTED are
2270 : // fixed.
2271 : template <typename TestConfig>
2272 20 : static void TestGeneralizeFieldWithSpecialTransition(TestConfig& config,
2273 : const CRFTData& from,
2274 : const CRFTData& to,
2275 : const CRFTData& expected) {
2276 : Isolate* isolate = CcTest::i_isolate();
2277 :
2278 20 : Expectations expectations(isolate);
2279 :
2280 : // Create a map, add required properties to it and initialize expectations.
2281 20 : Handle<Map> initial_map = Map::Create(isolate, 0);
2282 20 : Handle<Map> map = initial_map;
2283 150 : for (int i = 0; i < kPropCount; i++) {
2284 140 : map = expectations.AddDataField(map, NONE, from.constness,
2285 140 : from.representation, from.type);
2286 : }
2287 20 : CHECK(!map->is_deprecated());
2288 20 : CHECK(map->is_stable());
2289 20 : CHECK(expectations.Check(*map));
2290 :
2291 20 : Expectations expectations2 = expectations;
2292 :
2293 : // Apply some special transition to |map|.
2294 20 : CHECK(map->owns_descriptors());
2295 20 : Handle<Map> map2 = config.Transition(map, expectations2);
2296 :
2297 : // |map| should still match expectations.
2298 20 : CHECK(!map->is_deprecated());
2299 20 : CHECK(expectations.Check(*map));
2300 :
2301 : if (config.generalizes_representations()) {
2302 70 : for (int i = 0; i < kPropCount; i++) {
2303 70 : expectations2.GeneralizeField(i);
2304 : }
2305 : }
2306 :
2307 20 : CHECK(!map2->is_deprecated());
2308 20 : CHECK(map2->is_stable());
2309 20 : CHECK(expectations2.Check(*map2));
2310 :
2311 : // Create new maps by generalizing representation of propX field.
2312 140 : Handle<Map> maps[kPropCount];
2313 140 : for (int i = 0; i < kPropCount; i++) {
2314 : Handle<Map> new_map = Map::ReconfigureProperty(isolate, map, i, kData, NONE,
2315 140 : to.representation, to.type);
2316 140 : maps[i] = new_map;
2317 :
2318 140 : expectations.SetDataField(i, expected.constness, expected.representation,
2319 140 : expected.type);
2320 :
2321 140 : CHECK(map->is_deprecated());
2322 420 : CHECK_NE(*map, *new_map);
2323 260 : CHECK(i == 0 || maps[i - 1]->is_deprecated());
2324 140 : CHECK(expectations.Check(*new_map));
2325 :
2326 140 : Handle<Map> new_map2 = Map::Update(isolate, map2);
2327 140 : CHECK(!new_map2->is_deprecated());
2328 140 : CHECK(!new_map2->is_dictionary_map());
2329 :
2330 : Handle<Map> tmp_map;
2331 280 : if (Map::TryUpdate(isolate, map2).ToHandle(&tmp_map)) {
2332 : // If Map::TryUpdate() manages to succeed the result must match the result
2333 : // of Map::Update().
2334 210 : CHECK_EQ(*new_map2, *tmp_map);
2335 : }
2336 :
2337 : if (config.is_non_equevalent_transition()) {
2338 : // In case of non-equivalent transition currently we generalize all
2339 : // representations.
2340 980 : for (int i = 0; i < kPropCount; i++) {
2341 980 : expectations2.GeneralizeField(i);
2342 : }
2343 280 : CHECK(new_map2->GetBackPointer()->IsUndefined(isolate));
2344 140 : CHECK(expectations2.Check(*new_map2));
2345 : } else {
2346 : CHECK(!new_map2->GetBackPointer()->IsUndefined(isolate));
2347 : CHECK(expectations2.Check(*new_map2));
2348 : }
2349 : }
2350 :
2351 20 : Handle<Map> active_map = maps[kPropCount - 1];
2352 20 : CHECK(!active_map->is_deprecated());
2353 :
2354 : // Update all deprecated maps and check that they are now the same.
2355 20 : Handle<Map> updated_map = Map::Update(isolate, map);
2356 60 : CHECK_EQ(*active_map, *updated_map);
2357 20 : CheckMigrationTarget(isolate, *map, *updated_map);
2358 150 : for (int i = 0; i < kPropCount; i++) {
2359 140 : updated_map = Map::Update(isolate, maps[i]);
2360 420 : CHECK_EQ(*active_map, *updated_map);
2361 280 : CheckMigrationTarget(isolate, *maps[i], *updated_map);
2362 : }
2363 20 : }
2364 :
2365 :
2366 28342 : TEST(ElementsKindTransitionFromMapOwningDescriptor) {
2367 5 : CcTest::InitializeVM();
2368 5 : v8::HandleScope scope(CcTest::isolate());
2369 : Isolate* isolate = CcTest::i_isolate();
2370 :
2371 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2372 : Handle<FieldType> value_type =
2373 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
2374 :
2375 : struct TestConfig {
2376 5 : Handle<Map> Transition(Handle<Map> map, Expectations& expectations) {
2377 10 : Handle<Symbol> frozen_symbol(map->GetReadOnlyRoots().frozen_symbol(),
2378 10 : CcTest::i_isolate());
2379 : expectations.SetElementsKind(DICTIONARY_ELEMENTS);
2380 : return Map::CopyForPreventExtensions(CcTest::i_isolate(), map, NONE,
2381 : frozen_symbol,
2382 5 : "CopyForPreventExtensions");
2383 : }
2384 : // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
2385 : bool generalizes_representations() const { return false; }
2386 : bool is_non_equevalent_transition() const { return true; }
2387 : };
2388 : TestConfig config;
2389 : TestGeneralizeFieldWithSpecialTransition(
2390 : config, {PropertyConstness::kMutable, Representation::Smi(), any_type},
2391 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2392 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2393 5 : }
2394 :
2395 :
2396 28342 : TEST(ElementsKindTransitionFromMapNotOwningDescriptor) {
2397 5 : CcTest::InitializeVM();
2398 5 : v8::HandleScope scope(CcTest::isolate());
2399 : Isolate* isolate = CcTest::i_isolate();
2400 :
2401 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2402 : Handle<FieldType> value_type =
2403 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
2404 :
2405 : struct TestConfig {
2406 5 : Handle<Map> Transition(Handle<Map> map, Expectations& expectations) {
2407 : Isolate* isolate = CcTest::i_isolate();
2408 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2409 :
2410 : // Add one more transition to |map| in order to prevent descriptors
2411 : // ownership.
2412 5 : CHECK(map->owns_descriptors());
2413 : Map::CopyWithField(isolate, map, MakeString("foo"), any_type, NONE,
2414 : PropertyConstness::kMutable, Representation::Smi(),
2415 5 : INSERT_TRANSITION)
2416 10 : .ToHandleChecked();
2417 5 : CHECK(!map->owns_descriptors());
2418 :
2419 : Handle<Symbol> frozen_symbol(ReadOnlyRoots(isolate).frozen_symbol(),
2420 : isolate);
2421 : expectations.SetElementsKind(DICTIONARY_ELEMENTS);
2422 : return Map::CopyForPreventExtensions(isolate, map, NONE, frozen_symbol,
2423 5 : "CopyForPreventExtensions");
2424 : }
2425 : // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
2426 : bool generalizes_representations() const { return false; }
2427 : bool is_non_equevalent_transition() const { return true; }
2428 : };
2429 : TestConfig config;
2430 : TestGeneralizeFieldWithSpecialTransition(
2431 : config, {PropertyConstness::kMutable, Representation::Smi(), any_type},
2432 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2433 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2434 5 : }
2435 :
2436 :
2437 28342 : TEST(PrototypeTransitionFromMapOwningDescriptor) {
2438 5 : CcTest::InitializeVM();
2439 5 : v8::HandleScope scope(CcTest::isolate());
2440 : Isolate* isolate = CcTest::i_isolate();
2441 :
2442 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2443 : Handle<FieldType> value_type =
2444 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
2445 :
2446 : struct TestConfig {
2447 : Handle<JSObject> prototype_;
2448 :
2449 5 : TestConfig() {
2450 : Isolate* isolate = CcTest::i_isolate();
2451 : Factory* factory = isolate->factory();
2452 5 : prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0));
2453 5 : }
2454 :
2455 5 : Handle<Map> Transition(Handle<Map> map, Expectations& expectations) {
2456 5 : return Map::TransitionToPrototype(CcTest::i_isolate(), map, prototype_);
2457 : }
2458 : // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
2459 : bool generalizes_representations() const {
2460 : return !IS_PROTO_TRANS_ISSUE_FIXED;
2461 : }
2462 : bool is_non_equevalent_transition() const { return true; }
2463 : };
2464 5 : TestConfig config;
2465 : TestGeneralizeFieldWithSpecialTransition(
2466 : config, {PropertyConstness::kMutable, Representation::Smi(), any_type},
2467 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2468 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2469 5 : }
2470 :
2471 :
2472 28342 : TEST(PrototypeTransitionFromMapNotOwningDescriptor) {
2473 5 : CcTest::InitializeVM();
2474 5 : v8::HandleScope scope(CcTest::isolate());
2475 : Isolate* isolate = CcTest::i_isolate();
2476 :
2477 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2478 : Handle<FieldType> value_type =
2479 5 : FieldType::Class(Map::Create(isolate, 0), isolate);
2480 :
2481 : struct TestConfig {
2482 : Handle<JSObject> prototype_;
2483 :
2484 5 : TestConfig() {
2485 : Isolate* isolate = CcTest::i_isolate();
2486 : Factory* factory = isolate->factory();
2487 5 : prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0));
2488 5 : }
2489 :
2490 5 : Handle<Map> Transition(Handle<Map> map, Expectations& expectations) {
2491 : Isolate* isolate = CcTest::i_isolate();
2492 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2493 :
2494 : // Add one more transition to |map| in order to prevent descriptors
2495 : // ownership.
2496 5 : CHECK(map->owns_descriptors());
2497 : Map::CopyWithField(isolate, map, MakeString("foo"), any_type, NONE,
2498 : PropertyConstness::kMutable, Representation::Smi(),
2499 5 : INSERT_TRANSITION)
2500 10 : .ToHandleChecked();
2501 5 : CHECK(!map->owns_descriptors());
2502 :
2503 5 : return Map::TransitionToPrototype(isolate, map, prototype_);
2504 : }
2505 : // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
2506 : bool generalizes_representations() const {
2507 : return !IS_PROTO_TRANS_ISSUE_FIXED;
2508 : }
2509 : bool is_non_equevalent_transition() const { return true; }
2510 : };
2511 5 : TestConfig config;
2512 : TestGeneralizeFieldWithSpecialTransition(
2513 : config, {PropertyConstness::kMutable, Representation::Smi(), any_type},
2514 : {PropertyConstness::kMutable, Representation::HeapObject(), value_type},
2515 5 : {PropertyConstness::kMutable, Representation::Tagged(), any_type});
2516 5 : }
2517 :
2518 :
2519 : ////////////////////////////////////////////////////////////////////////////////
2520 : // A set of tests for higher level transitioning mechanics.
2521 : //
2522 :
2523 : struct TransitionToDataFieldOperator {
2524 : PropertyConstness constness_;
2525 : Representation representation_;
2526 : PropertyAttributes attributes_;
2527 : Handle<FieldType> heap_type_;
2528 : Handle<Object> value_;
2529 :
2530 : TransitionToDataFieldOperator(PropertyConstness constness,
2531 : Representation representation,
2532 : Handle<FieldType> heap_type,
2533 : Handle<Object> value,
2534 : PropertyAttributes attributes = NONE)
2535 : : constness_(constness),
2536 : representation_(representation),
2537 : attributes_(attributes),
2538 : heap_type_(heap_type),
2539 15 : value_(value) {}
2540 :
2541 : Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) {
2542 : return expectations.TransitionToDataField(
2543 15 : map, attributes_, constness_, representation_, heap_type_, value_);
2544 : }
2545 : };
2546 :
2547 :
2548 : struct TransitionToDataConstantOperator {
2549 : PropertyAttributes attributes_;
2550 : Handle<JSFunction> value_;
2551 :
2552 : TransitionToDataConstantOperator(Handle<JSFunction> value,
2553 : PropertyAttributes attributes = NONE)
2554 20 : : attributes_(attributes), value_(value) {}
2555 :
2556 : Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) {
2557 25 : return expectations.TransitionToDataConstant(map, attributes_, value_);
2558 : }
2559 : };
2560 :
2561 :
2562 : struct TransitionToAccessorConstantOperator {
2563 : PropertyAttributes attributes_;
2564 : Handle<AccessorPair> pair_;
2565 :
2566 : TransitionToAccessorConstantOperator(Handle<AccessorPair> pair,
2567 : PropertyAttributes attributes = NONE)
2568 5 : : attributes_(attributes), pair_(pair) {}
2569 :
2570 : Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) {
2571 10 : return expectations.TransitionToAccessorConstant(map, attributes_, pair_);
2572 : }
2573 : };
2574 :
2575 :
2576 : struct ReconfigureAsDataPropertyOperator {
2577 : int descriptor_;
2578 : Representation representation_;
2579 : PropertyAttributes attributes_;
2580 : Handle<FieldType> heap_type_;
2581 :
2582 : ReconfigureAsDataPropertyOperator(int descriptor,
2583 : Representation representation,
2584 : Handle<FieldType> heap_type,
2585 : PropertyAttributes attributes = NONE)
2586 : : descriptor_(descriptor),
2587 : representation_(representation),
2588 : attributes_(attributes),
2589 : heap_type_(heap_type) {}
2590 :
2591 : Handle<Map> DoTransition(Isolate* isolate, Expectations& expectations,
2592 : Handle<Map> map) {
2593 : expectations.SetDataField(descriptor_, PropertyConstness::kMutable,
2594 : representation_, heap_type_);
2595 : return Map::ReconfigureExistingProperty(isolate, map, descriptor_, kData,
2596 : attributes_);
2597 : }
2598 : };
2599 :
2600 :
2601 : struct ReconfigureAsAccessorPropertyOperator {
2602 : int descriptor_;
2603 : PropertyAttributes attributes_;
2604 :
2605 : ReconfigureAsAccessorPropertyOperator(int descriptor,
2606 : PropertyAttributes attributes = NONE)
2607 : : descriptor_(descriptor), attributes_(attributes) {}
2608 :
2609 : Handle<Map> DoTransition(Isolate* isolate, Expectations& expectations,
2610 : Handle<Map> map) {
2611 : expectations.SetAccessorField(descriptor_);
2612 : return Map::ReconfigureExistingProperty(isolate, map, descriptor_,
2613 : kAccessor, attributes_);
2614 : }
2615 : };
2616 :
2617 : // Checks that field generalization happened.
2618 : struct FieldGeneralizationChecker {
2619 : int descriptor_;
2620 : PropertyConstness constness_;
2621 : Representation representation_;
2622 : PropertyAttributes attributes_;
2623 : Handle<FieldType> heap_type_;
2624 :
2625 : FieldGeneralizationChecker(int descriptor, PropertyConstness constness,
2626 : Representation representation,
2627 : Handle<FieldType> heap_type,
2628 : PropertyAttributes attributes = NONE)
2629 : : descriptor_(descriptor),
2630 : constness_(constness),
2631 : representation_(representation),
2632 : attributes_(attributes),
2633 15 : heap_type_(heap_type) {}
2634 :
2635 15 : void Check(Isolate* isolate, Expectations& expectations2, Handle<Map> map1,
2636 : Handle<Map> map2) {
2637 15 : CHECK(!map2->is_deprecated());
2638 :
2639 15 : CHECK(map1->is_deprecated());
2640 45 : CHECK_NE(*map1, *map2);
2641 15 : Handle<Map> updated_map = Map::Update(isolate, map1);
2642 45 : CHECK_EQ(*map2, *updated_map);
2643 15 : CheckMigrationTarget(isolate, *map1, *updated_map);
2644 :
2645 : expectations2.SetDataField(descriptor_, attributes_, constness_,
2646 15 : representation_, heap_type_);
2647 15 : CHECK(expectations2.Check(*map2));
2648 15 : }
2649 : };
2650 :
2651 :
2652 : // Checks that existing transition was taken as is.
2653 : struct SameMapChecker {
2654 10 : void Check(Isolate* isolate, Expectations& expectations, Handle<Map> map1,
2655 : Handle<Map> map2) {
2656 10 : CHECK(!map2->is_deprecated());
2657 30 : CHECK_EQ(*map1, *map2);
2658 10 : CHECK(expectations.Check(*map2));
2659 10 : }
2660 : };
2661 :
2662 :
2663 : // Checks that both |map1| and |map2| should stays non-deprecated, this is
2664 : // the case when property kind is change.
2665 : struct PropertyKindReconfigurationChecker {
2666 : void Check(Expectations& expectations, Handle<Map> map1, Handle<Map> map2) {
2667 : CHECK(!map1->is_deprecated());
2668 : CHECK(!map2->is_deprecated());
2669 : CHECK_NE(*map1, *map2);
2670 : CHECK(expectations.Check(*map2));
2671 : }
2672 : };
2673 :
2674 :
2675 : // This test transitions to various property types under different
2676 : // circumstances.
2677 : // Plan:
2678 : // 1) create a |map| with p0..p3 properties.
2679 : // 2) create |map1| by adding "p4" to |map0|.
2680 : // 3) create |map2| by transition to "p4" from |map0|.
2681 : //
2682 : // + - p4B: |map2|
2683 : // |
2684 : // {} - p0 - p1 - pA - p3: |map|
2685 : // |
2686 : // + - p4A: |map1|
2687 : //
2688 : // where "p4A" and "p4B" differ only in the attributes.
2689 : //
2690 : template <typename TransitionOp1, typename TransitionOp2, typename Checker>
2691 45 : static void TestTransitionTo(TransitionOp1& transition_op1,
2692 15 : TransitionOp2& transition_op2, Checker& checker) {
2693 : Isolate* isolate = CcTest::i_isolate();
2694 25 : Handle<FieldType> any_type = FieldType::Any(isolate);
2695 :
2696 25 : Expectations expectations(isolate);
2697 :
2698 : // Create a map, add required properties to it and initialize expectations.
2699 25 : Handle<Map> initial_map = Map::Create(isolate, 0);
2700 25 : Handle<Map> map = initial_map;
2701 175 : for (int i = 0; i < kPropCount - 1; i++) {
2702 150 : map = expectations.AddDataField(map, NONE, PropertyConstness::kMutable,
2703 : Representation::Smi(), any_type);
2704 : }
2705 25 : CHECK(expectations.Check(*map));
2706 :
2707 25 : Expectations expectations1 = expectations;
2708 : Handle<Map> map1 = transition_op1.DoTransition(expectations1, map);
2709 25 : CHECK(expectations1.Check(*map1));
2710 :
2711 25 : Expectations expectations2 = expectations;
2712 25 : Handle<Map> map2 = transition_op2.DoTransition(expectations2, map);
2713 :
2714 : // Let the test customization do the check.
2715 25 : checker.Check(isolate, expectations2, map1, map2);
2716 25 : }
2717 :
2718 :
2719 28342 : TEST(TransitionDataFieldToDataField) {
2720 5 : CcTest::InitializeVM();
2721 5 : v8::HandleScope scope(CcTest::isolate());
2722 : Isolate* isolate = CcTest::i_isolate();
2723 :
2724 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2725 :
2726 : Handle<Object> value1 = handle(Smi::kZero, isolate);
2727 : TransitionToDataFieldOperator transition_op1(
2728 : PropertyConstness::kMutable, Representation::Smi(), any_type, value1);
2729 :
2730 5 : Handle<Object> value2 = isolate->factory()->NewHeapNumber(0);
2731 : TransitionToDataFieldOperator transition_op2(
2732 : PropertyConstness::kMutable, Representation::Double(), any_type, value2);
2733 :
2734 : FieldGeneralizationChecker checker(kPropCount - 1,
2735 : PropertyConstness::kMutable,
2736 : Representation::Double(), any_type);
2737 5 : TestTransitionTo(transition_op1, transition_op2, checker);
2738 5 : }
2739 :
2740 28342 : TEST(TransitionDataConstantToSameDataConstant) {
2741 5 : CcTest::InitializeVM();
2742 5 : v8::HandleScope scope(CcTest::isolate());
2743 : Isolate* isolate = CcTest::i_isolate();
2744 : Factory* factory = isolate->factory();
2745 :
2746 : Handle<JSFunction> js_func =
2747 5 : factory->NewFunctionForTest(factory->empty_string());
2748 : TransitionToDataConstantOperator transition_op(js_func);
2749 :
2750 : SameMapChecker checker;
2751 5 : TestTransitionTo(transition_op, transition_op, checker);
2752 5 : }
2753 :
2754 :
2755 28342 : TEST(TransitionDataConstantToAnotherDataConstant) {
2756 5 : CcTest::InitializeVM();
2757 5 : v8::HandleScope scope(CcTest::isolate());
2758 : Isolate* isolate = CcTest::i_isolate();
2759 : Factory* factory = isolate->factory();
2760 :
2761 : Handle<String> name = factory->empty_string();
2762 : Handle<Map> sloppy_map =
2763 5 : Map::CopyInitialMap(isolate, isolate->sloppy_function_map());
2764 : Handle<SharedFunctionInfo> info =
2765 5 : factory->NewSharedFunctionInfoForBuiltin(name, Builtins::kIllegal);
2766 5 : Handle<FieldType> function_type = FieldType::Class(sloppy_map, isolate);
2767 5 : CHECK(sloppy_map->is_stable());
2768 :
2769 : Handle<JSFunction> js_func1 =
2770 10 : factory->NewFunction(sloppy_map, info, isolate->native_context());
2771 : TransitionToDataConstantOperator transition_op1(js_func1);
2772 :
2773 : Handle<JSFunction> js_func2 =
2774 10 : factory->NewFunction(sloppy_map, info, isolate->native_context());
2775 : TransitionToDataConstantOperator transition_op2(js_func2);
2776 :
2777 : if (FLAG_track_constant_fields) {
2778 : SameMapChecker checker;
2779 : TestTransitionTo(transition_op1, transition_op2, checker);
2780 :
2781 : } else {
2782 : FieldGeneralizationChecker checker(
2783 : kPropCount - 1, PropertyConstness::kMutable,
2784 : Representation::HeapObject(), function_type);
2785 5 : TestTransitionTo(transition_op1, transition_op2, checker);
2786 5 : }
2787 5 : }
2788 :
2789 :
2790 28342 : TEST(TransitionDataConstantToDataField) {
2791 5 : CcTest::InitializeVM();
2792 5 : v8::HandleScope scope(CcTest::isolate());
2793 : Isolate* isolate = CcTest::i_isolate();
2794 : Factory* factory = isolate->factory();
2795 :
2796 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
2797 :
2798 : Handle<JSFunction> js_func1 =
2799 5 : factory->NewFunctionForTest(factory->empty_string());
2800 : TransitionToDataConstantOperator transition_op1(js_func1);
2801 :
2802 5 : Handle<Object> value2 = isolate->factory()->NewHeapNumber(0);
2803 : TransitionToDataFieldOperator transition_op2(
2804 : PropertyConstness::kMutable, Representation::Double(), any_type, value2);
2805 :
2806 : FieldGeneralizationChecker checker(kPropCount - 1,
2807 : PropertyConstness::kMutable,
2808 : Representation::Tagged(), any_type);
2809 5 : TestTransitionTo(transition_op1, transition_op2, checker);
2810 5 : }
2811 :
2812 :
2813 28342 : TEST(TransitionAccessorConstantToSameAccessorConstant) {
2814 5 : CcTest::InitializeVM();
2815 5 : v8::HandleScope scope(CcTest::isolate());
2816 :
2817 5 : Handle<AccessorPair> pair = CreateAccessorPair(true, true);
2818 : TransitionToAccessorConstantOperator transition_op(pair);
2819 :
2820 : SameMapChecker checker;
2821 5 : TestTransitionTo(transition_op, transition_op, checker);
2822 5 : }
2823 :
2824 : // TODO(ishell): add this test once IS_ACCESSOR_FIELD_SUPPORTED is supported.
2825 : // TEST(TransitionAccessorConstantToAnotherAccessorConstant)
2826 :
2827 28342 : TEST(HoleyMutableHeapNumber) {
2828 5 : CcTest::InitializeVM();
2829 5 : v8::HandleScope scope(CcTest::isolate());
2830 : Isolate* isolate = CcTest::i_isolate();
2831 :
2832 : auto mhn = isolate->factory()->NewMutableHeapNumberWithHoleNaN();
2833 5 : CHECK_EQ(kHoleNanInt64, mhn->value_as_bits());
2834 :
2835 5 : mhn = isolate->factory()->NewMutableHeapNumber(0.0);
2836 5 : CHECK_EQ(uint64_t{0}, mhn->value_as_bits());
2837 :
2838 : mhn->set_value_as_bits(kHoleNanInt64);
2839 5 : CHECK_EQ(kHoleNanInt64, mhn->value_as_bits());
2840 :
2841 : // Ensure that new storage for uninitialized value or mutable heap number
2842 : // with uninitialized sentinel (kHoleNanInt64) is a mutable heap number
2843 : // with uninitialized sentinel.
2844 : Handle<Object> obj =
2845 : Object::NewStorageFor(isolate, isolate->factory()->uninitialized_value(),
2846 5 : Representation::Double());
2847 10 : CHECK(obj->IsMutableHeapNumber());
2848 5 : CHECK_EQ(kHoleNanInt64, MutableHeapNumber::cast(*obj)->value_as_bits());
2849 :
2850 5 : obj = Object::NewStorageFor(isolate, mhn, Representation::Double());
2851 10 : CHECK(obj->IsMutableHeapNumber());
2852 5 : CHECK_EQ(kHoleNanInt64, MutableHeapNumber::cast(*obj)->value_as_bits());
2853 5 : }
2854 :
2855 : } // namespace test_field_type_tracking
2856 : } // namespace compiler
2857 : } // namespace internal
2858 85011 : } // namespace v8
|