Index: test/cctest/test-migrations.cc |
diff --git a/test/cctest/test-migrations.cc b/test/cctest/test-migrations.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..79e23c2b639b03b25088747706c0b70281d8595d |
--- /dev/null |
+++ b/test/cctest/test-migrations.cc |
@@ -0,0 +1,2017 @@ |
+// Copyright 2015 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/code-stubs.h" |
+#include "src/compilation-cache.h" |
+#include "src/execution.h" |
+#include "src/factory.h" |
+#include "src/global-handles.h" |
+#include "src/ic/stub-cache.h" |
+#include "src/macro-assembler.h" |
+#include "src/smart-pointers.h" |
+#include "test/cctest/cctest.h" |
+ |
+using namespace v8::internal; |
+ |
+ |
+// TODO(ishell): fix this once ReconfigureProperty supports "non equivalent" |
+// transitions. |
+const bool IS_NON_EQUIVALENT_TRANSITION_SUPPORTED = false; |
+ |
+ |
+// TODO(ishell): fix this once TransitionToPrototype stops generalizing |
+// all field representations (similar to crbug/448711 where elements kind |
+// and observed transitions caused generalization of all field representations). |
+const bool IS_PROTO_TRANS_ISSUE_FIXED = false; |
+ |
+ |
+// TODO(ishell): fix this once TransitionToAccessorProperty is able to always |
+// keep map in fast mode. |
+const bool IS_ACCESSOR_FIELD_SUPPORTED = false; |
+ |
+ |
+// Number of properties used in the tests. |
+const int kPropCount = 7; |
+ |
+ |
+// |
+// Helper functions. |
+// |
+ |
+static Handle<String> MakeString(const char* str) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ return factory->InternalizeUtf8String(str); |
+} |
+ |
+ |
+static Handle<String> MakeName(const char* str, int suffix) { |
+ EmbeddedVector<char, 128> buffer; |
+ SNPrintF(buffer, "%s%d", str, suffix); |
+ return MakeString(buffer.start()); |
+} |
+ |
+ |
+static Handle<AccessorPair> CreateAccessorPair(bool with_getter, |
+ bool with_setter) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ Handle<AccessorPair> pair = factory->NewAccessorPair(); |
+ if (with_getter) { |
+ pair->set_getter(*factory->NewFunction(factory->empty_string())); |
+ } |
+ if (with_setter) { |
+ pair->set_setter(*factory->NewFunction(factory->empty_string())); |
+ } |
+ return pair; |
+} |
+ |
+ |
+static bool EqualDetails(DescriptorArray* descriptors, int descriptor, |
+ PropertyType type, PropertyAttributes attributes, |
+ Representation representation, int field_index = -1) { |
+ PropertyDetails details = descriptors->GetDetails(descriptor); |
+ if (details.type() != type) return false; |
+ if (details.attributes() != attributes) return false; |
+ if (!details.representation().Equals(representation)) return false; |
+ if (field_index >= 0 && details.field_index() != field_index) return false; |
+ return true; |
+} |
+ |
+ |
+class Expectations { |
+ static const int MAX_PROPERTIES = 10; |
+ Isolate* isolate_; |
+ PropertyType types_[MAX_PROPERTIES]; |
+ PropertyAttributes attributes_[MAX_PROPERTIES]; |
+ Representation representations_[MAX_PROPERTIES]; |
+ // HeapType for kField, value for DATA_CONSTANT and getter for |
+ // ACCESSOR_CONSTANT. |
+ Handle<Object> values_[MAX_PROPERTIES]; |
+ // Setter for ACCESSOR_CONSTANT. |
+ Handle<Object> setter_values_[MAX_PROPERTIES]; |
+ int number_of_properties_; |
+ |
+ public: |
+ explicit Expectations(Isolate* isolate) |
+ : isolate_(isolate), number_of_properties_(0) {} |
+ |
+ void Init(int index, PropertyType type, PropertyAttributes attributes, |
+ Representation representation, Handle<Object> value) { |
+ DCHECK(index < MAX_PROPERTIES); |
+ types_[index] = type; |
+ attributes_[index] = attributes; |
+ representations_[index] = representation; |
+ values_[index] = value; |
+ } |
+ |
+ void Print() const { |
+ OFStream os(stdout); |
+ os << "Expectations: #" << number_of_properties_ << "\n"; |
+ for (int i = 0; i < number_of_properties_; i++) { |
+ os << " " << i << ": "; |
+ os << "Descriptor @ "; |
+ if (types_[i] == ACCESSOR_CONSTANT) { |
+ os << "(get: " << Brief(*values_[i]) |
+ << ", set: " << Brief(*setter_values_[i]) << ") "; |
+ } else { |
+ os << Brief(*values_[i]); |
+ } |
+ os << " ("; |
+ switch (types_[i]) { |
+ case DATA_CONSTANT: |
+ os << "immutable "; |
+ // Fall through. |
+ case DATA: |
+ os << "data"; |
+ break; |
+ |
+ case ACCESSOR_CONSTANT: |
+ os << "immutable "; |
+ // Fall through. |
+ case ACCESSOR: |
+ os << "accessor"; |
+ break; |
+ } |
+ os << ": " << representations_[i].Mnemonic(); |
+ os << ", attrs: " << attributes_[i] << ")\n"; |
+ } |
+ } |
+ |
+ Handle<HeapType> GetFieldType(int index) { |
+ CHECK(index < MAX_PROPERTIES); |
+ CHECK(types_[index] == DATA || types_[index] == ACCESSOR); |
+ return Handle<HeapType>::cast(values_[index]); |
+ } |
+ |
+ void SetDataField(int index, PropertyAttributes attrs, |
+ Representation representation, Handle<HeapType> value) { |
+ Init(index, DATA, attrs, representation, value); |
+ } |
+ |
+ void SetDataField(int index, Representation representation, |
+ Handle<HeapType> value) { |
+ SetDataField(index, attributes_[index], representation, value); |
+ } |
+ |
+ void SetAccessorField(int index, PropertyAttributes attrs) { |
+ Init(index, ACCESSOR, attrs, Representation::Tagged(), |
+ HeapType::Any(isolate_)); |
+ } |
+ |
+ void SetAccessorField(int index) { |
+ SetAccessorField(index, attributes_[index]); |
+ } |
+ |
+ void SetDataConstant(int index, PropertyAttributes attrs, |
+ Handle<JSFunction> value) { |
+ Init(index, DATA_CONSTANT, attrs, Representation::HeapObject(), value); |
+ } |
+ |
+ void SetDataConstant(int index, Handle<JSFunction> value) { |
+ SetDataConstant(index, attributes_[index], value); |
+ } |
+ |
+ void SetAccessorConstant(int index, PropertyAttributes attrs, |
+ Handle<Object> getter, Handle<Object> setter) { |
+ Init(index, ACCESSOR_CONSTANT, attrs, Representation::Tagged(), getter); |
+ setter_values_[index] = setter; |
+ } |
+ |
+ void SetAccessorConstantComponent(int index, PropertyAttributes attrs, |
+ AccessorComponent component, |
+ Handle<Object> accessor) { |
+ CHECK_EQ(ACCESSOR_CONSTANT, types_[index]); |
+ CHECK(index < number_of_properties_); |
+ if (component == ACCESSOR_GETTER) { |
+ values_[index] = accessor; |
+ } else { |
+ setter_values_[index] = accessor; |
+ } |
+ } |
+ |
+ void SetAccessorConstant(int index, PropertyAttributes attrs, |
+ Handle<AccessorPair> pair) { |
+ Handle<Object> getter = handle(pair->getter(), isolate_); |
+ Handle<Object> setter = handle(pair->setter(), isolate_); |
+ SetAccessorConstant(index, attrs, getter, setter); |
+ } |
+ |
+ void SetAccessorConstant(int index, Handle<Object> getter, |
+ Handle<Object> setter) { |
+ SetAccessorConstant(index, attributes_[index], getter, setter); |
+ } |
+ |
+ void SetAccessorConstant(int index, Handle<AccessorPair> pair) { |
+ Handle<Object> getter = handle(pair->getter(), isolate_); |
+ Handle<Object> setter = handle(pair->setter(), isolate_); |
+ SetAccessorConstant(index, getter, setter); |
+ } |
+ |
+ void GeneralizeRepresentation(int index) { |
+ CHECK(index < number_of_properties_); |
+ representations_[index] = Representation::Tagged(); |
+ if (types_[index] == DATA || types_[index] == ACCESSOR) { |
+ values_[index] = HeapType::Any(isolate_); |
+ } |
+ } |
+ |
+ |
+ bool Check(DescriptorArray* descriptors, int descriptor) const { |
+ PropertyType type = types_[descriptor]; |
+ if (!EqualDetails(descriptors, descriptor, type, attributes_[descriptor], |
+ representations_[descriptor])) { |
+ return false; |
+ } |
+ Object* expected_value = *values_[descriptor]; |
+ Object* value = descriptors->GetValue(descriptor); |
+ switch (type) { |
+ case DATA: |
+ case ACCESSOR: |
+ return HeapType::cast(expected_value)->Equals(HeapType::cast(value)); |
+ |
+ case DATA_CONSTANT: |
+ return value == expected_value; |
+ |
+ case ACCESSOR_CONSTANT: { |
+ if (value == expected_value) return true; |
+ if (!value->IsAccessorPair()) return false; |
+ AccessorPair* pair = AccessorPair::cast(value); |
+ return pair->Equals(expected_value, *setter_values_[descriptor]); |
+ } |
+ } |
+ UNREACHABLE(); |
+ return false; |
+ } |
+ |
+ bool Check(Map* map, int expected_nof) const { |
+ CHECK(number_of_properties_ <= MAX_PROPERTIES); |
+ CHECK_EQ(expected_nof, map->NumberOfOwnDescriptors()); |
+ CHECK(!map->is_dictionary_map()); |
+ |
+ DescriptorArray* descriptors = map->instance_descriptors(); |
+ CHECK(expected_nof <= number_of_properties_); |
+ for (int i = 0; i < expected_nof; i++) { |
+ if (!Check(descriptors, i)) { |
+ Print(); |
+ Check(descriptors, i); |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ bool Check(Map* map) const { return Check(map, number_of_properties_); } |
+ |
+ |
+ // |
+ // Helper methods for initializing expectations and adding properties to |
+ // given |map|. |
+ // |
+ |
+ Handle<Map> AddDataField(Handle<Map> map, PropertyAttributes attributes, |
+ Representation representation, |
+ Handle<HeapType> heap_type) { |
+ CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors()); |
+ int property_index = number_of_properties_++; |
+ SetDataField(property_index, attributes, representation, heap_type); |
+ |
+ Handle<String> name = MakeName("prop", property_index); |
+ return Map::CopyWithField(map, name, heap_type, attributes, representation, |
+ INSERT_TRANSITION).ToHandleChecked(); |
+ } |
+ |
+ Handle<Map> AddDataConstant(Handle<Map> map, PropertyAttributes attributes, |
+ Handle<JSFunction> value) { |
+ CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors()); |
+ int property_index = number_of_properties_++; |
+ SetDataConstant(property_index, attributes, value); |
+ |
+ Handle<String> name = MakeName("prop", property_index); |
+ return Map::CopyWithConstant(map, name, value, attributes, |
+ INSERT_TRANSITION).ToHandleChecked(); |
+ } |
+ |
+ Handle<Map> TransitionToDataField(Handle<Map> map, |
+ PropertyAttributes attributes, |
+ Representation representation, |
+ Handle<HeapType> heap_type, |
+ Handle<Object> value) { |
+ CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors()); |
+ int property_index = number_of_properties_++; |
+ SetDataField(property_index, attributes, representation, heap_type); |
+ |
+ Handle<String> name = MakeName("prop", property_index); |
+ return Map::TransitionToDataProperty( |
+ map, name, value, attributes, Object::CERTAINLY_NOT_STORE_FROM_KEYED); |
+ } |
+ |
+ Handle<Map> TransitionToDataConstant(Handle<Map> map, |
+ PropertyAttributes attributes, |
+ Handle<JSFunction> value) { |
+ CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors()); |
+ int property_index = number_of_properties_++; |
+ SetDataConstant(property_index, attributes, value); |
+ |
+ Handle<String> name = MakeName("prop", property_index); |
+ return Map::TransitionToDataProperty( |
+ map, name, value, attributes, Object::CERTAINLY_NOT_STORE_FROM_KEYED); |
+ } |
+ |
+ Handle<Map> FollowDataTransition(Handle<Map> map, |
+ PropertyAttributes attributes, |
+ Representation representation, |
+ Handle<HeapType> heap_type) { |
+ CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors()); |
+ int property_index = number_of_properties_++; |
+ SetDataField(property_index, attributes, representation, heap_type); |
+ |
+ Handle<String> name = MakeName("prop", property_index); |
+ int t = map->SearchTransition(kData, *name, attributes); |
+ CHECK_NE(TransitionArray::kNotFound, t); |
+ return handle(map->GetTransition(t)); |
+ } |
+ |
+ Handle<Map> AddAccessorConstant(Handle<Map> map, |
+ PropertyAttributes attributes, |
+ Handle<AccessorPair> pair) { |
+ CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors()); |
+ int property_index = number_of_properties_++; |
+ SetAccessorConstant(property_index, attributes, pair); |
+ |
+ Handle<String> name = MakeName("prop", property_index); |
+ |
+ AccessorConstantDescriptor new_desc(name, pair, attributes); |
+ return Map::CopyInsertDescriptor(map, &new_desc, INSERT_TRANSITION); |
+ } |
+ |
+ Handle<Map> AddAccessorConstant(Handle<Map> map, |
+ PropertyAttributes attributes, |
+ Handle<Object> getter, |
+ Handle<Object> setter) { |
+ CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors()); |
+ int property_index = number_of_properties_++; |
+ SetAccessorConstant(property_index, attributes, getter, setter); |
+ |
+ Handle<String> name = MakeName("prop", property_index); |
+ |
+ CHECK(!getter->IsNull() || !setter->IsNull()); |
+ Factory* factory = isolate_->factory(); |
+ |
+ if (!getter->IsNull()) { |
+ Handle<AccessorPair> pair = factory->NewAccessorPair(); |
+ pair->SetComponents(*getter, *factory->null_value()); |
+ AccessorConstantDescriptor new_desc(name, pair, attributes); |
+ map = Map::CopyInsertDescriptor(map, &new_desc, INSERT_TRANSITION); |
+ } |
+ if (!setter->IsNull()) { |
+ Handle<AccessorPair> pair = factory->NewAccessorPair(); |
+ pair->SetComponents(*getter, *setter); |
+ AccessorConstantDescriptor new_desc(name, pair, attributes); |
+ map = Map::CopyInsertDescriptor(map, &new_desc, INSERT_TRANSITION); |
+ } |
+ return map; |
+ } |
+ |
+ Handle<Map> TransitionToAccessorConstant(Handle<Map> map, |
+ PropertyAttributes attributes, |
+ Handle<AccessorPair> pair) { |
+ CHECK_EQ(number_of_properties_, map->NumberOfOwnDescriptors()); |
+ int property_index = number_of_properties_++; |
+ SetAccessorConstant(property_index, attributes, pair); |
+ |
+ Handle<String> name = MakeName("prop", property_index); |
+ |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<Object> getter(pair->getter(), isolate); |
+ Handle<Object> setter(pair->setter(), isolate); |
+ |
+ map = Map::TransitionToAccessorProperty(map, name, ACCESSOR_GETTER, getter, |
+ attributes); |
+ CHECK(!map->is_deprecated()); |
+ CHECK(!map->is_dictionary_map()); |
+ |
+ map = Map::TransitionToAccessorProperty(map, name, ACCESSOR_SETTER, setter, |
+ attributes); |
+ CHECK(!map->is_deprecated()); |
+ CHECK(!map->is_dictionary_map()); |
+ return map; |
+ } |
+}; |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// A set of tests for property reconfiguration that makes new transition tree |
+// branch. |
+// |
+ |
+TEST(ReconfigureAccessorToNonExistingDataField) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> none_type = HeapType::None(isolate); |
+ Handle<AccessorPair> pair = CreateAccessorPair(true, true); |
+ |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ map = expectations.AddAccessorConstant(map, NONE, pair); |
+ |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ Handle<Map> new_map = Map::ReconfigureProperty( |
+ map, 0, kData, NONE, Representation::None(), none_type, FORCE_FIELD); |
+ // |map| did not change. |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ expectations.SetDataField(0, NONE, Representation::None(), none_type); |
+ |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(new_map->is_stable()); |
+ CHECK(expectations.Check(*new_map)); |
+ |
+ CHECK_EQ(*new_map, *Map::ReconfigureProperty(map, 0, kData, NONE, |
+ Representation::None(), |
+ none_type, FORCE_FIELD)); |
+ |
+ Handle<Object> value(Smi::FromInt(0), isolate); |
+ Handle<Map> prepared_map = Map::PrepareForDataProperty(new_map, 0, value); |
+ // None to Smi generalization is trivial, map does not change. |
+ CHECK_EQ(*new_map, *prepared_map); |
+ |
+ expectations.SetDataField(0, NONE, Representation::Smi(), any_type); |
+ CHECK(prepared_map->is_stable()); |
+ CHECK(expectations.Check(*prepared_map)); |
+ |
+ // Now create an object with |map|, migrate it to |prepared_map| and ensure |
+ // that the data property is uninitialized. |
+ Factory* factory = isolate->factory(); |
+ Handle<JSObject> obj = factory->NewJSObjectFromMap(map); |
+ JSObject::MigrateToMap(obj, prepared_map); |
+ FieldIndex index = FieldIndex::ForDescriptor(*prepared_map, 0); |
+ CHECK(obj->RawFastPropertyAt(index)->IsUninitialized()); |
+#ifdef VERIFY_HEAP |
+ obj->ObjectVerify(); |
+#endif |
+} |
+ |
+ |
+// This test checks that the LookupIterator machinery involved in |
+// JSObject::SetOwnPropertyIgnoreAttributes() does not try to migrate object |
+// to a map with a property with None representation. |
+TEST(ReconfigureAccessorToNonExistingDataFieldHeavy) { |
+ CcTest::InitializeVM(); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ |
+ CompileRun( |
+ "function getter() { return 1; };" |
+ "function setter() {};" |
+ "var o = {};" |
+ "Object.defineProperty(o, 'foo', " |
+ " { get: getter, set: setter, " |
+ " configurable: true, enumerable: true});"); |
+ |
+ Handle<String> foo_str = factory->InternalizeUtf8String("foo"); |
+ Handle<String> obj_name = factory->InternalizeUtf8String("o"); |
+ |
+ Handle<Object> obj_value = |
+ Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked(); |
+ CHECK(obj_value->IsJSObject()); |
+ Handle<JSObject> obj = Handle<JSObject>::cast(obj_value); |
+ |
+ CHECK_EQ(1, obj->map()->NumberOfOwnDescriptors()); |
+ CHECK(obj->map()->instance_descriptors()->GetValue(0)->IsAccessorPair()); |
+ |
+ Handle<Object> value(Smi::FromInt(42), isolate); |
+ JSObject::SetOwnPropertyIgnoreAttributes( |
+ obj, foo_str, value, NONE, JSObject::DONT_FORCE_FIELD).ToHandleChecked(); |
+ |
+ // Check that the property contains |value|. |
+ CHECK_EQ(1, obj->map()->NumberOfOwnDescriptors()); |
+ FieldIndex index = FieldIndex::ForDescriptor(obj->map(), 0); |
+ Object* the_value = obj->RawFastPropertyAt(index); |
+ CHECK(the_value->IsSmi()); |
+ CHECK_EQ(42, Smi::cast(the_value)->value()); |
+} |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// A set of tests for representation generalization case. |
+// |
+ |
+static void TestGeneralizeRepresentation(Representation from_representation, |
+ Handle<HeapType> from_type, |
+ Representation to_representation, |
+ Handle<HeapType> to_type, |
+ Representation expected_representation, |
+ Handle<HeapType> expected_type) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kPropCount; i++) { |
+ map = expectations.AddDataField(map, NONE, from_representation, from_type); |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ Zone zone; |
+ FakeStubForTesting stub(isolate); |
+ |
+ // Create new maps by generalizing representation of propX field. |
+ Handle<Map> maps[kPropCount]; |
+ for (int i = 0; i < kPropCount; i++) { |
+ Handle<Map> field_owner(map->FindFieldOwner(i), isolate); |
+ CompilationInfo info(&stub, isolate, &zone); |
+ CHECK(!info.HasAbortedDueToDependencyChange()); |
+ |
+ Map::AddDependentCompilationInfo(field_owner, |
+ DependentCode::kFieldTypeGroup, &info); |
+ |
+ Handle<Map> new_map = Map::ReconfigureProperty( |
+ map, i, kData, NONE, to_representation, to_type, FORCE_FIELD); |
+ maps[i] = new_map; |
+ |
+ expectations.SetDataField(i, expected_representation, expected_type); |
+ |
+ CHECK(map->is_deprecated()); |
+ CHECK(!info.HasAbortedDueToDependencyChange()); |
+ info.RollbackDependencies(); // Properly cleanup compilation info. |
+ |
+ CHECK_NE(*map, *new_map); |
+ CHECK(i == 0 || maps[i - 1]->is_deprecated()); |
+ |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(!new_map->is_dictionary_map()); |
+ CHECK(expectations.Check(*new_map)); |
+ } |
+ |
+ Handle<Map> active_map = maps[kPropCount - 1]; |
+ CHECK(!active_map->is_deprecated()); |
+ |
+ // Update all deprecated maps and check that they are now the same. |
+ CHECK_EQ(*active_map, *Map::Update(map)); |
+ for (int i = 0; i < kPropCount; i++) { |
+ CHECK_EQ(*active_map, *Map::Update(maps[i])); |
+ } |
+} |
+ |
+ |
+static void 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) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kPropCount; i++) { |
+ map = expectations.AddDataField(map, NONE, from_representation, from_type); |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ Zone zone; |
+ FakeStubForTesting stub(isolate); |
+ |
+ // Create new maps by generalizing representation of propX field. |
+ for (int i = 0; i < kPropCount; i++) { |
+ Handle<Map> field_owner(map->FindFieldOwner(i), isolate); |
+ CompilationInfo info(&stub, isolate, &zone); |
+ CHECK(!info.HasAbortedDueToDependencyChange()); |
+ |
+ Map::AddDependentCompilationInfo(field_owner, |
+ DependentCode::kFieldTypeGroup, &info); |
+ |
+ Handle<Map> new_map = Map::ReconfigureProperty( |
+ map, i, kData, NONE, to_representation, to_type, FORCE_FIELD); |
+ |
+ expectations.SetDataField(i, expected_representation, expected_type); |
+ |
+ CHECK_EQ(*map, *new_map); |
+ CHECK_EQ(expected_field_type_dependency, |
+ info.HasAbortedDueToDependencyChange()); |
+ |
+ info.RollbackDependencies(); // Properly cleanup compilation info. |
+ |
+ CHECK_EQ(*map, *new_map); |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(!new_map->is_dictionary_map()); |
+ CHECK(expectations.Check(*new_map)); |
+ } |
+ |
+ CHECK_EQ(*map, *Map::Update(map)); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationSmiToDouble) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ TestGeneralizeRepresentation(Representation::Smi(), any_type, |
+ Representation::Double(), any_type, |
+ Representation::Double(), any_type); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationSmiToTagged) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ TestGeneralizeRepresentation(Representation::Smi(), any_type, |
+ Representation::HeapObject(), value_type, |
+ Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationDoubleToTagged) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ TestGeneralizeRepresentation(Representation::Double(), any_type, |
+ Representation::HeapObject(), value_type, |
+ Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationHeapObjectToTagged) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ TestGeneralizeRepresentation(Representation::HeapObject(), value_type, |
+ Representation::Smi(), any_type, |
+ Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationHeapObjectToHeapObject) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ const int kMaxClassesPerFieldType = 5; |
+ Handle<HeapType> current_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ for (int i = 0; i < kMaxClassesPerFieldType; i++) { |
+ Handle<HeapType> new_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ Handle<HeapType> expected_type = |
+ (i < kMaxClassesPerFieldType - 1) |
+ ? HeapType::Union(current_type, new_type, isolate) |
+ : any_type; |
+ |
+ TestGeneralizeRepresentationTrivial( |
+ Representation::HeapObject(), current_type, |
+ Representation::HeapObject(), new_type, Representation::HeapObject(), |
+ expected_type); |
+ current_type = expected_type; |
+ } |
+ |
+ Handle<HeapType> new_type = HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ TestGeneralizeRepresentationTrivial( |
+ Representation::HeapObject(), any_type, Representation::HeapObject(), |
+ new_type, Representation::HeapObject(), any_type, false); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationNoneToSmi) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> none_type = HeapType::None(isolate); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ // None -> Smi representation change is trivial. |
+ TestGeneralizeRepresentationTrivial(Representation::None(), none_type, |
+ Representation::Smi(), any_type, |
+ Representation::Smi(), any_type); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationNoneToDouble) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> none_type = HeapType::None(isolate); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ // None -> Double representation change is NOT trivial. |
+ TestGeneralizeRepresentation(Representation::None(), none_type, |
+ Representation::Double(), any_type, |
+ Representation::Double(), any_type); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationNoneToHeapObject) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> none_type = HeapType::None(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ // None -> HeapObject representation change is trivial. |
+ TestGeneralizeRepresentationTrivial(Representation::None(), none_type, |
+ Representation::HeapObject(), value_type, |
+ Representation::HeapObject(), value_type); |
+} |
+ |
+ |
+TEST(GeneralizeRepresentationNoneToTagged) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> none_type = HeapType::None(isolate); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ // None -> HeapObject representation change is trivial. |
+ TestGeneralizeRepresentationTrivial(Representation::None(), none_type, |
+ Representation::Tagged(), any_type, |
+ Representation::Tagged(), any_type); |
+} |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// A set of tests for representation generalization case with kAccessor |
+// properties. |
+// |
+ |
+TEST(GeneralizeRepresentationWithAccessorProperties) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<AccessorPair> pair = CreateAccessorPair(true, true); |
+ |
+ const int kAccessorProp = kPropCount / 2; |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kPropCount; i++) { |
+ if (i == kAccessorProp) { |
+ map = expectations.AddAccessorConstant(map, NONE, pair); |
+ } else { |
+ map = |
+ expectations.AddDataField(map, NONE, Representation::Smi(), any_type); |
+ } |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ // Create new maps by generalizing representation of propX field. |
+ Handle<Map> maps[kPropCount]; |
+ for (int i = 0; i < kPropCount; i++) { |
+ if (i == kAccessorProp) { |
+ // Skip accessor property reconfiguration. |
+ maps[i] = maps[i - 1]; |
+ continue; |
+ } |
+ Handle<Map> new_map = Map::ReconfigureProperty( |
+ map, i, kData, NONE, Representation::Double(), any_type, FORCE_FIELD); |
+ maps[i] = new_map; |
+ |
+ expectations.SetDataField(i, Representation::Double(), any_type); |
+ |
+ CHECK(map->is_deprecated()); |
+ CHECK_NE(*map, *new_map); |
+ CHECK(i == 0 || maps[i - 1]->is_deprecated()); |
+ |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(!new_map->is_dictionary_map()); |
+ CHECK(expectations.Check(*new_map)); |
+ } |
+ |
+ Handle<Map> active_map = maps[kPropCount - 1]; |
+ CHECK(!active_map->is_deprecated()); |
+ |
+ // Update all deprecated maps and check that they are now the same. |
+ CHECK_EQ(*active_map, *Map::Update(map)); |
+ for (int i = 0; i < kPropCount; i++) { |
+ CHECK_EQ(*active_map, *Map::Update(maps[i])); |
+ } |
+} |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// A set of tests for attribute reconfiguration case. |
+// |
+ |
+// This test ensures that representation/field type generalization is correctly |
+// propagated from one branch of transition tree (|map2|) to another (|map|). |
+// |
+// - p2B - p3 - p4: |map2| |
+// / |
+// {} - p0 - p1 - p2A - p3 - p4: |map| |
+// |
+// where "p2A" and "p2B" differ only in the attributes. |
+// |
+static void TestReconfigureDataFieldAttribute_GeneralizeRepresentation( |
+ Representation from_representation, Handle<HeapType> from_type, |
+ Representation to_representation, Handle<HeapType> to_type, |
+ Representation expected_representation, Handle<HeapType> expected_type) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kPropCount; i++) { |
+ map = expectations.AddDataField(map, NONE, from_representation, from_type); |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ |
+ // Create another branch in transition tree (property at index |kSplitProp| |
+ // has different attributes), initialize expectations. |
+ const int kSplitProp = kPropCount / 2; |
+ Expectations expectations2(isolate); |
+ |
+ Handle<Map> map2 = initial_map; |
+ for (int i = 0; i < kSplitProp; i++) { |
+ map2 = expectations2.FollowDataTransition(map2, NONE, from_representation, |
+ from_type); |
+ } |
+ map2 = |
+ expectations2.AddDataField(map2, READ_ONLY, to_representation, to_type); |
+ |
+ for (int i = kSplitProp + 1; i < kPropCount; i++) { |
+ map2 = expectations2.AddDataField(map2, NONE, to_representation, to_type); |
+ } |
+ CHECK(!map2->is_deprecated()); |
+ CHECK(map2->is_stable()); |
+ CHECK(expectations2.Check(*map2)); |
+ |
+ Zone zone; |
+ FakeStubForTesting stub(isolate); |
+ Handle<Map> field_owner(map->FindFieldOwner(kSplitProp), isolate); |
+ CompilationInfo info(&stub, isolate, &zone); |
+ CHECK(!info.HasAbortedDueToDependencyChange()); |
+ Map::AddDependentCompilationInfo(field_owner, DependentCode::kFieldTypeGroup, |
+ &info); |
+ |
+ // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which |
+ // should generalize representations in |map1|. |
+ Handle<Map> new_map = |
+ Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE); |
+ |
+ // |map2| should be left unchanged. |
+ CHECK(!map2->is_deprecated()); |
+ CHECK_NE(*map2, *new_map); |
+ CHECK(expectations2.Check(*map2)); |
+ |
+ // |map| should be deprecated and |new_map| should match new expectations. |
+ for (int i = kSplitProp; i < kPropCount; i++) { |
+ expectations.SetDataField(i, expected_representation, expected_type); |
+ } |
+ CHECK(map->is_deprecated()); |
+ CHECK(!info.HasAbortedDueToDependencyChange()); |
+ info.RollbackDependencies(); // Properly cleanup compilation info. |
+ CHECK_NE(*map, *new_map); |
+ |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(!new_map->is_dictionary_map()); |
+ CHECK(expectations.Check(*new_map)); |
+ |
+ // Update deprecated |map|, it should become |new_map|. |
+ CHECK_EQ(*new_map, *Map::Update(map)); |
+} |
+ |
+ |
+// This test ensures that trivial representation/field type generalization |
+// (from HeapObject to HeapObject) is correctly propagated from one branch of |
+// transition tree (|map2|) to another (|map|). |
+// |
+// - p2B - p3 - p4: |map2| |
+// / |
+// {} - p0 - p1 - p2A - p3 - p4: |map| |
+// |
+// where "p2A" and "p2B" differ only in the attributes. |
+// |
+static void 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) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kPropCount; i++) { |
+ map = expectations.AddDataField(map, NONE, from_representation, from_type); |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ |
+ // Create another branch in transition tree (property at index |kSplitProp| |
+ // has different attributes), initialize expectations. |
+ const int kSplitProp = kPropCount / 2; |
+ Expectations expectations2(isolate); |
+ |
+ Handle<Map> map2 = initial_map; |
+ for (int i = 0; i < kSplitProp; i++) { |
+ map2 = expectations2.FollowDataTransition(map2, NONE, from_representation, |
+ from_type); |
+ } |
+ map2 = |
+ expectations2.AddDataField(map2, READ_ONLY, to_representation, to_type); |
+ |
+ for (int i = kSplitProp + 1; i < kPropCount; i++) { |
+ map2 = expectations2.AddDataField(map2, NONE, to_representation, to_type); |
+ } |
+ CHECK(!map2->is_deprecated()); |
+ CHECK(map2->is_stable()); |
+ CHECK(expectations2.Check(*map2)); |
+ |
+ Zone zone; |
+ FakeStubForTesting stub(isolate); |
+ Handle<Map> field_owner(map->FindFieldOwner(kSplitProp), isolate); |
+ CompilationInfo info(&stub, isolate, &zone); |
+ CHECK(!info.HasAbortedDueToDependencyChange()); |
+ Map::AddDependentCompilationInfo(field_owner, DependentCode::kFieldTypeGroup, |
+ &info); |
+ |
+ // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which |
+ // should generalize representations in |map1|. |
+ Handle<Map> new_map = |
+ Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE); |
+ |
+ // |map2| should be left unchanged. |
+ CHECK(!map2->is_deprecated()); |
+ CHECK_NE(*map2, *new_map); |
+ CHECK(expectations2.Check(*map2)); |
+ |
+ // In trivial case |map| should be returned as a result of the property |
+ // reconfiguration, respective field types should be generalized and |
+ // respective code dependencies should be invalidated. |map| should be NOT |
+ // deprecated and it should match new expectations. |
+ for (int i = kSplitProp; i < kPropCount; i++) { |
+ expectations.SetDataField(i, expected_representation, expected_type); |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK_EQ(*map, *new_map); |
+ CHECK_EQ(expected_field_type_dependency, |
+ info.HasAbortedDueToDependencyChange()); |
+ info.RollbackDependencies(); // Properly cleanup compilation info. |
+ |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(!new_map->is_dictionary_map()); |
+ CHECK(expectations.Check(*new_map)); |
+ |
+ CHECK_EQ(*new_map, *Map::Update(map)); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationSmiToDouble) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ TestReconfigureDataFieldAttribute_GeneralizeRepresentation( |
+ Representation::Smi(), any_type, Representation::Double(), any_type, |
+ Representation::Double(), any_type); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationSmiToTagged) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ TestReconfigureDataFieldAttribute_GeneralizeRepresentation( |
+ Representation::Smi(), any_type, Representation::HeapObject(), value_type, |
+ Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationDoubleToTagged) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ TestReconfigureDataFieldAttribute_GeneralizeRepresentation( |
+ Representation::Double(), any_type, Representation::HeapObject(), |
+ value_type, Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationHeapObjToHeapObj) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ const int kMaxClassesPerFieldType = 5; |
+ Handle<HeapType> current_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ for (int i = 0; i < kMaxClassesPerFieldType; i++) { |
+ Handle<HeapType> new_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ Handle<HeapType> expected_type = |
+ (i < kMaxClassesPerFieldType - 1) |
+ ? HeapType::Union(current_type, new_type, isolate) |
+ : any_type; |
+ |
+ TestReconfigureDataFieldAttribute_GeneralizeRepresentationTrivial( |
+ Representation::HeapObject(), current_type, |
+ Representation::HeapObject(), new_type, Representation::HeapObject(), |
+ expected_type); |
+ current_type = expected_type; |
+ } |
+ |
+ Handle<HeapType> new_type = HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ TestReconfigureDataFieldAttribute_GeneralizeRepresentationTrivial( |
+ Representation::HeapObject(), any_type, Representation::HeapObject(), |
+ new_type, Representation::HeapObject(), any_type, false); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationHeapObjectToTagged) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ TestReconfigureDataFieldAttribute_GeneralizeRepresentation( |
+ Representation::HeapObject(), value_type, Representation::Smi(), any_type, |
+ Representation::Tagged(), any_type); |
+} |
+ |
+ |
+// Checks that given |map| is deprecated and that it updates to given |new_map| |
+// which in turn should match expectations. |
+struct CheckDeprecated { |
+ void Check(Handle<Map> map, Handle<Map> new_map, |
+ const Expectations& expectations) { |
+ CHECK(map->is_deprecated()); |
+ CHECK_NE(*map, *new_map); |
+ |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(!new_map->is_dictionary_map()); |
+ CHECK(expectations.Check(*new_map)); |
+ |
+ // Update deprecated |map|, it should become |new_map|. |
+ CHECK_EQ(*new_map, *Map::Update(map)); |
+ } |
+}; |
+ |
+ |
+// Checks that given |map| is NOT deprecated, equals to given |new_map| and |
+// matches expectations. |
+struct CheckSameMap { |
+ void Check(Handle<Map> map, Handle<Map> new_map, |
+ const Expectations& expectations) { |
+ CHECK(!map->is_deprecated()); |
+ CHECK_EQ(*map, *new_map); |
+ |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(!new_map->is_dictionary_map()); |
+ CHECK(expectations.Check(*new_map)); |
+ |
+ // Update deprecated |map|, it should become |new_map|. |
+ CHECK_EQ(*new_map, *Map::Update(map)); |
+ } |
+}; |
+ |
+ |
+// Checks that given |map| is NOT deprecated, and |new_map| is a result of |
+// copy-generalize-all-representations. |
+struct CheckCopyGeneralizeAllRepresentations { |
+ void Check(Handle<Map> map, Handle<Map> new_map, Expectations& expectations) { |
+ CHECK(!map->is_deprecated()); |
+ CHECK_NE(*map, *new_map); |
+ |
+ CHECK(new_map->GetBackPointer()->IsUndefined()); |
+ for (int i = 0; i < kPropCount; i++) { |
+ expectations.GeneralizeRepresentation(i); |
+ } |
+ |
+ CHECK(!new_map->is_deprecated()); |
+ CHECK(!new_map->is_dictionary_map()); |
+ CHECK(expectations.Check(*new_map)); |
+ } |
+}; |
+ |
+ |
+// This test ensures that representation/field type generalization is correctly |
+// propagated from one branch of transition tree (|map2|) to another (|map1|). |
+// |
+// - p2B - p3 - p4: |map2| |
+// / |
+// {} - p0 - p1: |map| |
+// \ |
+// - p2A - p3 - p4: |map1| |
+// \ |
+// - the property customized by the TestConfig provided |
+// |
+// where "p2A" and "p2B" differ only in the attributes. |
+// |
+template <typename TestConfig, typename Checker> |
+static void TestReconfigureProperty_CustomPropertyAfterTargetMap( |
+ TestConfig& config, Checker& checker) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ const int kCustomPropIndex = kPropCount - 2; |
+ Expectations expectations(isolate); |
+ |
+ const int kSplitProp = 2; |
+ CHECK(kSplitProp < kCustomPropIndex); |
+ |
+ const Representation representation = Representation::Smi(); |
+ |
+ // Create common part of transition tree. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kSplitProp; i++) { |
+ map = expectations.AddDataField(map, NONE, representation, any_type); |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ |
+ // Create branch to |map1|. |
+ Handle<Map> map1 = map; |
+ Expectations expectations1 = expectations; |
+ for (int i = kSplitProp; i < kCustomPropIndex; i++) { |
+ map1 = expectations1.AddDataField(map1, NONE, representation, any_type); |
+ } |
+ map1 = config.AddPropertyAtBranch(1, expectations1, map1); |
+ for (int i = kCustomPropIndex + 1; i < kPropCount; i++) { |
+ map1 = expectations1.AddDataField(map1, NONE, representation, any_type); |
+ } |
+ CHECK(!map1->is_deprecated()); |
+ CHECK(map1->is_stable()); |
+ CHECK(expectations1.Check(*map1)); |
+ |
+ |
+ // Create another branch in transition tree (property at index |kSplitProp| |
+ // has different attributes), initialize expectations. |
+ Handle<Map> map2 = map; |
+ Expectations expectations2 = expectations; |
+ map2 = expectations2.AddDataField(map2, READ_ONLY, representation, any_type); |
+ for (int i = kSplitProp + 1; i < kCustomPropIndex; i++) { |
+ map2 = expectations2.AddDataField(map2, NONE, representation, any_type); |
+ } |
+ map2 = config.AddPropertyAtBranch(2, expectations2, map2); |
+ for (int i = kCustomPropIndex + 1; i < kPropCount; i++) { |
+ map2 = expectations2.AddDataField(map2, NONE, representation, any_type); |
+ } |
+ CHECK(!map2->is_deprecated()); |
+ CHECK(map2->is_stable()); |
+ CHECK(expectations2.Check(*map2)); |
+ |
+ |
+ // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which |
+ // should generalize representations in |map1|. |
+ Handle<Map> new_map = |
+ Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE); |
+ |
+ // |map2| should be left unchanged. |
+ CHECK(!map2->is_deprecated()); |
+ CHECK_NE(*map2, *new_map); |
+ CHECK(expectations2.Check(*map2)); |
+ |
+ config.UpdateExpectations(kCustomPropIndex, expectations1); |
+ checker.Check(map1, new_map, expectations1); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_SameDataConstantAfterTargetMap) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ |
+ struct TestConfig { |
+ Handle<JSFunction> js_func_; |
+ TestConfig() { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ js_func_ = factory->NewFunction(factory->empty_string()); |
+ } |
+ |
+ Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations, |
+ Handle<Map> map) { |
+ CHECK(branch_id == 1 || branch_id == 2); |
+ // Add the same data constant property at both transition tree branches. |
+ return expectations.AddDataConstant(map, NONE, js_func_); |
+ } |
+ |
+ void UpdateExpectations(int property_index, Expectations& expectations) { |
+ // Expectations stay the same. |
+ } |
+ }; |
+ |
+ TestConfig config; |
+ // Two branches are "compatible" so the |map1| should NOT be deprecated. |
+ CheckSameMap checker; |
+ TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_DataConstantToDataFieldAfterTargetMap) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ |
+ struct TestConfig { |
+ Handle<JSFunction> js_func1_; |
+ Handle<JSFunction> js_func2_; |
+ TestConfig() { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ js_func1_ = factory->NewFunction(factory->empty_string()); |
+ js_func2_ = factory->NewFunction(factory->empty_string()); |
+ } |
+ |
+ Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations, |
+ Handle<Map> map) { |
+ CHECK(branch_id == 1 || branch_id == 2); |
+ Handle<JSFunction> js_func = branch_id == 1 ? js_func1_ : js_func2_; |
+ return expectations.AddDataConstant(map, NONE, js_func); |
+ } |
+ |
+ void UpdateExpectations(int property_index, Expectations& expectations) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ expectations.SetDataField(property_index, Representation::HeapObject(), |
+ any_type); |
+ } |
+ }; |
+ |
+ TestConfig config; |
+ // Two branches are "incompatible" so the |map1| should be deprecated. |
+ CheckDeprecated checker; |
+ TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_SameAccessorConstantAfterTargetMap) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ |
+ struct TestConfig { |
+ Handle<AccessorPair> pair_; |
+ TestConfig() { pair_ = CreateAccessorPair(true, true); } |
+ |
+ Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations, |
+ Handle<Map> map) { |
+ CHECK(branch_id == 1 || branch_id == 2); |
+ // Add the same accessor constant property at both transition tree |
+ // branches. |
+ return expectations.AddAccessorConstant(map, NONE, pair_); |
+ } |
+ |
+ bool UpdateExpectations(int property_index, Expectations& expectations) { |
+ // Two branches are "compatible" so the |map1| should NOT be deprecated. |
+ return false; |
+ } |
+ }; |
+ |
+ TestConfig config; |
+ CheckSameMap checker; |
+ TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker); |
+} |
+ |
+ |
+TEST(ReconfigureDataFieldAttribute_AccConstantToAccFieldAfterTargetMap) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ |
+ struct TestConfig { |
+ Handle<AccessorPair> pair1_; |
+ Handle<AccessorPair> pair2_; |
+ TestConfig() { |
+ pair1_ = CreateAccessorPair(true, true); |
+ pair2_ = CreateAccessorPair(true, true); |
+ } |
+ |
+ Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations, |
+ Handle<Map> map) { |
+ CHECK(branch_id == 1 || branch_id == 2); |
+ Handle<AccessorPair> pair = branch_id == 1 ? pair1_ : pair2_; |
+ return expectations.AddAccessorConstant(map, NONE, pair); |
+ } |
+ |
+ void UpdateExpectations(int property_index, Expectations& expectations) { |
+ if (IS_ACCESSOR_FIELD_SUPPORTED) { |
+ expectations.SetAccessorField(property_index); |
+ } else { |
+ // Currently we have a copy-generalize-all-representations case and |
+ // ACCESSOR property becomes ACCESSOR_CONSTANT. |
+ expectations.SetAccessorConstant(property_index, pair2_); |
+ } |
+ } |
+ }; |
+ |
+ TestConfig config; |
+ if (IS_ACCESSOR_FIELD_SUPPORTED) { |
+ CheckCopyGeneralizeAllRepresentations checker; |
+ TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker); |
+ } else { |
+ // Currently we have a copy-generalize-all-representations case. |
+ CheckCopyGeneralizeAllRepresentations checker; |
+ TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker); |
+ } |
+} |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// A set of tests checking split map deprecation. |
+// |
+ |
+TEST(ReconfigurePropertySplitMapTransitionsOverflow) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kPropCount; i++) { |
+ map = expectations.AddDataField(map, NONE, Representation::Smi(), any_type); |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ |
+ // Generalize representation of property at index |kSplitProp|. |
+ const int kSplitProp = kPropCount / 2; |
+ Handle<Map> split_map; |
+ Handle<Map> map2 = initial_map; |
+ { |
+ for (int i = 0; i < kSplitProp + 1; i++) { |
+ if (i == kSplitProp) { |
+ split_map = map2; |
+ } |
+ |
+ Handle<String> name = MakeName("prop", i); |
+ int t = map2->SearchTransition(kData, *name, NONE); |
+ CHECK_NE(TransitionArray::kNotFound, t); |
+ map2 = handle(map2->GetTransition(t)); |
+ } |
+ |
+ map2 = Map::ReconfigureProperty(map2, kSplitProp, kData, NONE, |
+ Representation::Double(), any_type, |
+ FORCE_FIELD); |
+ expectations.SetDataField(kSplitProp, Representation::Double(), any_type); |
+ |
+ CHECK(expectations.Check(*split_map, kSplitProp)); |
+ CHECK(expectations.Check(*map2, kSplitProp + 1)); |
+ } |
+ |
+ // At this point |map| should be deprecated and disconnected from the |
+ // transition tree. |
+ CHECK(map->is_deprecated()); |
+ CHECK(!split_map->is_deprecated()); |
+ CHECK(!map2->is_deprecated()); |
+ |
+ // Fill in transition tree of |map2| so that it can't have more transitions. |
+ for (int i = 0; i < TransitionArray::kMaxNumberOfTransitions; i++) { |
+ CHECK(map2->CanHaveMoreTransitions()); |
+ Handle<String> name = MakeName("foo", i); |
+ Map::CopyWithField(map2, name, any_type, NONE, Representation::Smi(), |
+ INSERT_TRANSITION).ToHandleChecked(); |
+ } |
+ CHECK(!map2->CanHaveMoreTransitions()); |
+ |
+ // Try to update |map|, since there is no place for propX transition at |map2| |
+ // |map| should become "copy-generalized". |
+ Handle<Map> updated_map = Map::Update(map); |
+ CHECK(updated_map->GetBackPointer()->IsUndefined()); |
+ |
+ for (int i = 0; i < kPropCount; i++) { |
+ expectations.SetDataField(i, Representation::Tagged(), any_type); |
+ } |
+ CHECK(expectations.Check(*updated_map)); |
+} |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// A set of tests involving special transitions (such as elements kind |
+// transition, observed transition or prototype transition). |
+// |
+ |
+// This test ensures that representation/field type generalization is correctly |
+// propagated from one branch of transition tree (|map2|) to another (|map|). |
+// |
+// p4B: |map2| |
+// | |
+// * - special transition |
+// | |
+// {} - p0 - p1 - p2A - p3 - p4A: |map| |
+// |
+// where "p4A" and "p4B" are exactly the same properties. |
+// |
+// TODO(ishell): unify this test template with |
+// TestReconfigureDataFieldAttribute_GeneralizeRepresentation once |
+// IS_PROTO_TRANS_ISSUE_FIXED and IS_NON_EQUIVALENT_TRANSITION_SUPPORTED are |
+// fixed. |
+template <typename TestConfig> |
+static void TestGeneralizeRepresentationWithSpecialTransition( |
+ TestConfig& config, Representation from_representation, |
+ Handle<HeapType> from_type, Representation to_representation, |
+ Handle<HeapType> to_type, Representation expected_representation, |
+ Handle<HeapType> expected_type) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kPropCount; i++) { |
+ map = expectations.AddDataField(map, NONE, from_representation, from_type); |
+ } |
+ CHECK(!map->is_deprecated()); |
+ CHECK(map->is_stable()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ // Apply some special transition to |map|. |
+ CHECK(map->owns_descriptors()); |
+ Handle<Map> map2 = config.Transition(map); |
+ |
+ // |map| should still match expectations. |
+ CHECK(!map->is_deprecated()); |
+ CHECK(expectations.Check(*map)); |
+ |
+ Expectations expectations2 = expectations; |
+ if (config.generalizes_representations()) { |
+ for (int i = 0; i < kPropCount; i++) { |
+ expectations2.GeneralizeRepresentation(i); |
+ } |
+ } |
+ |
+ CHECK(!map2->is_deprecated()); |
+ CHECK(map2->is_stable()); |
+ CHECK(expectations2.Check(*map2)); |
+ |
+ // Create new maps by generalizing representation of propX field. |
+ Handle<Map> maps[kPropCount]; |
+ for (int i = 0; i < kPropCount; i++) { |
+ Handle<Map> new_map = Map::ReconfigureProperty( |
+ map, i, kData, NONE, to_representation, to_type, FORCE_FIELD); |
+ maps[i] = new_map; |
+ |
+ expectations.SetDataField(i, expected_representation, expected_type); |
+ |
+ CHECK(map->is_deprecated()); |
+ CHECK_NE(*map, *new_map); |
+ CHECK(i == 0 || maps[i - 1]->is_deprecated()); |
+ CHECK(expectations.Check(*new_map)); |
+ |
+ Handle<Map> new_map2 = Map::Update(map2); |
+ CHECK(!new_map2->is_deprecated()); |
+ CHECK(!new_map2->is_dictionary_map()); |
+ |
+ if (!IS_NON_EQUIVALENT_TRANSITION_SUPPORTED) { |
+ // In case of non-equivalent transition currently we generalize all |
+ // representations. |
+ for (int i = 0; i < kPropCount; i++) { |
+ expectations2.GeneralizeRepresentation(i); |
+ } |
+ CHECK(new_map2->GetBackPointer()->IsUndefined()); |
+ CHECK(expectations2.Check(*new_map2)); |
+ } else { |
+ CHECK(expectations.Check(*new_map2)); |
+ } |
+ } |
+ |
+ Handle<Map> active_map = maps[kPropCount - 1]; |
+ CHECK(!active_map->is_deprecated()); |
+ |
+ // Update all deprecated maps and check that they are now the same. |
+ CHECK_EQ(*active_map, *Map::Update(map)); |
+ for (int i = 0; i < kPropCount; i++) { |
+ CHECK_EQ(*active_map, *Map::Update(maps[i])); |
+ } |
+} |
+ |
+ |
+TEST(ElementsKindTransitionFromMapOwningDescriptor) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ struct TestConfig { |
+ Handle<Map> Transition(Handle<Map> map) { |
+ return Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS, |
+ INSERT_TRANSITION); |
+ } |
+ // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed. |
+ bool generalizes_representations() const { return false; } |
+ }; |
+ TestConfig config; |
+ TestGeneralizeRepresentationWithSpecialTransition( |
+ config, Representation::Smi(), any_type, Representation::HeapObject(), |
+ value_type, Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(ElementsKindTransitionFromMapNotOwningDescriptor) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ struct TestConfig { |
+ Handle<Map> Transition(Handle<Map> map) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ // Add one more transition to |map| in order to prevent descriptors |
+ // ownership. |
+ CHECK(map->owns_descriptors()); |
+ Map::CopyWithField(map, MakeString("foo"), any_type, NONE, |
+ Representation::Smi(), |
+ INSERT_TRANSITION).ToHandleChecked(); |
+ CHECK(!map->owns_descriptors()); |
+ |
+ return Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS, |
+ INSERT_TRANSITION); |
+ } |
+ // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed. |
+ bool generalizes_representations() const { return false; } |
+ }; |
+ TestConfig config; |
+ TestGeneralizeRepresentationWithSpecialTransition( |
+ config, Representation::Smi(), any_type, Representation::HeapObject(), |
+ value_type, Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(ForObservedTransitionFromMapOwningDescriptor) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ struct TestConfig { |
+ Handle<Map> Transition(Handle<Map> map) { |
+ return Map::CopyForObserved(map); |
+ } |
+ // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed. |
+ bool generalizes_representations() const { return false; } |
+ }; |
+ TestConfig config; |
+ TestGeneralizeRepresentationWithSpecialTransition( |
+ config, Representation::Smi(), any_type, Representation::HeapObject(), |
+ value_type, Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(ForObservedTransitionFromMapNotOwningDescriptor) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ struct TestConfig { |
+ Handle<Map> Transition(Handle<Map> map) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ // Add one more transition to |map| in order to prevent descriptors |
+ // ownership. |
+ CHECK(map->owns_descriptors()); |
+ Map::CopyWithField(map, MakeString("foo"), any_type, NONE, |
+ Representation::Smi(), |
+ INSERT_TRANSITION).ToHandleChecked(); |
+ CHECK(!map->owns_descriptors()); |
+ |
+ return Map::CopyForObserved(map); |
+ } |
+ // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed. |
+ bool generalizes_representations() const { return false; } |
+ }; |
+ TestConfig config; |
+ TestGeneralizeRepresentationWithSpecialTransition( |
+ config, Representation::Smi(), any_type, Representation::HeapObject(), |
+ value_type, Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(PrototypeTransitionFromMapOwningDescriptor) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ struct TestConfig { |
+ Handle<JSObject> prototype_; |
+ |
+ TestConfig() { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0)); |
+ } |
+ |
+ Handle<Map> Transition(Handle<Map> map) { |
+ return Map::TransitionToPrototype(map, prototype_, REGULAR_PROTOTYPE); |
+ } |
+ // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed. |
+ bool generalizes_representations() const { |
+ return !IS_PROTO_TRANS_ISSUE_FIXED; |
+ } |
+ }; |
+ TestConfig config; |
+ TestGeneralizeRepresentationWithSpecialTransition( |
+ config, Representation::Smi(), any_type, Representation::HeapObject(), |
+ value_type, Representation::Tagged(), any_type); |
+} |
+ |
+ |
+TEST(PrototypeTransitionFromMapNotOwningDescriptor) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<HeapType> value_type = |
+ HeapType::Class(Map::Create(isolate, 0), isolate); |
+ |
+ struct TestConfig { |
+ Handle<JSObject> prototype_; |
+ |
+ TestConfig() { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ prototype_ = factory->NewJSObjectFromMap(Map::Create(isolate, 0)); |
+ } |
+ |
+ Handle<Map> Transition(Handle<Map> map) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ // Add one more transition to |map| in order to prevent descriptors |
+ // ownership. |
+ CHECK(map->owns_descriptors()); |
+ Map::CopyWithField(map, MakeString("foo"), any_type, NONE, |
+ Representation::Smi(), |
+ INSERT_TRANSITION).ToHandleChecked(); |
+ CHECK(!map->owns_descriptors()); |
+ |
+ return Map::TransitionToPrototype(map, prototype_, REGULAR_PROTOTYPE); |
+ } |
+ // TODO(ishell): remove once IS_PROTO_TRANS_ISSUE_FIXED is removed. |
+ bool generalizes_representations() const { |
+ return !IS_PROTO_TRANS_ISSUE_FIXED; |
+ } |
+ }; |
+ TestConfig config; |
+ TestGeneralizeRepresentationWithSpecialTransition( |
+ config, Representation::Smi(), any_type, Representation::HeapObject(), |
+ value_type, Representation::Tagged(), any_type); |
+} |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// A set of tests for higher level transitioning mechanics. |
+// |
+ |
+struct TransitionToDataFieldOperator { |
+ Representation representation_; |
+ PropertyAttributes attributes_; |
+ Handle<HeapType> heap_type_; |
+ Handle<Object> value_; |
+ |
+ TransitionToDataFieldOperator(Representation representation, |
+ Handle<HeapType> heap_type, |
+ Handle<Object> value, |
+ PropertyAttributes attributes = NONE) |
+ : representation_(representation), |
+ attributes_(attributes), |
+ heap_type_(heap_type), |
+ value_(value) {} |
+ |
+ Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) { |
+ return expectations.TransitionToDataField(map, attributes_, representation_, |
+ heap_type_, value_); |
+ } |
+}; |
+ |
+ |
+struct TransitionToDataConstantOperator { |
+ PropertyAttributes attributes_; |
+ Handle<JSFunction> value_; |
+ |
+ TransitionToDataConstantOperator(Handle<JSFunction> value, |
+ PropertyAttributes attributes = NONE) |
+ : attributes_(attributes), value_(value) {} |
+ |
+ Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) { |
+ return expectations.TransitionToDataConstant(map, attributes_, value_); |
+ } |
+}; |
+ |
+ |
+struct TransitionToAccessorConstantOperator { |
+ PropertyAttributes attributes_; |
+ Handle<AccessorPair> pair_; |
+ |
+ TransitionToAccessorConstantOperator(Handle<AccessorPair> pair, |
+ PropertyAttributes attributes = NONE) |
+ : attributes_(attributes), pair_(pair) {} |
+ |
+ Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) { |
+ return expectations.TransitionToAccessorConstant(map, attributes_, pair_); |
+ } |
+}; |
+ |
+ |
+struct ReconfigureAsDataPropertyOperator { |
+ int descriptor_; |
+ Representation representation_; |
+ PropertyAttributes attributes_; |
+ Handle<HeapType> heap_type_; |
+ |
+ ReconfigureAsDataPropertyOperator(int descriptor, |
+ Representation representation, |
+ Handle<HeapType> heap_type, |
+ PropertyAttributes attributes = NONE) |
+ : descriptor_(descriptor), |
+ representation_(representation), |
+ attributes_(attributes), |
+ heap_type_(heap_type) {} |
+ |
+ Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) { |
+ expectations.SetDataField(descriptor_, representation_, heap_type_); |
+ return Map::ReconfigureExistingProperty(map, descriptor_, kData, |
+ attributes_); |
+ } |
+}; |
+ |
+ |
+struct ReconfigureAsAccessorPropertyOperator { |
+ int descriptor_; |
+ PropertyAttributes attributes_; |
+ |
+ ReconfigureAsAccessorPropertyOperator(int descriptor, |
+ PropertyAttributes attributes = NONE) |
+ : descriptor_(descriptor), attributes_(attributes) {} |
+ |
+ Handle<Map> DoTransition(Expectations& expectations, Handle<Map> map) { |
+ expectations.SetAccessorField(descriptor_); |
+ return Map::ReconfigureExistingProperty(map, descriptor_, kAccessor, |
+ attributes_); |
+ } |
+}; |
+ |
+ |
+// Checks that representation/field type generalization happened. |
+struct FieldGeneralizationChecker { |
+ int descriptor_; |
+ Representation representation_; |
+ PropertyAttributes attributes_; |
+ Handle<HeapType> heap_type_; |
+ |
+ FieldGeneralizationChecker(int descriptor, Representation representation, |
+ Handle<HeapType> heap_type, |
+ PropertyAttributes attributes = NONE) |
+ : descriptor_(descriptor), |
+ representation_(representation), |
+ attributes_(attributes), |
+ heap_type_(heap_type) {} |
+ |
+ void Check(Expectations& expectations2, Handle<Map> map1, Handle<Map> map2) { |
+ CHECK(!map2->is_deprecated()); |
+ |
+ CHECK(map1->is_deprecated()); |
+ CHECK_NE(*map1, *map2); |
+ CHECK_EQ(*map2, *Map::Update(map1)); |
+ |
+ expectations2.SetDataField(descriptor_, representation_, heap_type_); |
+ CHECK(expectations2.Check(*map2)); |
+ } |
+}; |
+ |
+ |
+// Checks that existing transition was taken as is. |
+struct SameMapChecker { |
+ void Check(Expectations& expectations, Handle<Map> map1, Handle<Map> map2) { |
+ CHECK(!map2->is_deprecated()); |
+ CHECK_EQ(*map1, *map2); |
+ CHECK(expectations.Check(*map2)); |
+ } |
+}; |
+ |
+ |
+// Checks that both |map1| and |map2| should stays non-deprecated, this is |
+// the case when property kind is change. |
+struct PropertyKindReconfigurationChecker { |
+ void Check(Expectations& expectations, Handle<Map> map1, Handle<Map> map2) { |
+ CHECK(!map1->is_deprecated()); |
+ CHECK(!map2->is_deprecated()); |
+ CHECK_NE(*map1, *map2); |
+ CHECK(expectations.Check(*map2)); |
+ } |
+}; |
+ |
+ |
+// This test transitions to various property types under different |
+// circumstances. |
+// Plan: |
+// 1) create a |map| with p0..p3 properties. |
+// 2) create |map1| by adding "p4" to |map0|. |
+// 3) create |map2| by transition to "p4" from |map0|. |
+// |
+// - p4B: |map2| |
+// / |
+// {} - p0 - p1 - pA - p3: |map| |
+// \ |
+// - p4A: |map1| |
+// |
+// where "p4A" and "p4B" differ only in the attributes. |
+// |
+template <typename TransitionOp1, typename TransitionOp2, typename Checker> |
+static void TestTransitionTo(TransitionOp1& transition_op1, |
+ TransitionOp2& transition_op2, Checker& checker) { |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ Expectations expectations(isolate); |
+ |
+ // Create a map, add required properties to it and initialize expectations. |
+ Handle<Map> initial_map = Map::Create(isolate, 0); |
+ Handle<Map> map = initial_map; |
+ for (int i = 0; i < kPropCount - 1; i++) { |
+ map = expectations.AddDataField(map, NONE, Representation::Smi(), any_type); |
+ } |
+ CHECK(expectations.Check(*map)); |
+ |
+ Expectations expectations1 = expectations; |
+ Handle<Map> map1 = transition_op1.DoTransition(expectations1, map); |
+ CHECK(expectations1.Check(*map1)); |
+ |
+ Expectations expectations2 = expectations; |
+ Handle<Map> map2 = transition_op2.DoTransition(expectations2, map); |
+ |
+ // Let the test customization do the check. |
+ checker.Check(expectations2, map1, map2); |
+} |
+ |
+ |
+TEST(TransitionDataFieldToDataField) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ Handle<Object> value1 = handle(Smi::FromInt(0), isolate); |
+ TransitionToDataFieldOperator transition_op1(Representation::Smi(), any_type, |
+ value1); |
+ |
+ Handle<Object> value2 = isolate->factory()->NewHeapNumber(0); |
+ TransitionToDataFieldOperator transition_op2(Representation::Double(), |
+ any_type, value2); |
+ |
+ FieldGeneralizationChecker checker(kPropCount - 1, Representation::Double(), |
+ any_type); |
+ TestTransitionTo(transition_op1, transition_op2, checker); |
+} |
+ |
+ |
+TEST(TransitionDataConstantToSameDataConstant) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ |
+ Handle<JSFunction> js_func = factory->NewFunction(factory->empty_string()); |
+ TransitionToDataConstantOperator transition_op(js_func); |
+ |
+ SameMapChecker checker; |
+ TestTransitionTo(transition_op, transition_op, checker); |
+} |
+ |
+ |
+TEST(TransitionDataConstantToAnotherDataConstant) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ Handle<JSFunction> js_func1 = factory->NewFunction(factory->empty_string()); |
+ TransitionToDataConstantOperator transition_op1(js_func1); |
+ |
+ Handle<JSFunction> js_func2 = factory->NewFunction(factory->empty_string()); |
+ TransitionToDataConstantOperator transition_op2(js_func2); |
+ |
+ FieldGeneralizationChecker checker(kPropCount - 1, |
+ Representation::HeapObject(), any_type); |
+ TestTransitionTo(transition_op1, transition_op2, checker); |
+} |
+ |
+ |
+TEST(TransitionDataConstantToDataField) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ |
+ Handle<JSFunction> js_func1 = factory->NewFunction(factory->empty_string()); |
+ TransitionToDataConstantOperator transition_op1(js_func1); |
+ |
+ Handle<Object> value2 = isolate->factory()->NewHeapNumber(0); |
+ TransitionToDataFieldOperator transition_op2(Representation::Double(), |
+ any_type, value2); |
+ |
+ FieldGeneralizationChecker checker(kPropCount - 1, Representation::Tagged(), |
+ any_type); |
+ TestTransitionTo(transition_op1, transition_op2, checker); |
+} |
+ |
+ |
+TEST(TransitionAccessorConstantToSameAccessorConstant) { |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ |
+ Handle<AccessorPair> pair = CreateAccessorPair(true, true); |
+ TransitionToAccessorConstantOperator transition_op(pair); |
+ |
+ SameMapChecker checker; |
+ TestTransitionTo(transition_op, transition_op, checker); |
+} |
+ |
+ |
+// TODO(ishell): add this test once IS_ACCESSOR_FIELD_SUPPORTED is supported. |
+// TEST(TransitionAccessorConstantToAnotherAccessorConstant) |