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