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