1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdlib.h>
6 #include <utility>
7
8 #include "test/cctest/test-api.h"
9
10 #include "src/v8.h"
11
12 #include "src/compilation-cache.h"
13 #include "src/execution.h"
14 #include "src/factory.h"
15 #include "src/global-handles.h"
16 #include "src/ic/stub-cache.h"
17 #include "src/macro-assembler.h"
18
19 using namespace v8::internal;
20
21
22 // TODO(ishell): fix this once TransitionToPrototype stops generalizing
23 // all field representations (similar to crbug/448711 where elements kind
24 // and observed transitions caused generalization of all field representations).
25 const bool IS_PROTO_TRANS_ISSUE_FIXED = false;
26
27
28 // TODO(ishell): fix this once TransitionToAccessorProperty is able to always
29 // keep map in fast mode.
30 const bool IS_ACCESSOR_FIELD_SUPPORTED = false;
31
32
33 // Number of properties used in the tests.
34 const int kPropCount = 7;
35
36
37 //
38 // Helper functions.
39 //
40
MakeString(const char * str)41 static Handle<String> MakeString(const char* str) {
42 Isolate* isolate = CcTest::i_isolate();
43 Factory* factory = isolate->factory();
44 return factory->InternalizeUtf8String(str);
45 }
46
47
MakeName(const char * str,int suffix)48 static Handle<String> MakeName(const char* str, int suffix) {
49 EmbeddedVector<char, 128> buffer;
50 SNPrintF(buffer, "%s%d", str, suffix);
51 return MakeString(buffer.start());
52 }
53
54
CreateAccessorPair(bool with_getter,bool with_setter)55 static Handle<AccessorPair> CreateAccessorPair(bool with_getter,
56 bool with_setter) {
57 Isolate* isolate = CcTest::i_isolate();
58 Factory* factory = isolate->factory();
59 Handle<AccessorPair> pair = factory->NewAccessorPair();
60 Handle<String> empty_string = factory->empty_string();
61 if (with_getter) {
62 Handle<JSFunction> func = factory->NewFunction(empty_string);
63 pair->set_getter(*func);
64 }
65 if (with_setter) {
66 Handle<JSFunction> func = factory->NewFunction(empty_string);
67 pair->set_setter(*func);
68 }
69 return pair;
70 }
71
72
EqualDetails(DescriptorArray * descriptors,int descriptor,PropertyType type,PropertyAttributes attributes,Representation representation,int field_index=-1)73 static bool EqualDetails(DescriptorArray* descriptors, int descriptor,
74 PropertyType type, PropertyAttributes attributes,
75 Representation representation, int field_index = -1) {
76 PropertyDetails details = descriptors->GetDetails(descriptor);
77 if (details.type() != type) return false;
78 if (details.attributes() != attributes) return false;
79 if (!details.representation().Equals(representation)) return false;
80 if (field_index >= 0 && details.field_index() != field_index) return false;
81 return true;
82 }
83
84
85 class Expectations {
86 static const int MAX_PROPERTIES = 10;
87 Isolate* isolate_;
88 PropertyType types_[MAX_PROPERTIES];
89 PropertyAttributes attributes_[MAX_PROPERTIES];
90 Representation representations_[MAX_PROPERTIES];
91 // HeapType for kField, value for DATA_CONSTANT and getter for
92 // ACCESSOR_CONSTANT.
93 Handle<Object> values_[MAX_PROPERTIES];
94 // Setter for ACCESSOR_CONSTANT.
95 Handle<Object> setter_values_[MAX_PROPERTIES];
96 int number_of_properties_;
97
98 public:
Expectations(Isolate * isolate)99 explicit Expectations(Isolate* isolate)
100 : isolate_(isolate), number_of_properties_(0) {}
101
Init(int index,PropertyType type,PropertyAttributes attributes,Representation representation,Handle<Object> value)102 void Init(int index, PropertyType type, PropertyAttributes attributes,
103 Representation representation, Handle<Object> value) {
104 CHECK(index < MAX_PROPERTIES);
105 types_[index] = type;
106 attributes_[index] = attributes;
107 representations_[index] = representation;
108 values_[index] = value;
109 }
110
Print() const111 void Print() const {
112 OFStream os(stdout);
113 os << "Expectations: #" << number_of_properties_ << "\n";
114 for (int i = 0; i < number_of_properties_; i++) {
115 os << " " << i << ": ";
116 os << "Descriptor @ ";
117 if (types_[i] == ACCESSOR_CONSTANT) {
118 os << "(get: " << Brief(*values_[i])
119 << ", set: " << Brief(*setter_values_[i]) << ") ";
120 } else {
121 os << Brief(*values_[i]);
122 }
123 os << " (";
124 switch (types_[i]) {
125 case DATA_CONSTANT:
126 os << "immutable ";
127 // Fall through.
128 case DATA:
129 os << "data";
130 break;
131
132 case ACCESSOR_CONSTANT:
133 os << "immutable ";
134 // Fall through.
135 case ACCESSOR:
136 os << "accessor";
137 break;
138 }
139 os << ": " << representations_[i].Mnemonic();
140 os << ", attrs: " << attributes_[i] << ")\n";
141 }
142 os << "\n";
143 }
144
GetFieldType(int index)145 Handle<HeapType> GetFieldType(int index) {
146 CHECK(index < MAX_PROPERTIES);
147 CHECK(types_[index] == DATA || types_[index] == ACCESSOR);
148 return Handle<HeapType>::cast(values_[index]);
149 }
150
SetDataField(int index,PropertyAttributes attrs,Representation representation,Handle<HeapType> value)151 void SetDataField(int index, PropertyAttributes attrs,
152 Representation representation, Handle<HeapType> value) {
153 Init(index, DATA, attrs, representation, value);
154 }
155
SetDataField(int index,Representation representation,Handle<HeapType> value)156 void SetDataField(int index, Representation representation,
157 Handle<HeapType> value) {
158 SetDataField(index, attributes_[index], representation, value);
159 }
160
SetAccessorField(int index,PropertyAttributes attrs)161 void SetAccessorField(int index, PropertyAttributes attrs) {
162 Init(index, ACCESSOR, attrs, Representation::Tagged(),
163 HeapType::Any(isolate_));
164 }
165
SetAccessorField(int index)166 void SetAccessorField(int index) {
167 SetAccessorField(index, attributes_[index]);
168 }
169
SetDataConstant(int index,PropertyAttributes attrs,Handle<JSFunction> value)170 void SetDataConstant(int index, PropertyAttributes attrs,
171 Handle<JSFunction> value) {
172 Init(index, DATA_CONSTANT, attrs, Representation::HeapObject(), value);
173 }
174
SetDataConstant(int index,Handle<JSFunction> value)175 void SetDataConstant(int index, Handle<JSFunction> value) {
176 SetDataConstant(index, attributes_[index], value);
177 }
178
SetAccessorConstant(int index,PropertyAttributes attrs,Handle<Object> getter,Handle<Object> setter)179 void SetAccessorConstant(int index, PropertyAttributes attrs,
180 Handle<Object> getter, Handle<Object> setter) {
181 Init(index, ACCESSOR_CONSTANT, attrs, Representation::Tagged(), getter);
182 setter_values_[index] = setter;
183 }
184
SetAccessorConstantComponent(int index,PropertyAttributes attrs,AccessorComponent component,Handle<Object> accessor)185 void SetAccessorConstantComponent(int index, PropertyAttributes attrs,
186 AccessorComponent component,
187 Handle<Object> accessor) {
188 CHECK_EQ(ACCESSOR_CONSTANT, types_[index]);
189 CHECK(index < number_of_properties_);
190 if (component == ACCESSOR_GETTER) {
191 values_[index] = accessor;
192 } else {
193 setter_values_[index] = accessor;
194 }
195 }
196
SetAccessorConstant(int index,PropertyAttributes attrs,Handle<AccessorPair> pair)197 void SetAccessorConstant(int index, PropertyAttributes attrs,
198 Handle<AccessorPair> pair) {
199 Handle<Object> getter = handle(pair->getter(), isolate_);
200 Handle<Object> setter = handle(pair->setter(), isolate_);
201 SetAccessorConstant(index, attrs, getter, setter);
202 }
203
SetAccessorConstant(int index,Handle<Object> getter,Handle<Object> setter)204 void SetAccessorConstant(int index, Handle<Object> getter,
205 Handle<Object> setter) {
206 SetAccessorConstant(index, attributes_[index], getter, setter);
207 }
208
SetAccessorConstant(int index,Handle<AccessorPair> pair)209 void SetAccessorConstant(int index, Handle<AccessorPair> pair) {
210 Handle<Object> getter = handle(pair->getter(), isolate_);
211 Handle<Object> setter = handle(pair->setter(), isolate_);
212 SetAccessorConstant(index, getter, setter);
213 }
214
GeneralizeRepresentation(int index)215 void GeneralizeRepresentation(int index) {
216 CHECK(index < number_of_properties_);
217 representations_[index] = Representation::Tagged();
218 if (types_[index] == DATA || types_[index] == ACCESSOR) {
219 values_[index] = HeapType::Any(isolate_);
220 }
221 }
222
223
Check(DescriptorArray * descriptors,int descriptor) const224 bool Check(DescriptorArray* descriptors, int descriptor) const {
225 PropertyType type = types_[descriptor];
226 if (!EqualDetails(descriptors, descriptor, type, attributes_[descriptor],
227 representations_[descriptor])) {
228 return false;
229 }
230 Object* value = descriptors->GetValue(descriptor);
231 Object* expected_value = *values_[descriptor];
232 switch (type) {
233 case DATA:
234 case ACCESSOR: {
235 HeapType* type = descriptors->GetFieldType(descriptor);
236 return HeapType::cast(expected_value)->Equals(type);
237 }
238
239 case DATA_CONSTANT:
240 return value == expected_value;
241
242 case ACCESSOR_CONSTANT: {
243 if (value == expected_value) return true;
244 if (!value->IsAccessorPair()) return false;
245 AccessorPair* pair = AccessorPair::cast(value);
246 return pair->Equals(expected_value, *setter_values_[descriptor]);
247 }
248 }
249 UNREACHABLE();
250 return false;
251 }
252
Check(Map * map,int expected_nof) const253 bool Check(Map* map, int expected_nof) const {
254 CHECK(number_of_properties_ <= MAX_PROPERTIES);
255 CHECK_EQ(expected_nof, map->NumberOfOwnDescriptors());
256 CHECK(!map->is_dictionary_map());
257
258 DescriptorArray* descriptors = map->instance_descriptors();
259 CHECK(expected_nof <= number_of_properties_);
260 for (int i = 0; i < expected_nof; i++) {
261 if (!Check(descriptors, i)) {
262 Print();
263 #ifdef OBJECT_PRINT
264 descriptors->Print();
265 #endif
266 Check(descriptors, i);
267 return false;
268 }
269 }
270 return true;
271 }
272
Check(Map * map) const273 bool Check(Map* map) const { return Check(map, number_of_properties_); }
274
275
276 //
277 // Helper methods for initializing expectations and adding properties to
278 // given |map|.
279 //
280
AddDataField(Handle<Map> map,PropertyAttributes attributes,Representation representation,Handle<HeapType> heap_type)281 Handle<Map> AddDataField(Handle<Map> map, PropertyAttributes attributes,
282 Representation representation,
283 Handle<HeapType> heap_type) {
284 CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
285 int property_index = number_of_properties_++;
286 SetDataField(property_index, attributes, representation, heap_type);
287
288 Handle<String> name = MakeName("prop", property_index);
289 return Map::CopyWithField(map, name, heap_type, attributes, representation,
290 INSERT_TRANSITION)
291 .ToHandleChecked();
292 }
293
AddDataConstant(Handle<Map> map,PropertyAttributes attributes,Handle<JSFunction> value)294 Handle<Map> AddDataConstant(Handle<Map> map, PropertyAttributes attributes,
295 Handle<JSFunction> value) {
296 CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
297 int property_index = number_of_properties_++;
298 SetDataConstant(property_index, attributes, value);
299
300 Handle<String> name = MakeName("prop", property_index);
301 return Map::CopyWithConstant(map, name, value, attributes,
302 INSERT_TRANSITION)
303 .ToHandleChecked();
304 }
305
TransitionToDataField(Handle<Map> map,PropertyAttributes attributes,Representation representation,Handle<HeapType> heap_type,Handle<Object> value)306 Handle<Map> TransitionToDataField(Handle<Map> map,
307 PropertyAttributes attributes,
308 Representation representation,
309 Handle<HeapType> heap_type,
310 Handle<Object> value) {
311 CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
312 int property_index = number_of_properties_++;
313 SetDataField(property_index, attributes, representation, heap_type);
314
315 Handle<String> name = MakeName("prop", property_index);
316 return Map::TransitionToDataProperty(
317 map, name, value, attributes, Object::CERTAINLY_NOT_STORE_FROM_KEYED);
318 }
319
TransitionToDataConstant(Handle<Map> map,PropertyAttributes attributes,Handle<JSFunction> value)320 Handle<Map> TransitionToDataConstant(Handle<Map> map,
321 PropertyAttributes attributes,
322 Handle<JSFunction> value) {
323 CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
324 int property_index = number_of_properties_++;
325 SetDataConstant(property_index, attributes, value);
326
327 Handle<String> name = MakeName("prop", property_index);
328 return Map::TransitionToDataProperty(
329 map, name, value, attributes, Object::CERTAINLY_NOT_STORE_FROM_KEYED);
330 }
331
FollowDataTransition(Handle<Map> map,PropertyAttributes attributes,Representation representation,Handle<HeapType> heap_type)332 Handle<Map> FollowDataTransition(Handle<Map> map,
333 PropertyAttributes attributes,
334 Representation representation,
335 Handle<HeapType> heap_type) {
336 CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
337 int property_index = number_of_properties_++;
338 SetDataField(property_index, attributes, representation, heap_type);
339
340 Handle<String> name = MakeName("prop", property_index);
341 Map* target =
342 TransitionArray::SearchTransition(*map, kData, *name, attributes);
343 CHECK(target != NULL);
344 return handle(target);
345 }
346
AddAccessorConstant(Handle<Map> map,PropertyAttributes attributes,Handle<AccessorPair> pair)347 Handle<Map> AddAccessorConstant(Handle<Map> map,
348 PropertyAttributes attributes,
349 Handle<AccessorPair> pair) {
350 CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
351 int property_index = number_of_properties_++;
352 SetAccessorConstant(property_index, attributes, pair);
353
354 Handle<String> name = MakeName("prop", property_index);
355
356 AccessorConstantDescriptor new_desc(name, pair, attributes);
357 return Map::CopyInsertDescriptor(map, &new_desc, INSERT_TRANSITION);
358 }
359
AddAccessorConstant(Handle<Map> map,PropertyAttributes attributes,Handle<Object> getter,Handle<Object> setter)360 Handle<Map> AddAccessorConstant(Handle<Map> map,
361 PropertyAttributes attributes,
362 Handle<Object> getter,
363 Handle<Object> setter) {
364 CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
365 int property_index = number_of_properties_++;
366 SetAccessorConstant(property_index, attributes, getter, setter);
367
368 Handle<String> name = MakeName("prop", property_index);
369
370 CHECK(!getter->IsNull() || !setter->IsNull());
371 Factory* factory = isolate_->factory();
372
373 if (!getter->IsNull()) {
374 Handle<AccessorPair> pair = factory->NewAccessorPair();
375 pair->SetComponents(*getter, *factory->null_value());
376 AccessorConstantDescriptor new_desc(name, pair, attributes);
377 map = Map::CopyInsertDescriptor(map, &new_desc, INSERT_TRANSITION);
378 }
379 if (!setter->IsNull()) {
380 Handle<AccessorPair> pair = factory->NewAccessorPair();
381 pair->SetComponents(*getter, *setter);
382 AccessorConstantDescriptor new_desc(name, pair, attributes);
383 map = Map::CopyInsertDescriptor(map, &new_desc, INSERT_TRANSITION);
384 }
385 return map;
386 }
387
TransitionToAccessorConstant(Handle<Map> map,PropertyAttributes attributes,Handle<AccessorPair> pair)388 Handle<Map> TransitionToAccessorConstant(Handle<Map> map,
389 PropertyAttributes attributes,
390 Handle<AccessorPair> pair) {
391 CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors());
392 int property_index = number_of_properties_++;
393 SetAccessorConstant(property_index, attributes, pair);
394
395 Handle<String> name = MakeName("prop", property_index);
396
397 Isolate* isolate = CcTest::i_isolate();
398 Handle<Object> getter(pair->getter(), isolate);
399 Handle<Object> setter(pair->setter(), isolate);
400
401 map = Map::TransitionToAccessorProperty(map, name, ACCESSOR_GETTER, getter,
402 attributes);
403 CHECK(!map->is_deprecated());
404 CHECK(!map->is_dictionary_map());
405
406 map = Map::TransitionToAccessorProperty(map, name, ACCESSOR_SETTER, setter,
407 attributes);
408 CHECK(!map->is_deprecated());
409 CHECK(!map->is_dictionary_map());
410 return map;
411 }
412 };
413
414
415 ////////////////////////////////////////////////////////////////////////////////
416 // A set of tests for property reconfiguration that makes new transition tree
417 // branch.
418 //
419
TEST(ReconfigureAccessorToNonExistingDataField)420 TEST(ReconfigureAccessorToNonExistingDataField) {
421 CcTest::InitializeVM();
422 v8::HandleScope scope(CcTest::isolate());
423 Isolate* isolate = CcTest::i_isolate();
424 Handle<HeapType> any_type = HeapType::Any(isolate);
425 Handle<HeapType> none_type = HeapType::None(isolate);
426 Handle<AccessorPair> pair = CreateAccessorPair(true, true);
427
428 Expectations expectations(isolate);
429
430 // Create a map, add required properties to it and initialize expectations.
431 Handle<Map> initial_map = Map::Create(isolate, 0);
432 Handle<Map> map = initial_map;
433 map = expectations.AddAccessorConstant(map, NONE, pair);
434
435 CHECK(!map->is_deprecated());
436 CHECK(map->is_stable());
437 CHECK(expectations.Check(*map));
438
439 Handle<Map> new_map = Map::ReconfigureProperty(
440 map, 0, kData, NONE, Representation::None(), none_type, FORCE_FIELD);
441 // |map| did not change except marked unstable.
442 CHECK(!map->is_deprecated());
443 CHECK(!map->is_stable());
444 CHECK(expectations.Check(*map));
445
446 expectations.SetDataField(0, NONE, Representation::None(), none_type);
447
448 CHECK(!new_map->is_deprecated());
449 CHECK(new_map->is_stable());
450 CHECK(expectations.Check(*new_map));
451
452 Handle<Map> new_map2 = Map::ReconfigureProperty(
453 map, 0, kData, NONE, Representation::None(), none_type, FORCE_FIELD);
454 CHECK_EQ(*new_map, *new_map2);
455
456 Handle<Object> value(Smi::FromInt(0), isolate);
457 Handle<Map> prepared_map = Map::PrepareForDataProperty(new_map, 0, value);
458 // None to Smi generalization is trivial, map does not change.
459 CHECK_EQ(*new_map, *prepared_map);
460
461 expectations.SetDataField(0, NONE, Representation::Smi(), any_type);
462 CHECK(prepared_map->is_stable());
463 CHECK(expectations.Check(*prepared_map));
464
465 // Now create an object with |map|, migrate it to |prepared_map| and ensure
466 // that the data property is uninitialized.
467 Factory* factory = isolate->factory();
468 Handle<JSObject> obj = factory->NewJSObjectFromMap(map);
469 JSObject::MigrateToMap(obj, prepared_map);
470 FieldIndex index = FieldIndex::ForDescriptor(*prepared_map, 0);
471 CHECK(obj->RawFastPropertyAt(index)->IsUninitialized());
472 #ifdef VERIFY_HEAP
473 obj->ObjectVerify();
474 #endif
475 }
476
477
478 // This test checks that the LookupIterator machinery involved in
479 // JSObject::SetOwnPropertyIgnoreAttributes() does not try to migrate object
480 // to a map with a property with None representation.
TEST(ReconfigureAccessorToNonExistingDataFieldHeavy)481 TEST(ReconfigureAccessorToNonExistingDataFieldHeavy) {
482 CcTest::InitializeVM();
483 Isolate* isolate = CcTest::i_isolate();
484 Factory* factory = isolate->factory();
485 v8::HandleScope scope(CcTest::isolate());
486
487 CompileRun(
488 "function getter() { return 1; };"
489 "function setter() {};"
490 "var o = {};"
491 "Object.defineProperty(o, 'foo', "
492 " { get: getter, set: setter, "
493 " configurable: true, enumerable: true});");
494
495 Handle<String> foo_str = factory->InternalizeUtf8String("foo");
496 Handle<String> obj_name = factory->InternalizeUtf8String("o");
497
498 Handle<Object> obj_value =
499 Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked();
500 CHECK(obj_value->IsJSObject());
501 Handle<JSObject> obj = Handle<JSObject>::cast(obj_value);
502
503 CHECK_EQ(1, obj->map()->NumberOfOwnDescriptors());
504 CHECK(obj->map()->instance_descriptors()->GetValue(0)->IsAccessorPair());
505
506 Handle<Object> value(Smi::FromInt(42), isolate);
507 JSObject::SetOwnPropertyIgnoreAttributes(obj, foo_str, value, NONE).Check();
508
509 // Check that the property contains |value|.
510 CHECK_EQ(1, obj->map()->NumberOfOwnDescriptors());
511 FieldIndex index = FieldIndex::ForDescriptor(obj->map(), 0);
512 Object* the_value = obj->RawFastPropertyAt(index);
513 CHECK(the_value->IsSmi());
514 CHECK_EQ(42, Smi::cast(the_value)->value());
515 }
516
517
518 ////////////////////////////////////////////////////////////////////////////////
519 // A set of tests for representation generalization case.
520 //
521
522 // This test ensures that representation/field type generalization at
523 // |property_index| is done correctly independently of the fact that the |map|
524 // is detached from transition tree or not.
525 //
526 // {} - p0 - p1 - p2: |detach_point_map|
527 // |
528 // X - detached at |detach_property_at_index|
529 // |
530 // + - p3 - p4: |map|
531 //
532 // Detaching does not happen if |detach_property_at_index| is -1.
533 //
TestGeneralizeRepresentation(int detach_property_at_index,int property_index,Representation from_representation,Handle<HeapType> from_type,Representation to_representation,Handle<HeapType> to_type,Representation expected_representation,Handle<HeapType> expected_type,bool expected_deprecation,bool expected_field_type_dependency)534 static void TestGeneralizeRepresentation(
535 int detach_property_at_index, int property_index,
536 Representation from_representation, Handle<HeapType> from_type,
537 Representation to_representation, Handle<HeapType> to_type,
538 Representation expected_representation, Handle<HeapType> expected_type,
539 bool expected_deprecation, bool expected_field_type_dependency) {
540 Isolate* isolate = CcTest::i_isolate();
541 Handle<HeapType> any_type = HeapType::Any(isolate);
542
543 CHECK(detach_property_at_index >= -1 &&
544 detach_property_at_index < kPropCount);
545 CHECK(property_index < kPropCount);
546 CHECK_NE(detach_property_at_index, property_index);
547
548 const bool is_detached_map = detach_property_at_index >= 0;
549
550 Expectations expectations(isolate);
551
552 // Create a map, add required properties to it and initialize expectations.
553 Handle<Map> initial_map = Map::Create(isolate, 0);
554 Handle<Map> map = initial_map;
555 Handle<Map> detach_point_map;
556 for (int i = 0; i < kPropCount; i++) {
557 if (i == property_index) {
558 map =
559 expectations.AddDataField(map, NONE, from_representation, from_type);
560 } else {
561 map =
562 expectations.AddDataField(map, NONE, Representation::Smi(), any_type);
563 if (i == detach_property_at_index) {
564 detach_point_map = map;
565 }
566 }
567 }
568 CHECK(!map->is_deprecated());
569 CHECK(map->is_stable());
570 CHECK(expectations.Check(*map));
571
572 Zone zone;
573
574 if (is_detached_map) {
575 detach_point_map = Map::ReconfigureProperty(
576 detach_point_map, detach_property_at_index, kData, NONE,
577 Representation::Tagged(), any_type, FORCE_FIELD);
578 expectations.SetDataField(detach_property_at_index,
579 Representation::Tagged(), any_type);
580 CHECK(map->is_deprecated());
581 CHECK(expectations.Check(*detach_point_map,
582 detach_point_map->NumberOfOwnDescriptors()));
583 }
584
585 // Create new maps by generalizing representation of propX field.
586 Handle<Map> field_owner(map->FindFieldOwner(property_index), isolate);
587 CompilationInfo info("testing", isolate, &zone);
588 CHECK(!info.dependencies()->HasAborted());
589
590 info.dependencies()->AssumeFieldType(field_owner);
591
592 Handle<Map> new_map =
593 Map::ReconfigureProperty(map, property_index, kData, NONE,
594 to_representation, to_type, FORCE_FIELD);
595
596 expectations.SetDataField(property_index, expected_representation,
597 expected_type);
598
599 CHECK(!new_map->is_deprecated());
600 CHECK(expectations.Check(*new_map));
601
602 if (is_detached_map) {
603 CHECK(!map->is_stable());
604 CHECK(map->is_deprecated());
605 CHECK_NE(*map, *new_map);
606 CHECK_EQ(expected_field_type_dependency && !field_owner->is_deprecated(),
607 info.dependencies()->HasAborted());
608
609 } else if (expected_deprecation) {
610 CHECK(!map->is_stable());
611 CHECK(map->is_deprecated());
612 CHECK(field_owner->is_deprecated());
613 CHECK_NE(*map, *new_map);
614 CHECK(!info.dependencies()->HasAborted());
615
616 } else {
617 CHECK(!field_owner->is_deprecated());
618 CHECK(map->is_stable()); // Map did not change, must be left stable.
619 CHECK_EQ(*map, *new_map);
620
621 CHECK_EQ(expected_field_type_dependency, info.dependencies()->HasAborted());
622 }
623
624 {
625 // Check that all previous maps are not stable.
626 Map* tmp = *new_map;
627 while (true) {
628 Object* back = tmp->GetBackPointer();
629 if (back->IsUndefined()) break;
630 tmp = Map::cast(back);
631 CHECK(!tmp->is_stable());
632 }
633 }
634
635 info.dependencies()->Rollback(); // Properly cleanup compilation info.
636
637 // Update all deprecated maps and check that they are now the same.
638 Handle<Map> updated_map = Map::Update(map);
639 CHECK_EQ(*new_map, *updated_map);
640 }
641
642
TestGeneralizeRepresentation(Representation from_representation,Handle<HeapType> from_type,Representation to_representation,Handle<HeapType> to_type,Representation expected_representation,Handle<HeapType> expected_type,bool expected_deprecation,bool expected_field_type_dependency)643 static void TestGeneralizeRepresentation(
644 Representation from_representation, Handle<HeapType> from_type,
645 Representation to_representation, Handle<HeapType> to_type,
646 Representation expected_representation, Handle<HeapType> expected_type,
647 bool expected_deprecation, bool expected_field_type_dependency) {
648 // Check the cases when the map being reconfigured is a part of the
649 // transition tree.
650 STATIC_ASSERT(kPropCount > 4);
651 int indices[] = {0, 2, kPropCount - 1};
652 for (int i = 0; i < static_cast<int>(arraysize(indices)); i++) {
653 TestGeneralizeRepresentation(
654 -1, indices[i], from_representation, from_type, to_representation,
655 to_type, expected_representation, expected_type, expected_deprecation,
656 expected_field_type_dependency);
657 }
658
659 if (!from_representation.IsNone()) {
660 // Check the cases when the map being reconfigured is NOT a part of the
661 // transition tree. "None -> anything" representation changes make sense
662 // only for "attached" maps.
663 int indices[] = {0, kPropCount - 1};
664 for (int i = 0; i < static_cast<int>(arraysize(indices)); i++) {
665 TestGeneralizeRepresentation(
666 indices[i], 2, from_representation, from_type, to_representation,
667 to_type, expected_representation, expected_type, expected_deprecation,
668 expected_field_type_dependency);
669 }
670
671 // Check that reconfiguration to the very same field works correctly.
672 Representation representation = from_representation;
673 Handle<HeapType> type = from_type;
674 TestGeneralizeRepresentation(-1, 2, representation, type, representation,
675 type, representation, type, false, false);
676 }
677 }
678
679
TestGeneralizeRepresentation(Representation from_representation,Handle<HeapType> from_type,Representation to_representation,Handle<HeapType> to_type,Representation expected_representation,Handle<HeapType> expected_type)680 static void TestGeneralizeRepresentation(Representation from_representation,
681 Handle<HeapType> from_type,
682 Representation to_representation,
683 Handle<HeapType> to_type,
684 Representation expected_representation,
685 Handle<HeapType> expected_type) {
686 const bool expected_deprecation = true;
687 const bool expected_field_type_dependency = false;
688
689 TestGeneralizeRepresentation(
690 from_representation, from_type, to_representation, to_type,
691 expected_representation, expected_type, expected_deprecation,
692 expected_field_type_dependency);
693 }
694
695
TestGeneralizeRepresentationTrivial(Representation from_representation,Handle<HeapType> from_type,Representation to_representation,Handle<HeapType> to_type,Representation expected_representation,Handle<HeapType> expected_type,bool expected_field_type_dependency=true)696 static void TestGeneralizeRepresentationTrivial(
697 Representation from_representation, Handle<HeapType> from_type,
698 Representation to_representation, Handle<HeapType> to_type,
699 Representation expected_representation, Handle<HeapType> expected_type,
700 bool expected_field_type_dependency = true) {
701 const bool expected_deprecation = false;
702
703 TestGeneralizeRepresentation(
704 from_representation, from_type, to_representation, to_type,
705 expected_representation, expected_type, expected_deprecation,
706 expected_field_type_dependency);
707 }
708
709
TEST(GeneralizeRepresentationSmiToDouble)710 TEST(GeneralizeRepresentationSmiToDouble) {
711 CcTest::InitializeVM();
712 v8::HandleScope scope(CcTest::isolate());
713 Isolate* isolate = CcTest::i_isolate();
714 Handle<HeapType> any_type = HeapType::Any(isolate);
715
716 TestGeneralizeRepresentation(Representation::Smi(), any_type,
717 Representation::Double(), any_type,
718 Representation::Double(), any_type);
719 }
720
721
TEST(GeneralizeRepresentationSmiToTagged)722 TEST(GeneralizeRepresentationSmiToTagged) {
723 CcTest::InitializeVM();
724 v8::HandleScope scope(CcTest::isolate());
725 Isolate* isolate = CcTest::i_isolate();
726 Handle<HeapType> any_type = HeapType::Any(isolate);
727 Handle<HeapType> value_type =
728 HeapType::Class(Map::Create(isolate, 0), isolate);
729
730 TestGeneralizeRepresentation(Representation::Smi(), any_type,
731 Representation::HeapObject(), value_type,
732 Representation::Tagged(), any_type);
733 }
734
735
TEST(GeneralizeRepresentationDoubleToTagged)736 TEST(GeneralizeRepresentationDoubleToTagged) {
737 CcTest::InitializeVM();
738 v8::HandleScope scope(CcTest::isolate());
739 Isolate* isolate = CcTest::i_isolate();
740 Handle<HeapType> any_type = HeapType::Any(isolate);
741 Handle<HeapType> value_type =
742 HeapType::Class(Map::Create(isolate, 0), isolate);
743
744 TestGeneralizeRepresentation(Representation::Double(), any_type,
745 Representation::HeapObject(), value_type,
746 Representation::Tagged(), any_type);
747 }
748
749
TEST(GeneralizeRepresentationHeapObjectToTagged)750 TEST(GeneralizeRepresentationHeapObjectToTagged) {
751 CcTest::InitializeVM();
752 v8::HandleScope scope(CcTest::isolate());
753 Isolate* isolate = CcTest::i_isolate();
754 Handle<HeapType> any_type = HeapType::Any(isolate);
755 Handle<HeapType> value_type =
756 HeapType::Class(Map::Create(isolate, 0), isolate);
757
758 TestGeneralizeRepresentation(Representation::HeapObject(), value_type,
759 Representation::Smi(), any_type,
760 Representation::Tagged(), any_type);
761 }
762
763
TEST(GeneralizeRepresentationHeapObjectToHeapObject)764 TEST(GeneralizeRepresentationHeapObjectToHeapObject) {
765 CcTest::InitializeVM();
766 v8::HandleScope scope(CcTest::isolate());
767 Isolate* isolate = CcTest::i_isolate();
768 Handle<HeapType> any_type = HeapType::Any(isolate);
769
770 const int kMaxClassesPerFieldType = 1;
771 Handle<HeapType> current_type =
772 HeapType::Class(Map::Create(isolate, 0), isolate);
773
774 for (int i = 0; i < kMaxClassesPerFieldType; i++) {
775 Handle<HeapType> new_type =
776 HeapType::Class(Map::Create(isolate, 0), isolate);
777
778 Handle<HeapType> expected_type =
779 (i < kMaxClassesPerFieldType - 1)
780 ? HeapType::Union(current_type, new_type, isolate)
781 : any_type;
782
783 TestGeneralizeRepresentationTrivial(
784 Representation::HeapObject(), current_type,
785 Representation::HeapObject(), new_type, Representation::HeapObject(),
786 expected_type);
787 current_type = expected_type;
788 }
789
790 Handle<HeapType> new_type = HeapType::Class(Map::Create(isolate, 0), isolate);
791
792 TestGeneralizeRepresentationTrivial(
793 Representation::HeapObject(), any_type, Representation::HeapObject(),
794 new_type, Representation::HeapObject(), any_type, false);
795 }
796
797
TEST(GeneralizeRepresentationNoneToSmi)798 TEST(GeneralizeRepresentationNoneToSmi) {
799 CcTest::InitializeVM();
800 v8::HandleScope scope(CcTest::isolate());
801 Isolate* isolate = CcTest::i_isolate();
802 Handle<HeapType> none_type = HeapType::None(isolate);
803 Handle<HeapType> any_type = HeapType::Any(isolate);
804
805 // None -> Smi representation change is trivial.
806 TestGeneralizeRepresentationTrivial(Representation::None(), none_type,
807 Representation::Smi(), any_type,
808 Representation::Smi(), any_type);
809 }
810
811
TEST(GeneralizeRepresentationNoneToDouble)812 TEST(GeneralizeRepresentationNoneToDouble) {
813 CcTest::InitializeVM();
814 v8::HandleScope scope(CcTest::isolate());
815 Isolate* isolate = CcTest::i_isolate();
816 Handle<HeapType> none_type = HeapType::None(isolate);
817 Handle<HeapType> any_type = HeapType::Any(isolate);
818
819 // None -> Double representation change is NOT trivial.
820 TestGeneralizeRepresentation(Representation::None(), none_type,
821 Representation::Double(), any_type,
822 Representation::Double(), any_type);
823 }
824
825
TEST(GeneralizeRepresentationNoneToHeapObject)826 TEST(GeneralizeRepresentationNoneToHeapObject) {
827 CcTest::InitializeVM();
828 v8::HandleScope scope(CcTest::isolate());
829 Isolate* isolate = CcTest::i_isolate();
830 Handle<HeapType> none_type = HeapType::None(isolate);
831 Handle<HeapType> value_type =
832 HeapType::Class(Map::Create(isolate, 0), isolate);
833
834 // None -> HeapObject representation change is trivial.
835 TestGeneralizeRepresentationTrivial(Representation::None(), none_type,
836 Representation::HeapObject(), value_type,
837 Representation::HeapObject(), value_type);
838 }
839
840
TEST(GeneralizeRepresentationNoneToTagged)841 TEST(GeneralizeRepresentationNoneToTagged) {
842 CcTest::InitializeVM();
843 v8::HandleScope scope(CcTest::isolate());
844 Isolate* isolate = CcTest::i_isolate();
845 Handle<HeapType> none_type = HeapType::None(isolate);
846 Handle<HeapType> any_type = HeapType::Any(isolate);
847
848 // None -> HeapObject representation change is trivial.
849 TestGeneralizeRepresentationTrivial(Representation::None(), none_type,
850 Representation::Tagged(), any_type,
851 Representation::Tagged(), any_type);
852 }
853
854
855 ////////////////////////////////////////////////////////////////////////////////
856 // A set of tests for representation generalization case with kAccessor
857 // properties.
858 //
859
TEST(GeneralizeRepresentationWithAccessorProperties)860 TEST(GeneralizeRepresentationWithAccessorProperties) {
861 CcTest::InitializeVM();
862 v8::HandleScope scope(CcTest::isolate());
863 Isolate* isolate = CcTest::i_isolate();
864 Handle<HeapType> any_type = HeapType::Any(isolate);
865 Handle<AccessorPair> pair = CreateAccessorPair(true, true);
866
867 const int kAccessorProp = kPropCount / 2;
868 Expectations expectations(isolate);
869
870 // Create a map, add required properties to it and initialize expectations.
871 Handle<Map> initial_map = Map::Create(isolate, 0);
872 Handle<Map> map = initial_map;
873 for (int i = 0; i < kPropCount; i++) {
874 if (i == kAccessorProp) {
875 map = expectations.AddAccessorConstant(map, NONE, pair);
876 } else {
877 map =
878 expectations.AddDataField(map, NONE, Representation::Smi(), any_type);
879 }
880 }
881 CHECK(!map->is_deprecated());
882 CHECK(map->is_stable());
883 CHECK(expectations.Check(*map));
884
885 // Create new maps by generalizing representation of propX field.
886 Handle<Map> maps[kPropCount];
887 for (int i = 0; i < kPropCount; i++) {
888 if (i == kAccessorProp) {
889 // Skip accessor property reconfiguration.
890 maps[i] = maps[i - 1];
891 continue;
892 }
893 Handle<Map> new_map = Map::ReconfigureProperty(
894 map, i, kData, NONE, Representation::Double(), any_type, FORCE_FIELD);
895 maps[i] = new_map;
896
897 expectations.SetDataField(i, Representation::Double(), any_type);
898
899 CHECK(!map->is_stable());
900 CHECK(map->is_deprecated());
901 CHECK_NE(*map, *new_map);
902 CHECK(i == 0 || maps[i - 1]->is_deprecated());
903
904 CHECK(!new_map->is_deprecated());
905 CHECK(expectations.Check(*new_map));
906 }
907
908 Handle<Map> active_map = maps[kPropCount - 1];
909 CHECK(!active_map->is_deprecated());
910
911 // Update all deprecated maps and check that they are now the same.
912 Handle<Map> updated_map = Map::Update(map);
913 CHECK_EQ(*active_map, *updated_map);
914 for (int i = 0; i < kPropCount; i++) {
915 updated_map = Map::Update(maps[i]);
916 CHECK_EQ(*active_map, *updated_map);
917 }
918 }
919
920
921 ////////////////////////////////////////////////////////////////////////////////
922 // A set of tests for attribute reconfiguration case.
923 //
924
925 // This test ensures that representation/field type generalization is correctly
926 // propagated from one branch of transition tree (|map2|) to another (|map|).
927 //
928 // + - p2B - p3 - p4: |map2|
929 // |
930 // {} - p0 - p1 - p2A - p3 - p4: |map|
931 //
932 // where "p2A" and "p2B" differ only in the attributes.
933 //
TestReconfigureDataFieldAttribute_GeneralizeRepresentation(Representation from_representation,Handle<HeapType> from_type,Representation to_representation,Handle<HeapType> to_type,Representation expected_representation,Handle<HeapType> expected_type)934 static void TestReconfigureDataFieldAttribute_GeneralizeRepresentation(
935 Representation from_representation, Handle<HeapType> from_type,
936 Representation to_representation, Handle<HeapType> to_type,
937 Representation expected_representation, Handle<HeapType> expected_type) {
938 Isolate* isolate = CcTest::i_isolate();
939
940 Expectations expectations(isolate);
941
942 // Create a map, add required properties to it and initialize expectations.
943 Handle<Map> initial_map = Map::Create(isolate, 0);
944 Handle<Map> map = initial_map;
945 for (int i = 0; i < kPropCount; i++) {
946 map = expectations.AddDataField(map, NONE, from_representation, from_type);
947 }
948 CHECK(!map->is_deprecated());
949 CHECK(map->is_stable());
950 CHECK(expectations.Check(*map));
951
952
953 // Create another branch in transition tree (property at index |kSplitProp|
954 // has different attributes), initialize expectations.
955 const int kSplitProp = kPropCount / 2;
956 Expectations expectations2(isolate);
957
958 Handle<Map> map2 = initial_map;
959 for (int i = 0; i < kSplitProp; i++) {
960 map2 = expectations2.FollowDataTransition(map2, NONE, from_representation,
961 from_type);
962 }
963 map2 =
964 expectations2.AddDataField(map2, READ_ONLY, to_representation, to_type);
965
966 for (int i = kSplitProp + 1; i < kPropCount; i++) {
967 map2 = expectations2.AddDataField(map2, NONE, to_representation, to_type);
968 }
969 CHECK(!map2->is_deprecated());
970 CHECK(map2->is_stable());
971 CHECK(expectations2.Check(*map2));
972
973 Zone zone;
974 Handle<Map> field_owner(map->FindFieldOwner(kSplitProp), isolate);
975 CompilationInfo info("testing", isolate, &zone);
976 CHECK(!info.dependencies()->HasAborted());
977 info.dependencies()->AssumeFieldType(field_owner);
978
979 // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which
980 // should generalize representations in |map1|.
981 Handle<Map> new_map =
982 Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE);
983
984 // |map2| should be left unchanged but marked unstable.
985 CHECK(!map2->is_stable());
986 CHECK(!map2->is_deprecated());
987 CHECK_NE(*map2, *new_map);
988 CHECK(expectations2.Check(*map2));
989
990 // |map| should be deprecated and |new_map| should match new expectations.
991 for (int i = kSplitProp; i < kPropCount; i++) {
992 expectations.SetDataField(i, expected_representation, expected_type);
993 }
994 CHECK(map->is_deprecated());
995 CHECK(!info.dependencies()->HasAborted());
996 info.dependencies()->Rollback(); // Properly cleanup compilation info.
997 CHECK_NE(*map, *new_map);
998
999 CHECK(!new_map->is_deprecated());
1000 CHECK(expectations.Check(*new_map));
1001
1002 // Update deprecated |map|, it should become |new_map|.
1003 Handle<Map> updated_map = Map::Update(map);
1004 CHECK_EQ(*new_map, *updated_map);
1005 }
1006
1007
1008 // This test ensures that trivial representation/field type generalization
1009 // (from HeapObject to HeapObject) is correctly propagated from one branch of
1010 // 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 //
TestReconfigureDataFieldAttribute_GeneralizeRepresentationTrivial(Representation from_representation,Handle<HeapType> from_type,Representation to_representation,Handle<HeapType> to_type,Representation expected_representation,Handle<HeapType> expected_type,bool expected_field_type_dependency=true)1018 static void TestReconfigureDataFieldAttribute_GeneralizeRepresentationTrivial(
1019 Representation from_representation, Handle<HeapType> from_type,
1020 Representation to_representation, Handle<HeapType> to_type,
1021 Representation expected_representation, Handle<HeapType> expected_type,
1022 bool expected_field_type_dependency = true) {
1023 Isolate* isolate = CcTest::i_isolate();
1024
1025 Expectations expectations(isolate);
1026
1027 // Create a map, add required properties to it and initialize expectations.
1028 Handle<Map> initial_map = Map::Create(isolate, 0);
1029 Handle<Map> map = initial_map;
1030 for (int i = 0; i < kPropCount; i++) {
1031 map = expectations.AddDataField(map, NONE, from_representation, from_type);
1032 }
1033 CHECK(!map->is_deprecated());
1034 CHECK(map->is_stable());
1035 CHECK(expectations.Check(*map));
1036
1037
1038 // Create another branch in transition tree (property at index |kSplitProp|
1039 // has different attributes), initialize expectations.
1040 const int kSplitProp = kPropCount / 2;
1041 Expectations expectations2(isolate);
1042
1043 Handle<Map> map2 = initial_map;
1044 for (int i = 0; i < kSplitProp; i++) {
1045 map2 = expectations2.FollowDataTransition(map2, NONE, from_representation,
1046 from_type);
1047 }
1048 map2 =
1049 expectations2.AddDataField(map2, READ_ONLY, to_representation, to_type);
1050
1051 for (int i = kSplitProp + 1; i < kPropCount; i++) {
1052 map2 = expectations2.AddDataField(map2, NONE, to_representation, to_type);
1053 }
1054 CHECK(!map2->is_deprecated());
1055 CHECK(map2->is_stable());
1056 CHECK(expectations2.Check(*map2));
1057
1058 Zone zone;
1059 Handle<Map> field_owner(map->FindFieldOwner(kSplitProp), isolate);
1060 CompilationInfo info("testing", isolate, &zone);
1061 CHECK(!info.dependencies()->HasAborted());
1062 info.dependencies()->AssumeFieldType(field_owner);
1063
1064 // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which
1065 // should generalize representations in |map1|.
1066 Handle<Map> new_map =
1067 Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE);
1068
1069 // |map2| should be left unchanged but marked unstable.
1070 CHECK(!map2->is_stable());
1071 CHECK(!map2->is_deprecated());
1072 CHECK_NE(*map2, *new_map);
1073 CHECK(expectations2.Check(*map2));
1074
1075 // In trivial case |map| should be returned as a result of the property
1076 // reconfiguration, respective field types should be generalized and
1077 // respective code dependencies should be invalidated. |map| should be NOT
1078 // deprecated and it should match new expectations.
1079 for (int i = kSplitProp; i < kPropCount; i++) {
1080 expectations.SetDataField(i, expected_representation, expected_type);
1081 }
1082 CHECK(!map->is_deprecated());
1083 CHECK_EQ(*map, *new_map);
1084 CHECK_EQ(expected_field_type_dependency, info.dependencies()->HasAborted());
1085 info.dependencies()->Rollback(); // Properly cleanup compilation info.
1086
1087 CHECK(!new_map->is_deprecated());
1088 CHECK(expectations.Check(*new_map));
1089
1090 Handle<Map> updated_map = Map::Update(map);
1091 CHECK_EQ(*new_map, *updated_map);
1092 }
1093
1094
TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationSmiToDouble)1095 TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationSmiToDouble) {
1096 CcTest::InitializeVM();
1097 v8::HandleScope scope(CcTest::isolate());
1098 Isolate* isolate = CcTest::i_isolate();
1099 Handle<HeapType> any_type = HeapType::Any(isolate);
1100
1101 TestReconfigureDataFieldAttribute_GeneralizeRepresentation(
1102 Representation::Smi(), any_type, Representation::Double(), any_type,
1103 Representation::Double(), any_type);
1104 }
1105
1106
TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationSmiToTagged)1107 TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationSmiToTagged) {
1108 CcTest::InitializeVM();
1109 v8::HandleScope scope(CcTest::isolate());
1110 Isolate* isolate = CcTest::i_isolate();
1111 Handle<HeapType> any_type = HeapType::Any(isolate);
1112 Handle<HeapType> value_type =
1113 HeapType::Class(Map::Create(isolate, 0), isolate);
1114
1115 TestReconfigureDataFieldAttribute_GeneralizeRepresentation(
1116 Representation::Smi(), any_type, Representation::HeapObject(), value_type,
1117 Representation::Tagged(), any_type);
1118 }
1119
1120
TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationDoubleToTagged)1121 TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationDoubleToTagged) {
1122 CcTest::InitializeVM();
1123 v8::HandleScope scope(CcTest::isolate());
1124 Isolate* isolate = CcTest::i_isolate();
1125 Handle<HeapType> any_type = HeapType::Any(isolate);
1126 Handle<HeapType> value_type =
1127 HeapType::Class(Map::Create(isolate, 0), isolate);
1128
1129 TestReconfigureDataFieldAttribute_GeneralizeRepresentation(
1130 Representation::Double(), any_type, Representation::HeapObject(),
1131 value_type, Representation::Tagged(), any_type);
1132 }
1133
1134
TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationHeapObjToHeapObj)1135 TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationHeapObjToHeapObj) {
1136 CcTest::InitializeVM();
1137 v8::HandleScope scope(CcTest::isolate());
1138 Isolate* isolate = CcTest::i_isolate();
1139 Handle<HeapType> any_type = HeapType::Any(isolate);
1140
1141 const int kMaxClassesPerFieldType = 1;
1142 Handle<HeapType> current_type =
1143 HeapType::Class(Map::Create(isolate, 0), isolate);
1144
1145 for (int i = 0; i < kMaxClassesPerFieldType; i++) {
1146 Handle<HeapType> new_type =
1147 HeapType::Class(Map::Create(isolate, 0), isolate);
1148
1149 Handle<HeapType> expected_type =
1150 (i < kMaxClassesPerFieldType - 1)
1151 ? HeapType::Union(current_type, new_type, isolate)
1152 : any_type;
1153
1154 TestReconfigureDataFieldAttribute_GeneralizeRepresentationTrivial(
1155 Representation::HeapObject(), current_type,
1156 Representation::HeapObject(), new_type, Representation::HeapObject(),
1157 expected_type);
1158 current_type = expected_type;
1159 }
1160
1161 Handle<HeapType> new_type = HeapType::Class(Map::Create(isolate, 0), isolate);
1162
1163 TestReconfigureDataFieldAttribute_GeneralizeRepresentationTrivial(
1164 Representation::HeapObject(), any_type, Representation::HeapObject(),
1165 new_type, Representation::HeapObject(), any_type, false);
1166 }
1167
1168
TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationHeapObjectToTagged)1169 TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationHeapObjectToTagged) {
1170 CcTest::InitializeVM();
1171 v8::HandleScope scope(CcTest::isolate());
1172 Isolate* isolate = CcTest::i_isolate();
1173 Handle<HeapType> any_type = HeapType::Any(isolate);
1174 Handle<HeapType> value_type =
1175 HeapType::Class(Map::Create(isolate, 0), isolate);
1176
1177 TestReconfigureDataFieldAttribute_GeneralizeRepresentation(
1178 Representation::HeapObject(), value_type, Representation::Smi(), any_type,
1179 Representation::Tagged(), any_type);
1180 }
1181
1182
1183 // Checks that given |map| is deprecated and that it updates to given |new_map|
1184 // which in turn should match expectations.
1185 struct CheckDeprecated {
CheckCheckDeprecated1186 void Check(Handle<Map> map, Handle<Map> new_map,
1187 const Expectations& expectations) {
1188 CHECK(map->is_deprecated());
1189 CHECK_NE(*map, *new_map);
1190
1191 CHECK(!new_map->is_deprecated());
1192 CHECK(expectations.Check(*new_map));
1193
1194 // Update deprecated |map|, it should become |new_map|.
1195 Handle<Map> updated_map = Map::Update(map);
1196 CHECK_EQ(*new_map, *updated_map);
1197 }
1198 };
1199
1200
1201 // Checks that given |map| is NOT deprecated, equals to given |new_map| and
1202 // matches expectations.
1203 struct CheckSameMap {
CheckCheckSameMap1204 void Check(Handle<Map> map, Handle<Map> new_map,
1205 const Expectations& expectations) {
1206 // |map| was not reconfigured, therefore it should stay stable.
1207 CHECK(map->is_stable());
1208 CHECK(!map->is_deprecated());
1209 CHECK_EQ(*map, *new_map);
1210
1211 CHECK(!new_map->is_deprecated());
1212 CHECK(expectations.Check(*new_map));
1213
1214 // Update deprecated |map|, it should become |new_map|.
1215 Handle<Map> updated_map = Map::Update(map);
1216 CHECK_EQ(*new_map, *updated_map);
1217 }
1218 };
1219
1220
1221 // Checks that given |map| is NOT deprecated and matches expectations.
1222 // |new_map| is unrelated to |map|.
1223 struct CheckUnrelated {
CheckCheckUnrelated1224 void Check(Handle<Map> map, Handle<Map> new_map,
1225 const Expectations& expectations) {
1226 CHECK(!map->is_deprecated());
1227 CHECK_NE(*map, *new_map);
1228 CHECK(expectations.Check(*map));
1229
1230 CHECK(new_map->is_stable());
1231 CHECK(!new_map->is_deprecated());
1232 }
1233 };
1234
1235
1236 // Checks that given |map| is NOT deprecated, and |new_map| is a result of
1237 // copy-generalize-all-representations.
1238 struct CheckCopyGeneralizeAllRepresentations {
CheckCheckCopyGeneralizeAllRepresentations1239 void Check(Handle<Map> map, Handle<Map> new_map, Expectations& expectations) {
1240 CHECK(!map->is_deprecated());
1241 CHECK_NE(*map, *new_map);
1242
1243 CHECK(new_map->GetBackPointer()->IsUndefined());
1244 for (int i = 0; i < kPropCount; i++) {
1245 expectations.GeneralizeRepresentation(i);
1246 }
1247
1248 CHECK(!new_map->is_deprecated());
1249 CHECK(expectations.Check(*new_map));
1250 }
1251 };
1252
1253
1254 // This test ensures that representation/field type generalization is correctly
1255 // propagated from one branch of transition tree (|map2|) to another (|map1|).
1256 //
1257 // + - p2B - p3 - p4: |map2|
1258 // |
1259 // {} - p0 - p1: |map|
1260 // |
1261 // + - p2A - p3 - p4: |map1|
1262 // |
1263 // + - the property customized by the TestConfig provided
1264 //
1265 // where "p2A" and "p2B" differ only in the attributes.
1266 //
1267 template <typename TestConfig, typename Checker>
TestReconfigureProperty_CustomPropertyAfterTargetMap(TestConfig & config,Checker & checker)1268 static void TestReconfigureProperty_CustomPropertyAfterTargetMap(
1269 TestConfig& config, Checker& checker) {
1270 Isolate* isolate = CcTest::i_isolate();
1271 Handle<HeapType> any_type = HeapType::Any(isolate);
1272
1273 const int kCustomPropIndex = kPropCount - 2;
1274 Expectations expectations(isolate);
1275
1276 const int kSplitProp = 2;
1277 CHECK(kSplitProp < kCustomPropIndex);
1278
1279 const Representation representation = Representation::Smi();
1280
1281 // Create common part of transition tree.
1282 Handle<Map> initial_map = Map::Create(isolate, 0);
1283 Handle<Map> map = initial_map;
1284 for (int i = 0; i < kSplitProp; i++) {
1285 map = expectations.AddDataField(map, NONE, representation, any_type);
1286 }
1287 CHECK(!map->is_deprecated());
1288 CHECK(map->is_stable());
1289 CHECK(expectations.Check(*map));
1290
1291
1292 // Create branch to |map1|.
1293 Handle<Map> map1 = map;
1294 Expectations expectations1 = expectations;
1295 for (int i = kSplitProp; i < kCustomPropIndex; i++) {
1296 map1 = expectations1.AddDataField(map1, NONE, representation, any_type);
1297 }
1298 map1 = config.AddPropertyAtBranch(1, expectations1, map1);
1299 for (int i = kCustomPropIndex + 1; i < kPropCount; i++) {
1300 map1 = expectations1.AddDataField(map1, NONE, representation, any_type);
1301 }
1302 CHECK(!map1->is_deprecated());
1303 CHECK(map1->is_stable());
1304 CHECK(expectations1.Check(*map1));
1305
1306
1307 // Create another branch in transition tree (property at index |kSplitProp|
1308 // has different attributes), initialize expectations.
1309 Handle<Map> map2 = map;
1310 Expectations expectations2 = expectations;
1311 map2 = expectations2.AddDataField(map2, READ_ONLY, representation, any_type);
1312 for (int i = kSplitProp + 1; i < kCustomPropIndex; i++) {
1313 map2 = expectations2.AddDataField(map2, NONE, representation, any_type);
1314 }
1315 map2 = config.AddPropertyAtBranch(2, expectations2, map2);
1316 for (int i = kCustomPropIndex + 1; i < kPropCount; i++) {
1317 map2 = expectations2.AddDataField(map2, NONE, representation, any_type);
1318 }
1319 CHECK(!map2->is_deprecated());
1320 CHECK(map2->is_stable());
1321 CHECK(expectations2.Check(*map2));
1322
1323
1324 // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which
1325 // should generalize representations in |map1|.
1326 Handle<Map> new_map =
1327 Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE);
1328
1329 // |map2| should be left unchanged but marked unstable.
1330 CHECK(!map2->is_stable());
1331 CHECK(!map2->is_deprecated());
1332 CHECK_NE(*map2, *new_map);
1333 CHECK(expectations2.Check(*map2));
1334
1335 config.UpdateExpectations(kCustomPropIndex, expectations1);
1336 checker.Check(map1, new_map, expectations1);
1337 }
1338
1339
TEST(ReconfigureDataFieldAttribute_SameDataConstantAfterTargetMap)1340 TEST(ReconfigureDataFieldAttribute_SameDataConstantAfterTargetMap) {
1341 CcTest::InitializeVM();
1342 v8::HandleScope scope(CcTest::isolate());
1343
1344 struct TestConfig {
1345 Handle<JSFunction> js_func_;
1346 TestConfig() {
1347 Isolate* isolate = CcTest::i_isolate();
1348 Factory* factory = isolate->factory();
1349 js_func_ = factory->NewFunction(factory->empty_string());
1350 }
1351
1352 Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1353 Handle<Map> map) {
1354 CHECK(branch_id == 1 || branch_id == 2);
1355 // Add the same data constant property at both transition tree branches.
1356 return expectations.AddDataConstant(map, NONE, js_func_);
1357 }
1358
1359 void UpdateExpectations(int property_index, Expectations& expectations) {
1360 // Expectations stay the same.
1361 }
1362 };
1363
1364 TestConfig config;
1365 // Two branches are "compatible" so the |map1| should NOT be deprecated.
1366 CheckSameMap checker;
1367 TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1368 }
1369
1370
TEST(ReconfigureDataFieldAttribute_DataConstantToDataFieldAfterTargetMap)1371 TEST(ReconfigureDataFieldAttribute_DataConstantToDataFieldAfterTargetMap) {
1372 CcTest::InitializeVM();
1373 v8::HandleScope scope(CcTest::isolate());
1374
1375 struct TestConfig {
1376 Handle<JSFunction> js_func1_;
1377 Handle<JSFunction> js_func2_;
1378 TestConfig() {
1379 Isolate* isolate = CcTest::i_isolate();
1380 Factory* factory = isolate->factory();
1381 js_func1_ = factory->NewFunction(factory->empty_string());
1382 js_func2_ = factory->NewFunction(factory->empty_string());
1383 }
1384
1385 Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1386 Handle<Map> map) {
1387 CHECK(branch_id == 1 || branch_id == 2);
1388 Handle<JSFunction> js_func = branch_id == 1 ? js_func1_ : js_func2_;
1389 return expectations.AddDataConstant(map, NONE, js_func);
1390 }
1391
1392 void UpdateExpectations(int property_index, Expectations& expectations) {
1393 Isolate* isolate = CcTest::i_isolate();
1394 Handle<HeapType> function_type =
1395 HeapType::Class(isolate->sloppy_function_map(), isolate);
1396 expectations.SetDataField(property_index, Representation::HeapObject(),
1397 function_type);
1398 }
1399 };
1400
1401 TestConfig config;
1402 // Two branches are "incompatible" so the |map1| should be deprecated.
1403 CheckDeprecated checker;
1404 TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1405 }
1406
1407
TEST(ReconfigureDataFieldAttribute_DataConstantToAccConstantAfterTargetMap)1408 TEST(ReconfigureDataFieldAttribute_DataConstantToAccConstantAfterTargetMap) {
1409 CcTest::InitializeVM();
1410 v8::HandleScope scope(CcTest::isolate());
1411
1412 struct TestConfig {
1413 Handle<JSFunction> js_func_;
1414 Handle<AccessorPair> pair_;
1415 TestConfig() {
1416 Isolate* isolate = CcTest::i_isolate();
1417 Factory* factory = isolate->factory();
1418 js_func_ = factory->NewFunction(factory->empty_string());
1419 pair_ = CreateAccessorPair(true, true);
1420 }
1421
1422 Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1423 Handle<Map> map) {
1424 CHECK(branch_id == 1 || branch_id == 2);
1425 if (branch_id == 1) {
1426 return expectations.AddDataConstant(map, NONE, js_func_);
1427 } else {
1428 return expectations.AddAccessorConstant(map, NONE, pair_);
1429 }
1430 }
1431
1432 void UpdateExpectations(int property_index, Expectations& expectations) {}
1433 };
1434
1435 TestConfig config;
1436 // These are completely separate branches in transition tree.
1437 CheckUnrelated checker;
1438 TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1439 }
1440
1441
TEST(ReconfigureDataFieldAttribute_SameAccessorConstantAfterTargetMap)1442 TEST(ReconfigureDataFieldAttribute_SameAccessorConstantAfterTargetMap) {
1443 CcTest::InitializeVM();
1444 v8::HandleScope scope(CcTest::isolate());
1445
1446 struct TestConfig {
1447 Handle<AccessorPair> pair_;
1448 TestConfig() { pair_ = CreateAccessorPair(true, true); }
1449
1450 Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1451 Handle<Map> map) {
1452 CHECK(branch_id == 1 || branch_id == 2);
1453 // Add the same accessor constant property at both transition tree
1454 // branches.
1455 return expectations.AddAccessorConstant(map, NONE, pair_);
1456 }
1457
1458 void UpdateExpectations(int property_index, Expectations& expectations) {
1459 // Two branches are "compatible" so the |map1| should NOT be deprecated.
1460 }
1461 };
1462
1463 TestConfig config;
1464 CheckSameMap checker;
1465 TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1466 }
1467
1468
TEST(ReconfigureDataFieldAttribute_AccConstantToAccFieldAfterTargetMap)1469 TEST(ReconfigureDataFieldAttribute_AccConstantToAccFieldAfterTargetMap) {
1470 CcTest::InitializeVM();
1471 v8::HandleScope scope(CcTest::isolate());
1472
1473 struct TestConfig {
1474 Handle<AccessorPair> pair1_;
1475 Handle<AccessorPair> pair2_;
1476 TestConfig() {
1477 pair1_ = CreateAccessorPair(true, true);
1478 pair2_ = CreateAccessorPair(true, true);
1479 }
1480
1481 Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1482 Handle<Map> map) {
1483 CHECK(branch_id == 1 || branch_id == 2);
1484 Handle<AccessorPair> pair = branch_id == 1 ? pair1_ : pair2_;
1485 return expectations.AddAccessorConstant(map, NONE, pair);
1486 }
1487
1488 void UpdateExpectations(int property_index, Expectations& expectations) {
1489 if (IS_ACCESSOR_FIELD_SUPPORTED) {
1490 expectations.SetAccessorField(property_index);
1491 } else {
1492 // Currently we have a copy-generalize-all-representations case and
1493 // ACCESSOR property becomes ACCESSOR_CONSTANT.
1494 expectations.SetAccessorConstant(property_index, pair2_);
1495 }
1496 }
1497 };
1498
1499 TestConfig config;
1500 if (IS_ACCESSOR_FIELD_SUPPORTED) {
1501 CheckCopyGeneralizeAllRepresentations checker;
1502 TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1503 } else {
1504 // Currently we have a copy-generalize-all-representations case.
1505 CheckCopyGeneralizeAllRepresentations checker;
1506 TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1507 }
1508 }
1509
1510
TEST(ReconfigureDataFieldAttribute_AccConstantToDataFieldAfterTargetMap)1511 TEST(ReconfigureDataFieldAttribute_AccConstantToDataFieldAfterTargetMap) {
1512 CcTest::InitializeVM();
1513 v8::HandleScope scope(CcTest::isolate());
1514
1515 struct TestConfig {
1516 Handle<AccessorPair> pair_;
1517 TestConfig() { pair_ = CreateAccessorPair(true, true); }
1518
1519 Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
1520 Handle<Map> map) {
1521 CHECK(branch_id == 1 || branch_id == 2);
1522 if (branch_id == 1) {
1523 return expectations.AddAccessorConstant(map, NONE, pair_);
1524 } else {
1525 Isolate* isolate = CcTest::i_isolate();
1526 Handle<HeapType> any_type = HeapType::Any(isolate);
1527 return expectations.AddDataField(map, NONE, Representation::Smi(),
1528 any_type);
1529 }
1530 }
1531
1532 void UpdateExpectations(int property_index, Expectations& expectations) {}
1533 };
1534
1535 TestConfig config;
1536 // These are completely separate branches in transition tree.
1537 CheckUnrelated checker;
1538 TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
1539 }
1540
1541
1542 ////////////////////////////////////////////////////////////////////////////////
1543 // A set of tests checking split map deprecation.
1544 //
1545
TEST(ReconfigurePropertySplitMapTransitionsOverflow)1546 TEST(ReconfigurePropertySplitMapTransitionsOverflow) {
1547 CcTest::InitializeVM();
1548 v8::HandleScope scope(CcTest::isolate());
1549 Isolate* isolate = CcTest::i_isolate();
1550 Handle<HeapType> any_type = HeapType::Any(isolate);
1551
1552 Expectations expectations(isolate);
1553
1554 // Create a map, add required properties to it and initialize expectations.
1555 Handle<Map> initial_map = Map::Create(isolate, 0);
1556 Handle<Map> map = initial_map;
1557 for (int i = 0; i < kPropCount; i++) {
1558 map = expectations.AddDataField(map, NONE, Representation::Smi(), any_type);
1559 }
1560 CHECK(!map->is_deprecated());
1561 CHECK(map->is_stable());
1562
1563 // Generalize representation of property at index |kSplitProp|.
1564 const int kSplitProp = kPropCount / 2;
1565 Handle<Map> split_map;
1566 Handle<Map> map2 = initial_map;
1567 {
1568 for (int i = 0; i < kSplitProp + 1; i++) {
1569 if (i == kSplitProp) {
1570 split_map = map2;
1571 }
1572
1573 Handle<String> name = MakeName("prop", i);
1574 Map* target =
1575 TransitionArray::SearchTransition(*map2, kData, *name, NONE);
1576 CHECK(target != NULL);
1577 map2 = handle(target);
1578 }
1579
1580 map2 = Map::ReconfigureProperty(map2, kSplitProp, kData, NONE,
1581 Representation::Double(), any_type,
1582 FORCE_FIELD);
1583 expectations.SetDataField(kSplitProp, Representation::Double(), any_type);
1584
1585 CHECK(expectations.Check(*split_map, kSplitProp));
1586 CHECK(expectations.Check(*map2, kSplitProp + 1));
1587 }
1588
1589 // At this point |map| should be deprecated and disconnected from the
1590 // transition tree.
1591 CHECK(map->is_deprecated());
1592 CHECK(!split_map->is_deprecated());
1593 CHECK(map2->is_stable());
1594 CHECK(!map2->is_deprecated());
1595
1596 // Fill in transition tree of |map2| so that it can't have more transitions.
1597 for (int i = 0; i < TransitionArray::kMaxNumberOfTransitions; i++) {
1598 CHECK(TransitionArray::CanHaveMoreTransitions(map2));
1599 Handle<String> name = MakeName("foo", i);
1600 Map::CopyWithField(map2, name, any_type, NONE, Representation::Smi(),
1601 INSERT_TRANSITION)
1602 .ToHandleChecked();
1603 }
1604 CHECK(!TransitionArray::CanHaveMoreTransitions(map2));
1605
1606 // Try to update |map|, since there is no place for propX transition at |map2|
1607 // |map| should become "copy-generalized".
1608 Handle<Map> updated_map = Map::Update(map);
1609 CHECK(updated_map->GetBackPointer()->IsUndefined());
1610
1611 for (int i = 0; i < kPropCount; i++) {
1612 expectations.SetDataField(i, Representation::Tagged(), any_type);
1613 }
1614 CHECK(expectations.Check(*updated_map));
1615 }
1616
1617
1618 ////////////////////////////////////////////////////////////////////////////////
1619 // A set of tests involving special transitions (such as elements kind
1620 // transition, observed transition or prototype transition).
1621 //
1622
1623 // This test ensures that representation/field type generalization is correctly
1624 // propagated from one branch of transition tree (|map2|) to another (|map|).
1625 //
1626 // p4B: |map2|
1627 // |
1628 // * - special transition
1629 // |
1630 // {} - p0 - p1 - p2A - p3 - p4A: |map|
1631 //
1632 // where "p4A" and "p4B" are exactly the same properties.
1633 //
1634 // TODO(ishell): unify this test template with
1635 // TestReconfigureDataFieldAttribute_GeneralizeRepresentation once
1636 // IS_PROTO_TRANS_ISSUE_FIXED and IS_NON_EQUIVALENT_TRANSITION_SUPPORTED are
1637 // fixed.
1638 template <typename TestConfig>
TestGeneralizeRepresentationWithSpecialTransition(TestConfig & config,Representation from_representation,Handle<HeapType> from_type,Representation to_representation,Handle<HeapType> to_type,Representation expected_representation,Handle<HeapType> expected_type)1639 static void TestGeneralizeRepresentationWithSpecialTransition(
1640 TestConfig& config, Representation from_representation,
1641 Handle<HeapType> from_type, Representation to_representation,
1642 Handle<HeapType> to_type, Representation expected_representation,
1643 Handle<HeapType> expected_type) {
1644 Isolate* isolate = CcTest::i_isolate();
1645
1646 Expectations expectations(isolate);
1647
1648 // Create a map, add required properties to it and initialize expectations.
1649 Handle<Map> initial_map = Map::Create(isolate, 0);
1650 Handle<Map> map = initial_map;
1651 for (int i = 0; i < kPropCount; i++) {
1652 map = expectations.AddDataField(map, NONE, from_representation, from_type);
1653 }
1654 CHECK(!map->is_deprecated());
1655 CHECK(map->is_stable());
1656 CHECK(expectations.Check(*map));
1657
1658 // Apply some special transition to |map|.
1659 CHECK(map->owns_descriptors());
1660 Handle<Map> map2 = config.Transition(map);
1661
1662 // |map| should still match expectations.
1663 CHECK(!map->is_deprecated());
1664 CHECK(expectations.Check(*map));
1665
1666 Expectations expectations2 = expectations;
1667 if (config.generalizes_representations()) {
1668 for (int i = 0; i < kPropCount; i++) {
1669 expectations2.GeneralizeRepresentation(i);
1670 }
1671 }
1672
1673 CHECK(!map2->is_deprecated());
1674 CHECK(map2->is_stable());
1675 CHECK(expectations2.Check(*map2));
1676
1677 // Create new maps by generalizing representation of propX field.
1678 Handle<Map> maps[kPropCount];
1679 for (int i = 0; i < kPropCount; i++) {
1680 Handle<Map> new_map = Map::ReconfigureProperty(
1681 map, i, kData, NONE, to_representation, to_type, FORCE_FIELD);
1682 maps[i] = new_map;
1683
1684 expectations.SetDataField(i, expected_representation, expected_type);
1685
1686 CHECK(map->is_deprecated());
1687 CHECK_NE(*map, *new_map);
1688 CHECK(i == 0 || maps[i - 1]->is_deprecated());
1689 CHECK(expectations.Check(*new_map));
1690
1691 Handle<Map> new_map2 = Map::Update(map2);
1692 CHECK(!new_map2->is_deprecated());
1693 CHECK(!new_map2->is_dictionary_map());
1694
1695 Handle<Map> tmp_map;
1696 if (Map::TryUpdate(map2).ToHandle(&tmp_map)) {
1697 // If Map::TryUpdate() manages to succeed the result must match the result
1698 // of Map::Update().
1699 CHECK_EQ(*new_map2, *tmp_map);
1700 }
1701
1702 if (config.is_non_equevalent_transition()) {
1703 // In case of non-equivalent transition currently we generalize all
1704 // representations.
1705 for (int i = 0; i < kPropCount; i++) {
1706 expectations2.GeneralizeRepresentation(i);
1707 }
1708 CHECK(new_map2->GetBackPointer()->IsUndefined());
1709 CHECK(expectations2.Check(*new_map2));
1710 } else {
1711 CHECK(!new_map2->GetBackPointer()->IsUndefined());
1712 CHECK(expectations2.Check(*new_map2));
1713 }
1714 }
1715
1716 Handle<Map> active_map = maps[kPropCount - 1];
1717 CHECK(!active_map->is_deprecated());
1718
1719 // Update all deprecated maps and check that they are now the same.
1720 Handle<Map> updated_map = Map::Update(map);
1721 CHECK_EQ(*active_map, *updated_map);
1722 for (int i = 0; i < kPropCount; i++) {
1723 updated_map = Map::Update(maps[i]);
1724 CHECK_EQ(*active_map, *updated_map);
1725 }
1726 }
1727
1728
TEST(ElementsKindTransitionFromMapOwningDescriptor)1729 TEST(ElementsKindTransitionFromMapOwningDescriptor) {
1730 CcTest::InitializeVM();
1731 v8::HandleScope scope(CcTest::isolate());
1732 Isolate* isolate = CcTest::i_isolate();
1733 Handle<HeapType> any_type = HeapType::Any(isolate);
1734 Handle<HeapType> value_type =
1735 HeapType::Class(Map::Create(isolate, 0), isolate);
1736
1737 struct TestConfig {
1738 Handle<Map> Transition(Handle<Map> map) {
1739 return Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS,
1740 INSERT_TRANSITION);
1741 }
1742 // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
1743 bool generalizes_representations() const { return false; }
1744 bool is_non_equevalent_transition() const { return false; }
1745 };
1746 TestConfig config;
1747 TestGeneralizeRepresentationWithSpecialTransition(
1748 config, Representation::Smi(), any_type, Representation::HeapObject(),
1749 value_type, Representation::Tagged(), any_type);
1750 }
1751
1752
TEST(ElementsKindTransitionFromMapNotOwningDescriptor)1753 TEST(ElementsKindTransitionFromMapNotOwningDescriptor) {
1754 CcTest::InitializeVM();
1755 v8::HandleScope scope(CcTest::isolate());
1756 Isolate* isolate = CcTest::i_isolate();
1757 Handle<HeapType> any_type = HeapType::Any(isolate);
1758 Handle<HeapType> value_type =
1759 HeapType::Class(Map::Create(isolate, 0), isolate);
1760
1761 struct TestConfig {
1762 Handle<Map> Transition(Handle<Map> map) {
1763 Isolate* isolate = CcTest::i_isolate();
1764 Handle<HeapType> any_type = HeapType::Any(isolate);
1765
1766 // Add one more transition to |map| in order to prevent descriptors
1767 // ownership.
1768 CHECK(map->owns_descriptors());
1769 Map::CopyWithField(map, MakeString("foo"), any_type, NONE,
1770 Representation::Smi(), INSERT_TRANSITION)
1771 .ToHandleChecked();
1772 CHECK(!map->owns_descriptors());
1773
1774 return Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS,
1775 INSERT_TRANSITION);
1776 }
1777 // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
1778 bool generalizes_representations() const { return false; }
1779 bool is_non_equevalent_transition() const { return false; }
1780 };
1781 TestConfig config;
1782 TestGeneralizeRepresentationWithSpecialTransition(
1783 config, Representation::Smi(), any_type, Representation::HeapObject(),
1784 value_type, Representation::Tagged(), any_type);
1785 }
1786
1787
TEST(ForObservedTransitionFromMapOwningDescriptor)1788 TEST(ForObservedTransitionFromMapOwningDescriptor) {
1789 CcTest::InitializeVM();
1790 v8::HandleScope scope(CcTest::isolate());
1791 Isolate* isolate = CcTest::i_isolate();
1792 Handle<HeapType> any_type = HeapType::Any(isolate);
1793 Handle<HeapType> value_type =
1794 HeapType::Class(Map::Create(isolate, 0), isolate);
1795
1796 struct TestConfig {
1797 Handle<Map> Transition(Handle<Map> map) {
1798 return Map::CopyForObserved(map);
1799 }
1800 // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
1801 bool generalizes_representations() const { return false; }
1802 bool is_non_equevalent_transition() const { return true; }
1803 };
1804 TestConfig config;
1805 TestGeneralizeRepresentationWithSpecialTransition(
1806 config, Representation::Smi(), any_type, Representation::HeapObject(),
1807 value_type, Representation::Tagged(), any_type);
1808 }
1809
1810
TEST(ForObservedTransitionFromMapNotOwningDescriptor)1811 TEST(ForObservedTransitionFromMapNotOwningDescriptor) {
1812 CcTest::InitializeVM();
1813 v8::HandleScope scope(CcTest::isolate());
1814 Isolate* isolate = CcTest::i_isolate();
1815 Handle<HeapType> any_type = HeapType::Any(isolate);
1816 Handle<HeapType> value_type =
1817 HeapType::Class(Map::Create(isolate, 0), isolate);
1818
1819 struct TestConfig {
1820 Handle<Map> Transition(Handle<Map> map) {
1821 Isolate* isolate = CcTest::i_isolate();
1822 Handle<HeapType> any_type = HeapType::Any(isolate);
1823
1824 // Add one more transition to |map| in order to prevent descriptors
1825 // ownership.
1826 CHECK(map->owns_descriptors());
1827 Map::CopyWithField(map, MakeString("foo"), any_type, NONE,
1828 Representation::Smi(), INSERT_TRANSITION)
1829 .ToHandleChecked();
1830 CHECK(!map->owns_descriptors());
1831
1832 return Map::CopyForObserved(map);
1833 }
1834 // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
1835 bool generalizes_representations() const { return false; }
1836 bool is_non_equevalent_transition() const { return true; }
1837 };
1838 TestConfig config;
1839 TestGeneralizeRepresentationWithSpecialTransition(
1840 config, Representation::Smi(), any_type, Representation::HeapObject(),
1841 value_type, Representation::Tagged(), any_type);
1842 }
1843
1844
TEST(PrototypeTransitionFromMapOwningDescriptor)1845 TEST(PrototypeTransitionFromMapOwningDescriptor) {
1846 CcTest::InitializeVM();
1847 v8::HandleScope scope(CcTest::isolate());
1848 Isolate* isolate = CcTest::i_isolate();
1849
1850 Handle<HeapType> any_type = HeapType::Any(isolate);
1851 Handle<HeapType> value_type =
1852 HeapType::Class(Map::Create(isolate, 0), isolate);
1853
1854 struct TestConfig {
1855 Handle<JSObject> prototype_;
1856
1857 TestConfig() {
1858 Isolate* isolate = CcTest::i_isolate();
1859 Factory* factory = isolate->factory();
1860 prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0));
1861 }
1862
1863 Handle<Map> Transition(Handle<Map> map) {
1864 return Map::TransitionToPrototype(map, prototype_, REGULAR_PROTOTYPE);
1865 }
1866 // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
1867 bool generalizes_representations() const {
1868 return !IS_PROTO_TRANS_ISSUE_FIXED;
1869 }
1870 bool is_non_equevalent_transition() const { return true; }
1871 };
1872 TestConfig config;
1873 TestGeneralizeRepresentationWithSpecialTransition(
1874 config, Representation::Smi(), any_type, Representation::HeapObject(),
1875 value_type, Representation::Tagged(), any_type);
1876 }
1877
1878
TEST(PrototypeTransitionFromMapNotOwningDescriptor)1879 TEST(PrototypeTransitionFromMapNotOwningDescriptor) {
1880 CcTest::InitializeVM();
1881 v8::HandleScope scope(CcTest::isolate());
1882 Isolate* isolate = CcTest::i_isolate();
1883
1884 Handle<HeapType> any_type = HeapType::Any(isolate);
1885 Handle<HeapType> value_type =
1886 HeapType::Class(Map::Create(isolate, 0), isolate);
1887
1888 struct TestConfig {
1889 Handle<JSObject> prototype_;
1890
1891 TestConfig() {
1892 Isolate* isolate = CcTest::i_isolate();
1893 Factory* factory = isolate->factory();
1894 prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0));
1895 }
1896
1897 Handle<Map> Transition(Handle<Map> map) {
1898 Isolate* isolate = CcTest::i_isolate();
1899 Handle<HeapType> any_type = HeapType::Any(isolate);
1900
1901 // Add one more transition to |map| in order to prevent descriptors
1902 // ownership.
1903 CHECK(map->owns_descriptors());
1904 Map::CopyWithField(map, MakeString("foo"), any_type, NONE,
1905 Representation::Smi(), INSERT_TRANSITION)
1906 .ToHandleChecked();
1907 CHECK(!map->owns_descriptors());
1908
1909 return Map::TransitionToPrototype(map, prototype_, REGULAR_PROTOTYPE);
1910 }
1911 // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed.
1912 bool generalizes_representations() const {
1913 return !IS_PROTO_TRANS_ISSUE_FIXED;
1914 }
1915 bool is_non_equevalent_transition() const { return true; }
1916 };
1917 TestConfig config;
1918 TestGeneralizeRepresentationWithSpecialTransition(
1919 config, Representation::Smi(), any_type, Representation::HeapObject(),
1920 value_type, Representation::Tagged(), any_type);
1921 }
1922
1923
1924 ////////////////////////////////////////////////////////////////////////////////
1925 // A set of tests for higher level transitioning mechanics.
1926 //
1927
1928 struct TransitionToDataFieldOperator {
1929 Representation representation_;
1930 PropertyAttributes attributes_;
1931 Handle<HeapType> heap_type_;
1932 Handle<Object> value_;
1933
TransitionToDataFieldOperatorTransitionToDataFieldOperator1934 TransitionToDataFieldOperator(Representation representation,
1935 Handle<HeapType> heap_type,
1936 Handle<Object> value,
1937 PropertyAttributes attributes = NONE)
1938 : representation_(representation),
1939 attributes_(attributes),
1940 heap_type_(heap_type),
1941 value_(value) {}
1942
DoTransitionTransitionToDataFieldOperator1943 Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) {
1944 return expectations.TransitionToDataField(map, attributes_, representation_,
1945 heap_type_, value_);
1946 }
1947 };
1948
1949
1950 struct TransitionToDataConstantOperator {
1951 PropertyAttributes attributes_;
1952 Handle<JSFunction> value_;
1953
TransitionToDataConstantOperatorTransitionToDataConstantOperator1954 TransitionToDataConstantOperator(Handle<JSFunction> value,
1955 PropertyAttributes attributes = NONE)
1956 : attributes_(attributes), value_(value) {}
1957
DoTransitionTransitionToDataConstantOperator1958 Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) {
1959 return expectations.TransitionToDataConstant(map, attributes_, value_);
1960 }
1961 };
1962
1963
1964 struct TransitionToAccessorConstantOperator {
1965 PropertyAttributes attributes_;
1966 Handle<AccessorPair> pair_;
1967
TransitionToAccessorConstantOperatorTransitionToAccessorConstantOperator1968 TransitionToAccessorConstantOperator(Handle<AccessorPair> pair,
1969 PropertyAttributes attributes = NONE)
1970 : attributes_(attributes), pair_(pair) {}
1971
DoTransitionTransitionToAccessorConstantOperator1972 Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) {
1973 return expectations.TransitionToAccessorConstant(map, attributes_, pair_);
1974 }
1975 };
1976
1977
1978 struct ReconfigureAsDataPropertyOperator {
1979 int descriptor_;
1980 Representation representation_;
1981 PropertyAttributes attributes_;
1982 Handle<HeapType> heap_type_;
1983
ReconfigureAsDataPropertyOperatorReconfigureAsDataPropertyOperator1984 ReconfigureAsDataPropertyOperator(int descriptor,
1985 Representation representation,
1986 Handle<HeapType> heap_type,
1987 PropertyAttributes attributes = NONE)
1988 : descriptor_(descriptor),
1989 representation_(representation),
1990 attributes_(attributes),
1991 heap_type_(heap_type) {}
1992
DoTransitionReconfigureAsDataPropertyOperator1993 Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) {
1994 expectations.SetDataField(descriptor_, representation_, heap_type_);
1995 return Map::ReconfigureExistingProperty(map, descriptor_, kData,
1996 attributes_);
1997 }
1998 };
1999
2000
2001 struct ReconfigureAsAccessorPropertyOperator {
2002 int descriptor_;
2003 PropertyAttributes attributes_;
2004
ReconfigureAsAccessorPropertyOperatorReconfigureAsAccessorPropertyOperator2005 ReconfigureAsAccessorPropertyOperator(int descriptor,
2006 PropertyAttributes attributes = NONE)
2007 : descriptor_(descriptor), attributes_(attributes) {}
2008
DoTransitionReconfigureAsAccessorPropertyOperator2009 Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) {
2010 expectations.SetAccessorField(descriptor_);
2011 return Map::ReconfigureExistingProperty(map, descriptor_, kAccessor,
2012 attributes_);
2013 }
2014 };
2015
2016
2017 // Checks that representation/field type generalization happened.
2018 struct FieldGeneralizationChecker {
2019 int descriptor_;
2020 Representation representation_;
2021 PropertyAttributes attributes_;
2022 Handle<HeapType> heap_type_;
2023
FieldGeneralizationCheckerFieldGeneralizationChecker2024 FieldGeneralizationChecker(int descriptor, Representation representation,
2025 Handle<HeapType> heap_type,
2026 PropertyAttributes attributes = NONE)
2027 : descriptor_(descriptor),
2028 representation_(representation),
2029 attributes_(attributes),
2030 heap_type_(heap_type) {}
2031
CheckFieldGeneralizationChecker2032 void Check(Expectations& expectations2, Handle<Map> map1, Handle<Map> map2) {
2033 CHECK(!map2->is_deprecated());
2034
2035 CHECK(map1->is_deprecated());
2036 CHECK_NE(*map1, *map2);
2037 Handle<Map> updated_map = Map::Update(map1);
2038 CHECK_EQ(*map2, *updated_map);
2039
2040 expectations2.SetDataField(descriptor_, attributes_, representation_,
2041 heap_type_);
2042 CHECK(expectations2.Check(*map2));
2043 }
2044 };
2045
2046
2047 // Checks that existing transition was taken as is.
2048 struct SameMapChecker {
CheckSameMapChecker2049 void Check(Expectations& expectations, Handle<Map> map1, Handle<Map> map2) {
2050 CHECK(!map2->is_deprecated());
2051 CHECK_EQ(*map1, *map2);
2052 CHECK(expectations.Check(*map2));
2053 }
2054 };
2055
2056
2057 // Checks that both |map1| and |map2| should stays non-deprecated, this is
2058 // the case when property kind is change.
2059 struct PropertyKindReconfigurationChecker {
CheckPropertyKindReconfigurationChecker2060 void Check(Expectations& expectations, Handle<Map> map1, Handle<Map> map2) {
2061 CHECK(!map1->is_deprecated());
2062 CHECK(!map2->is_deprecated());
2063 CHECK_NE(*map1, *map2);
2064 CHECK(expectations.Check(*map2));
2065 }
2066 };
2067
2068
2069 // This test transitions to various property types under different
2070 // circumstances.
2071 // Plan:
2072 // 1) create a |map| with p0..p3 properties.
2073 // 2) create |map1| by adding "p4" to |map0|.
2074 // 3) create |map2| by transition to "p4" from |map0|.
2075 //
2076 // + - p4B: |map2|
2077 // |
2078 // {} - p0 - p1 - pA - p3: |map|
2079 // |
2080 // + - p4A: |map1|
2081 //
2082 // where "p4A" and "p4B" differ only in the attributes.
2083 //
2084 template <typename TransitionOp1, typename TransitionOp2, typename Checker>
TestTransitionTo(TransitionOp1 & transition_op1,TransitionOp2 & transition_op2,Checker & checker)2085 static void TestTransitionTo(TransitionOp1& transition_op1,
2086 TransitionOp2& transition_op2, Checker& checker) {
2087 Isolate* isolate = CcTest::i_isolate();
2088 Handle<HeapType> any_type = HeapType::Any(isolate);
2089
2090 Expectations expectations(isolate);
2091
2092 // Create a map, add required properties to it and initialize expectations.
2093 Handle<Map> initial_map = Map::Create(isolate, 0);
2094 Handle<Map> map = initial_map;
2095 for (int i = 0; i < kPropCount - 1; i++) {
2096 map = expectations.AddDataField(map, NONE, Representation::Smi(), any_type);
2097 }
2098 CHECK(expectations.Check(*map));
2099
2100 Expectations expectations1 = expectations;
2101 Handle<Map> map1 = transition_op1.DoTransition(expectations1, map);
2102 CHECK(expectations1.Check(*map1));
2103
2104 Expectations expectations2 = expectations;
2105 Handle<Map> map2 = transition_op2.DoTransition(expectations2, map);
2106
2107 // Let the test customization do the check.
2108 checker.Check(expectations2, map1, map2);
2109 }
2110
2111
TEST(TransitionDataFieldToDataField)2112 TEST(TransitionDataFieldToDataField) {
2113 CcTest::InitializeVM();
2114 v8::HandleScope scope(CcTest::isolate());
2115 Isolate* isolate = CcTest::i_isolate();
2116 Handle<HeapType> any_type = HeapType::Any(isolate);
2117
2118 Handle<Object> value1 = handle(Smi::FromInt(0), isolate);
2119 TransitionToDataFieldOperator transition_op1(Representation::Smi(), any_type,
2120 value1);
2121
2122 Handle<Object> value2 = isolate->factory()->NewHeapNumber(0);
2123 TransitionToDataFieldOperator transition_op2(Representation::Double(),
2124 any_type, value2);
2125
2126 FieldGeneralizationChecker checker(kPropCount - 1, Representation::Double(),
2127 any_type);
2128 TestTransitionTo(transition_op1, transition_op2, checker);
2129 }
2130
2131
TEST(TransitionDataConstantToSameDataConstant)2132 TEST(TransitionDataConstantToSameDataConstant) {
2133 CcTest::InitializeVM();
2134 v8::HandleScope scope(CcTest::isolate());
2135 Isolate* isolate = CcTest::i_isolate();
2136 Factory* factory = isolate->factory();
2137
2138 Handle<JSFunction> js_func = factory->NewFunction(factory->empty_string());
2139 TransitionToDataConstantOperator transition_op(js_func);
2140
2141 SameMapChecker checker;
2142 TestTransitionTo(transition_op, transition_op, checker);
2143 }
2144
2145
TEST(TransitionDataConstantToAnotherDataConstant)2146 TEST(TransitionDataConstantToAnotherDataConstant) {
2147 CcTest::InitializeVM();
2148 v8::HandleScope scope(CcTest::isolate());
2149 Isolate* isolate = CcTest::i_isolate();
2150 Factory* factory = isolate->factory();
2151 Handle<HeapType> function_type =
2152 HeapType::Class(isolate->sloppy_function_map(), isolate);
2153
2154 Handle<JSFunction> js_func1 = factory->NewFunction(factory->empty_string());
2155 TransitionToDataConstantOperator transition_op1(js_func1);
2156
2157 Handle<JSFunction> js_func2 = factory->NewFunction(factory->empty_string());
2158 TransitionToDataConstantOperator transition_op2(js_func2);
2159
2160 FieldGeneralizationChecker checker(
2161 kPropCount - 1, Representation::HeapObject(), function_type);
2162 TestTransitionTo(transition_op1, transition_op2, checker);
2163 }
2164
2165
TEST(TransitionDataConstantToDataField)2166 TEST(TransitionDataConstantToDataField) {
2167 CcTest::InitializeVM();
2168 v8::HandleScope scope(CcTest::isolate());
2169 Isolate* isolate = CcTest::i_isolate();
2170 Factory* factory = isolate->factory();
2171 Handle<HeapType> any_type = HeapType::Any(isolate);
2172
2173 Handle<JSFunction> js_func1 = factory->NewFunction(factory->empty_string());
2174 TransitionToDataConstantOperator transition_op1(js_func1);
2175
2176 Handle<Object> value2 = isolate->factory()->NewHeapNumber(0);
2177 TransitionToDataFieldOperator transition_op2(Representation::Double(),
2178 any_type, value2);
2179
2180 FieldGeneralizationChecker checker(kPropCount - 1, Representation::Tagged(),
2181 any_type);
2182 TestTransitionTo(transition_op1, transition_op2, checker);
2183 }
2184
2185
TEST(TransitionAccessorConstantToSameAccessorConstant)2186 TEST(TransitionAccessorConstantToSameAccessorConstant) {
2187 CcTest::InitializeVM();
2188 v8::HandleScope scope(CcTest::isolate());
2189
2190 Handle<AccessorPair> pair = CreateAccessorPair(true, true);
2191 TransitionToAccessorConstantOperator transition_op(pair);
2192
2193 SameMapChecker checker;
2194 TestTransitionTo(transition_op, transition_op, checker);
2195 }
2196
2197
2198 // TODO(ishell): add this test once IS_ACCESSOR_FIELD_SUPPORTED is supported.
2199 // TEST(TransitionAccessorConstantToAnotherAccessorConstant)
2200