| // Copyright 2014 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdlib.h> |
| #include <utility> |
| |
| #include "src/v8.h" |
| |
| #include "src/compilation-cache.h" |
| #include "src/execution.h" |
| #include "src/factory.h" |
| #include "src/field-type.h" |
| #include "src/global-handles.h" |
| #include "test/cctest/cctest.h" |
| |
| using namespace v8::internal; |
| |
| |
| // |
| // Helper functions. |
| // |
| |
| static void CheckPropertyDetailsFieldsConsistency(PropertyType type, |
| PropertyKind kind, |
| PropertyLocation location) { |
| int type_value = PropertyDetails::TypeField::encode(type); |
| int kind_location_value = PropertyDetails::KindField::encode(kind) | |
| PropertyDetails::LocationField::encode(location); |
| CHECK_EQ(type_value, kind_location_value); |
| } |
| |
| |
| TEST(PropertyDetailsFieldsConsistency) { |
| CheckPropertyDetailsFieldsConsistency(DATA, kData, kField); |
| CheckPropertyDetailsFieldsConsistency(DATA_CONSTANT, kData, kDescriptor); |
| CheckPropertyDetailsFieldsConsistency(ACCESSOR, kAccessor, kField); |
| CheckPropertyDetailsFieldsConsistency(ACCESSOR_CONSTANT, kAccessor, |
| kDescriptor); |
| } |
| |
| |
| TEST(TransitionArray_SimpleFieldTransitions) { |
| CcTest::InitializeVM(); |
| v8::HandleScope scope(CcTest::isolate()); |
| Isolate* isolate = CcTest::i_isolate(); |
| Factory* factory = isolate->factory(); |
| |
| Handle<String> name1 = factory->InternalizeUtf8String("foo"); |
| Handle<String> name2 = factory->InternalizeUtf8String("bar"); |
| PropertyAttributes attributes = NONE; |
| |
| Handle<Map> map0 = Map::Create(isolate, 0); |
| Handle<Map> map1 = |
| Map::CopyWithField(map0, name1, handle(FieldType::Any(), isolate), |
| attributes, Representation::Tagged(), OMIT_TRANSITION) |
| .ToHandleChecked(); |
| Handle<Map> map2 = |
| Map::CopyWithField(map0, name2, handle(FieldType::Any(), isolate), |
| attributes, Representation::Tagged(), OMIT_TRANSITION) |
| .ToHandleChecked(); |
| |
| CHECK(map0->raw_transitions()->IsSmi()); |
| |
| TransitionArray::Insert(map0, name1, map1, SIMPLE_PROPERTY_TRANSITION); |
| CHECK(TransitionArray::IsSimpleTransition(map0->raw_transitions())); |
| CHECK_EQ(*map1, |
| TransitionArray::SearchTransition(*map0, kData, *name1, attributes)); |
| CHECK_EQ(1, TransitionArray::NumberOfTransitions(map0->raw_transitions())); |
| CHECK_EQ(*name1, TransitionArray::GetKey(map0->raw_transitions(), 0)); |
| CHECK_EQ(*map1, TransitionArray::GetTarget(map0->raw_transitions(), 0)); |
| |
| TransitionArray::Insert(map0, name2, map2, SIMPLE_PROPERTY_TRANSITION); |
| CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions())); |
| |
| CHECK_EQ(*map1, |
| TransitionArray::SearchTransition(*map0, kData, *name1, attributes)); |
| CHECK_EQ(*map2, |
| TransitionArray::SearchTransition(*map0, kData, *name2, attributes)); |
| CHECK_EQ(2, TransitionArray::NumberOfTransitions(map0->raw_transitions())); |
| for (int i = 0; i < 2; i++) { |
| Name* key = TransitionArray::GetKey(map0->raw_transitions(), i); |
| Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i); |
| CHECK((key == *name1 && target == *map1) || |
| (key == *name2 && target == *map2)); |
| } |
| |
| #ifdef DEBUG |
| CHECK(TransitionArray::IsSortedNoDuplicates(*map0)); |
| #endif |
| } |
| |
| |
| TEST(TransitionArray_FullFieldTransitions) { |
| CcTest::InitializeVM(); |
| v8::HandleScope scope(CcTest::isolate()); |
| Isolate* isolate = CcTest::i_isolate(); |
| Factory* factory = isolate->factory(); |
| |
| Handle<String> name1 = factory->InternalizeUtf8String("foo"); |
| Handle<String> name2 = factory->InternalizeUtf8String("bar"); |
| PropertyAttributes attributes = NONE; |
| |
| Handle<Map> map0 = Map::Create(isolate, 0); |
| Handle<Map> map1 = |
| Map::CopyWithField(map0, name1, handle(FieldType::Any(), isolate), |
| attributes, Representation::Tagged(), OMIT_TRANSITION) |
| .ToHandleChecked(); |
| Handle<Map> map2 = |
| Map::CopyWithField(map0, name2, handle(FieldType::Any(), isolate), |
| attributes, Representation::Tagged(), OMIT_TRANSITION) |
| .ToHandleChecked(); |
| |
| CHECK(map0->raw_transitions()->IsSmi()); |
| |
| TransitionArray::Insert(map0, name1, map1, PROPERTY_TRANSITION); |
| CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions())); |
| CHECK_EQ(*map1, |
| TransitionArray::SearchTransition(*map0, kData, *name1, attributes)); |
| CHECK_EQ(1, TransitionArray::NumberOfTransitions(map0->raw_transitions())); |
| CHECK_EQ(*name1, TransitionArray::GetKey(map0->raw_transitions(), 0)); |
| CHECK_EQ(*map1, TransitionArray::GetTarget(map0->raw_transitions(), 0)); |
| |
| TransitionArray::Insert(map0, name2, map2, PROPERTY_TRANSITION); |
| CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions())); |
| |
| CHECK_EQ(*map1, |
| TransitionArray::SearchTransition(*map0, kData, *name1, attributes)); |
| CHECK_EQ(*map2, |
| TransitionArray::SearchTransition(*map0, kData, *name2, attributes)); |
| CHECK_EQ(2, TransitionArray::NumberOfTransitions(map0->raw_transitions())); |
| for (int i = 0; i < 2; i++) { |
| Name* key = TransitionArray::GetKey(map0->raw_transitions(), i); |
| Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i); |
| CHECK((key == *name1 && target == *map1) || |
| (key == *name2 && target == *map2)); |
| } |
| |
| #ifdef DEBUG |
| CHECK(TransitionArray::IsSortedNoDuplicates(*map0)); |
| #endif |
| } |
| |
| |
| TEST(TransitionArray_DifferentFieldNames) { |
| CcTest::InitializeVM(); |
| v8::HandleScope scope(CcTest::isolate()); |
| Isolate* isolate = CcTest::i_isolate(); |
| Factory* factory = isolate->factory(); |
| |
| const int PROPS_COUNT = 10; |
| Handle<String> names[PROPS_COUNT]; |
| Handle<Map> maps[PROPS_COUNT]; |
| PropertyAttributes attributes = NONE; |
| |
| Handle<Map> map0 = Map::Create(isolate, 0); |
| CHECK(map0->raw_transitions()->IsSmi()); |
| |
| for (int i = 0; i < PROPS_COUNT; i++) { |
| EmbeddedVector<char, 64> buffer; |
| SNPrintF(buffer, "prop%d", i); |
| Handle<String> name = factory->InternalizeUtf8String(buffer.start()); |
| Handle<Map> map = Map::CopyWithField( |
| map0, name, handle(FieldType::Any(), isolate), |
| attributes, Representation::Tagged(), OMIT_TRANSITION) |
| .ToHandleChecked(); |
| names[i] = name; |
| maps[i] = map; |
| |
| TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION); |
| } |
| |
| for (int i = 0; i < PROPS_COUNT; i++) { |
| CHECK_EQ(*maps[i], TransitionArray::SearchTransition( |
| *map0, kData, *names[i], attributes)); |
| } |
| for (int i = 0; i < PROPS_COUNT; i++) { |
| Name* key = TransitionArray::GetKey(map0->raw_transitions(), i); |
| Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i); |
| for (int j = 0; j < PROPS_COUNT; j++) { |
| if (*names[i] == key) { |
| CHECK_EQ(*maps[i], target); |
| break; |
| } |
| } |
| } |
| |
| #ifdef DEBUG |
| CHECK(TransitionArray::IsSortedNoDuplicates(*map0)); |
| #endif |
| } |
| |
| |
| TEST(TransitionArray_SameFieldNamesDifferentAttributesSimple) { |
| CcTest::InitializeVM(); |
| v8::HandleScope scope(CcTest::isolate()); |
| Isolate* isolate = CcTest::i_isolate(); |
| Factory* factory = isolate->factory(); |
| |
| Handle<Map> map0 = Map::Create(isolate, 0); |
| CHECK(map0->raw_transitions()->IsSmi()); |
| |
| const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1; |
| STATIC_ASSERT(ATTRS_COUNT == 8); |
| Handle<Map> attr_maps[ATTRS_COUNT]; |
| Handle<String> name = factory->InternalizeUtf8String("foo"); |
| |
| // Add transitions for same field name but different attributes. |
| for (int i = 0; i < ATTRS_COUNT; i++) { |
| PropertyAttributes attributes = static_cast<PropertyAttributes>(i); |
| |
| Handle<Map> map = Map::CopyWithField( |
| map0, name, handle(FieldType::Any(), isolate), |
| attributes, Representation::Tagged(), OMIT_TRANSITION) |
| .ToHandleChecked(); |
| attr_maps[i] = map; |
| |
| TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION); |
| } |
| |
| // Ensure that transitions for |name| field are valid. |
| for (int i = 0; i < ATTRS_COUNT; i++) { |
| PropertyAttributes attributes = static_cast<PropertyAttributes>(i); |
| CHECK_EQ(*attr_maps[i], TransitionArray::SearchTransition( |
| *map0, kData, *name, attributes)); |
| // All transitions use the same key, so this check doesn't need to |
| // care about ordering. |
| CHECK_EQ(*name, TransitionArray::GetKey(map0->raw_transitions(), i)); |
| } |
| |
| #ifdef DEBUG |
| CHECK(TransitionArray::IsSortedNoDuplicates(*map0)); |
| #endif |
| } |
| |
| |
| TEST(TransitionArray_SameFieldNamesDifferentAttributes) { |
| CcTest::InitializeVM(); |
| v8::HandleScope scope(CcTest::isolate()); |
| Isolate* isolate = CcTest::i_isolate(); |
| Factory* factory = isolate->factory(); |
| |
| const int PROPS_COUNT = 10; |
| Handle<String> names[PROPS_COUNT]; |
| Handle<Map> maps[PROPS_COUNT]; |
| |
| Handle<Map> map0 = Map::Create(isolate, 0); |
| CHECK(map0->raw_transitions()->IsSmi()); |
| |
| // Some number of fields. |
| for (int i = 0; i < PROPS_COUNT; i++) { |
| EmbeddedVector<char, 64> buffer; |
| SNPrintF(buffer, "prop%d", i); |
| Handle<String> name = factory->InternalizeUtf8String(buffer.start()); |
| Handle<Map> map = |
| Map::CopyWithField(map0, name, handle(FieldType::Any(), isolate), NONE, |
| Representation::Tagged(), OMIT_TRANSITION) |
| .ToHandleChecked(); |
| names[i] = name; |
| maps[i] = map; |
| |
| TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION); |
| } |
| |
| const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1; |
| STATIC_ASSERT(ATTRS_COUNT == 8); |
| Handle<Map> attr_maps[ATTRS_COUNT]; |
| Handle<String> name = factory->InternalizeUtf8String("foo"); |
| |
| // Add transitions for same field name but different attributes. |
| for (int i = 0; i < ATTRS_COUNT; i++) { |
| PropertyAttributes attributes = static_cast<PropertyAttributes>(i); |
| |
| Handle<Map> map = Map::CopyWithField( |
| map0, name, handle(FieldType::Any(), isolate), |
| attributes, Representation::Tagged(), OMIT_TRANSITION) |
| .ToHandleChecked(); |
| attr_maps[i] = map; |
| |
| TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION); |
| } |
| |
| // Ensure that transitions for |name| field are valid. |
| for (int i = 0; i < ATTRS_COUNT; i++) { |
| PropertyAttributes attr = static_cast<PropertyAttributes>(i); |
| CHECK_EQ(*attr_maps[i], |
| TransitionArray::SearchTransition(*map0, kData, *name, attr)); |
| } |
| |
| // Ensure that info about the other fields still valid. |
| CHECK_EQ(PROPS_COUNT + ATTRS_COUNT, |
| TransitionArray::NumberOfTransitions(map0->raw_transitions())); |
| for (int i = 0; i < PROPS_COUNT + ATTRS_COUNT; i++) { |
| Name* key = TransitionArray::GetKey(map0->raw_transitions(), i); |
| Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i); |
| if (key == *name) { |
| // Attributes transition. |
| PropertyAttributes attributes = |
| target->GetLastDescriptorDetails().attributes(); |
| CHECK_EQ(*attr_maps[static_cast<int>(attributes)], target); |
| } else { |
| for (int j = 0; j < PROPS_COUNT; j++) { |
| if (*names[j] == key) { |
| CHECK_EQ(*maps[j], target); |
| break; |
| } |
| } |
| } |
| } |
| |
| #ifdef DEBUG |
| CHECK(TransitionArray::IsSortedNoDuplicates(*map0)); |
| #endif |
| } |