Chromium Code Reviews| 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..b526d85b3723c8adcb100e07116bac3215c2efa2 |
| --- /dev/null |
| +++ b/test/cctest/test-migrations.cc |
| @@ -0,0 +1,2262 @@ |
| +// 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/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 (crbug/448711). |
| +const bool IS_CRBUG_448711_FIXED = false; |
| + |
| + |
| +// TODO(ishell): fix this once TransitionToAccessorProperty is able to always |
| +// keep map in fast mode. |
| +const bool IS_ACCESSOR_FIELD_SUPPORTED = false; |
| + |
| + |
| +// |
| +// 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 = 64; |
| + 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; |
| + } |
| + |
| + 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) { |
| + 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) { |
| + 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)) { |
| + Check(descriptors, i); |
| + return false; |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + bool Check(Map* map) { 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> 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; |
| + } |
| +}; |
| + |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// A set of tests for basic transitioning mechanics. |
| +// |
| + |
| +TEST(TransitionToDataFieldProperty) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Factory* factory = isolate->factory(); |
| + Handle<HeapType> any_type = HeapType::Any(isolate); |
| + |
| + const int kPropCount = 50; |
| + 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; |
| + Handle<Map> intermediate_maps[kPropCount]; |
| + Representation representations[] = { |
| + Representation::Smi(), Representation::Double(), |
| + Representation::HeapObject(), Representation::Tagged()}; |
| + // values[i] must fit into representations[i]. |
| + Handle<Object> values[] = { |
| + handle(Smi::FromInt(0), isolate), factory->NewHeapNumber(0), |
| + factory->NewJSObjectFromMap(Map::Create(isolate, 0)), |
| + factory->undefined_value()}; |
| + const int kValuesCount = arraysize(values); |
| + CHECK_EQ(kValuesCount, arraysize(representations)); |
| + |
| + for (int i = 0; i < kPropCount; i++) { |
| + PropertyAttributes attributes = static_cast<PropertyAttributes>(i % 8); |
| + Representation r = representations[i % kValuesCount]; |
| + CHECK(values[i % kValuesCount]->FitsRepresentation(r)); |
| + |
| + map = expectations.AddDataField(map, attributes, r, any_type); |
| + intermediate_maps[i] = map; |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + for (int i = 0; i < kPropCount; i++) { |
| + CHECK(expectations.Check(*intermediate_maps[i], i + 1)); |
| + } |
| + |
| + // Try to follow same transitions from the |initial_map|. |
| + Handle<Map> map2 = initial_map; |
| + for (int i = 0; i < kPropCount; i++) { |
| + PropertyAttributes attributes = static_cast<PropertyAttributes>(i % 8); |
| + Handle<String> name = MakeName("prop", i); |
| + map2 = Map::TransitionToDataProperty( |
| + map2, name, values[i % kValuesCount], attributes, |
| + Object::CERTAINLY_NOT_STORE_FROM_KEYED); |
| + |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(!map2->is_dictionary_map()); |
| + CHECK_EQ(*intermediate_maps[i], *map2); |
| + CHECK(expectations.Check(*map2, i + 1)); |
| + } |
| +} |
| + |
| + |
| +TEST(TransitionToDataConstantProperty) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Factory* factory = isolate->factory(); |
| + |
| + const int kPropCount = 50; |
| + Expectations expectations(isolate); |
| + |
| + Handle<JSFunction> functions[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + functions[i] = factory->NewFunction(factory->empty_string()); |
| + } |
| + |
| + // Create a map, add required properties to it and initialize expectations. |
| + Handle<Map> initial_map = Map::Create(isolate, 0); |
| + Handle<Map> map = initial_map; |
| + Handle<Map> intermediate_maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + PropertyAttributes attributes = static_cast<PropertyAttributes>(i % 8); |
| + map = expectations.AddDataConstant(map, attributes, functions[i]); |
| + intermediate_maps[i] = map; |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + for (int i = 0; i < kPropCount; i++) { |
| + CHECK(expectations.Check(*intermediate_maps[i], i + 1)); |
| + } |
| + |
| + // Try to follow same transitions from the |initial_map|. |
| + Handle<Map> map2 = initial_map; |
| + for (int i = 0; i < kPropCount; i++) { |
| + PropertyAttributes attributes = static_cast<PropertyAttributes>(i % 8); |
| + Handle<String> name = MakeName("prop", i); |
| + map2 = |
| + Map::TransitionToDataProperty(map2, name, functions[i], attributes, |
| + Object::CERTAINLY_NOT_STORE_FROM_KEYED); |
| + |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(!map2->is_dictionary_map()); |
| + CHECK_EQ(*intermediate_maps[i], *map2); |
| + CHECK(expectations.Check(*map2, i + 1)); |
| + } |
| +} |
| + |
| + |
| +TEST(TransitionToAccessorConstantProperty) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + |
| + const int kPropCount = 50; |
| + Expectations expectations(isolate); |
| + |
| + Handle<AccessorPair> accessor_pairs[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + // Getter only accessors. |
| + accessor_pairs[i] = CreateAccessorPair(true, false); |
| + } |
| + |
| + // Create a map, add required properties to it and initialize expectations. |
| + Handle<Map> initial_map = Map::Create(isolate, 0); |
| + Handle<Map> map = initial_map; |
| + Handle<Map> intermediate_maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + PropertyAttributes attributes = static_cast<PropertyAttributes>(i % 8); |
| + map = expectations.AddAccessorConstant( |
| + map, attributes, handle(accessor_pairs[i]->getter(), isolate), |
| + handle(accessor_pairs[i]->setter(), isolate)); |
| + intermediate_maps[i] = map; |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + for (int i = 0; i < kPropCount; i++) { |
| + CHECK(expectations.Check(*intermediate_maps[i], i + 1)); |
| + } |
| + |
| + // Try to follow same transitions from the |initial_map|. |
| + Handle<Map> map2 = initial_map; |
| + for (int i = 0; i < kPropCount; i++) { |
| + PropertyAttributes attributes = static_cast<PropertyAttributes>(i % 8); |
| + Handle<String> name = MakeName("prop", i); |
| + Handle<Object> getter(accessor_pairs[i]->getter(), isolate); |
| + Handle<Object> setter(accessor_pairs[i]->setter(), isolate); |
| + |
| + map2 = Map::TransitionToAccessorProperty(map2, name, ACCESSOR_GETTER, |
| + getter, attributes); |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(!map2->is_dictionary_map()); |
| + |
| + map2 = Map::TransitionToAccessorProperty(map2, name, ACCESSOR_SETTER, |
| + setter, attributes); |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(!map2->is_dictionary_map()); |
| + CHECK_EQ(*intermediate_maps[i], *map2); |
| + CHECK(expectations.Check(*map2, i + 1)); |
| + } |
| +} |
| + |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// A set of tests for representation generalization case. |
| +// |
| + |
| +TEST(GeneralizeRepresentationSmiToDouble) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Handle<HeapType> any_type = HeapType::Any(isolate); |
| + |
| + const int kPropCount = 5; |
| + 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()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to double. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + 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])); |
| + } |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to heap |
| + // object. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty(map, i, kData, NONE, |
| + Representation::HeapObject(), |
| + value_type, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField(i, Representation::Tagged(), 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])); |
| + } |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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::Double(), |
| + any_type); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to heap |
| + // object. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty(map, i, kData, NONE, |
| + Representation::HeapObject(), |
| + value_type, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField(i, Representation::Tagged(), 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])); |
| + } |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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::HeapObject(), |
| + value_type); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to smi. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty( |
| + map, i, kData, NONE, Representation::Smi(), any_type, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField(i, Representation::Tagged(), 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])); |
| + } |
| +} |
|
Toon Verwaest
2015/01/30 13:02:53
Seems like you could merge the few tests above int
|
| + |
| + |
| +TEST(GeneralizeRepresentationHeapObjectToHeapObject) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Handle<HeapType> value_type = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 5; |
| + 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::HeapObject(), |
| + value_type); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create some value types. |
| + static const int kAdditionalValueTypesCount = 4; |
| + Handle<HeapType> value_types[kAdditionalValueTypesCount]; |
| + for (int i = 0; i < kAdditionalValueTypesCount; i++) { |
| + Handle<Map> value_map = Map::Create(isolate, 0); |
| + CHECK(value_map->is_stable()); |
| + value_types[i] = HeapType::Class(value_map, isolate); |
| + } |
| + |
| + Zone zone; |
| + |
| + // Create new maps by generalizing representation of propX field to various |
| + // HeapObject types with stable maps. |
| + for (int val_index = 0; val_index < kAdditionalValueTypesCount; val_index++) { |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> field_owner(map->FindFieldOwner(i), isolate); |
| + CompilationInfo info(isolate, &zone); |
| + CHECK(!info.HasAbortedDueToDependencyChange()); |
| + |
| + Map::AddDependentCompilationInfo(field_owner, |
| + DependentCode::kFieldTypeGroup, &info); |
| + |
| + Handle<Map> new_map = Map::ReconfigureProperty( |
| + map, i, kData, NONE, Representation::HeapObject(), |
| + value_types[val_index], FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField( |
| + i, Representation::HeapObject(), |
| + HeapType::Union(value_types[val_index], expectations.GetFieldType(i), |
| + isolate)); |
| + |
| + // Maps should stay the same but field type modification should trigger |
| + // respective dependents to deoptimize. |
| + CHECK_EQ(*map, *new_map); |
| + CHECK(info.HasAbortedDueToDependencyChange()); |
| + info.RollbackDependencies(); // Properly cleanup compilation info. |
| + |
| + 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])); |
| + } |
| + } |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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::None(), none_type); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to smi. |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty( |
| + map, i, kData, NONE, Representation::Smi(), any_type, FORCE_FIELD); |
| + |
| + expectations.SetDataField(i, Representation::Smi(), any_type); |
| + |
| + CHECK_EQ(*map, *new_map); // None -> Smi change is trivial. |
| + CHECK(!new_map->is_deprecated()); |
| + CHECK(!new_map->is_dictionary_map()); |
| + CHECK(expectations.Check(*new_map)); |
| + } |
| + |
| + CHECK_EQ(*map, *Map::Update(map)); |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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::None(), none_type); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to heap |
| + // object. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + 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()); // None -> Double change is NOT trivial. |
| + 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])); |
| + } |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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::None(), none_type); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to heap |
| + // object. |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty(map, i, kData, NONE, |
| + Representation::HeapObject(), |
| + value_type, FORCE_FIELD); |
| + |
| + expectations.SetDataField(i, Representation::HeapObject(), value_type); |
| + |
| + CHECK_EQ(*map, *new_map); // None -> HeapObject change is trivial. |
| + CHECK(!new_map->is_deprecated()); |
| + CHECK(!new_map->is_dictionary_map()); |
| + CHECK(expectations.Check(*new_map)); |
| + } |
| + |
| + CHECK_EQ(*map, *Map::Update(map)); |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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::None(), none_type); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to heap |
| + // object. |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty( |
| + map, i, kData, NONE, Representation::Tagged(), any_type, FORCE_FIELD); |
| + |
| + expectations.SetDataField(i, Representation::Tagged(), any_type); |
| + |
| + CHECK_EQ(*map, *new_map); // None -> Tagged change is trivial. |
| + CHECK(!new_map->is_deprecated()); |
| + CHECK(!new_map->is_dictionary_map()); |
| + CHECK(expectations.Check(*new_map)); |
| + } |
| + |
| + CHECK_EQ(*map, *Map::Update(map)); |
| +} |
| + |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// 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 kPropCount = 5; |
| + 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 to double. |
| + 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 property location change case. |
| +// |
| + |
| +TEST(ReconfigureDataConstantToDataField) { |
| + 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_func = factory->NewFunction(factory->empty_string()); |
| + |
| + const int kPropCount = 5; |
| + 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.AddDataConstant(map, NONE, js_func); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by forcing propX to become a field. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty(map, i, kData, NONE, |
| + Representation::HeapObject(), |
| + any_type, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField(i, Representation::HeapObject(), 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])); |
| + } |
| +} |
| + |
| + |
| +TEST(ReconfigureAccessorConstantToAccessorField) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Factory* factory = isolate->factory(); |
| + Handle<JSFunction> getter = factory->NewFunction(factory->empty_string()); |
| + Handle<JSFunction> setter = factory->NewFunction(factory->empty_string()); |
| + |
| + const int kPropCount = 5; |
| + Expectations expectations(isolate); |
| + |
| + Handle<AccessorPair> accessor_pairs[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + // Getter only accessors. |
| + accessor_pairs[i] = CreateAccessorPair(true, false); |
| + } |
| + |
| + // Create a map, add required properties to it and initialize expectations. |
| + Handle<Map> initial_map = Map::Create(isolate, 0); |
| + Handle<Map> map = initial_map; |
| + Handle<Map> intermediate_maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + map = expectations.AddAccessorConstant( |
| + map, NONE, handle(accessor_pairs[i]->getter(), isolate), |
| + handle(accessor_pairs[i]->setter(), isolate)); |
| + intermediate_maps[i] = map; |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + for (int i = 0; i < kPropCount; i++) { |
| + CHECK(expectations.Check(*intermediate_maps[i], i + 1)); |
| + } |
| + |
| + // Try to follow same transitions from the |initial_map|. |
| + Handle<Map> map2 = initial_map; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<String> name = MakeName("prop", i); |
| + map2 = Map::TransitionToAccessorProperty(map2, name, ACCESSOR_GETTER, |
| + getter, NONE); |
| + if (IS_ACCESSOR_FIELD_SUPPORTED) { |
| + CHECK(!map2->is_dictionary_map()); |
| + } else { |
| + CHECK(map2->is_dictionary_map()); |
| + } |
| + |
| + map2 = Map::TransitionToAccessorProperty(map2, name, ACCESSOR_SETTER, |
| + setter, NONE); |
| + if (IS_ACCESSOR_FIELD_SUPPORTED) { |
| + CHECK(!map2->is_dictionary_map()); |
| + CHECK(expectations.Check(*map2, i + 1)); |
| + } else { |
| + CHECK(map2->is_dictionary_map()); |
| + } |
| + } |
| + |
| + if (!IS_ACCESSOR_FIELD_SUPPORTED) { |
| + CHECK(map2->is_dictionary_map()); |
| + } |
| +} |
| + |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// A set of tests for attribute reconfiguration case. |
| +// |
| + |
| +TEST(ReconfigureDataFieldAttribute_GeneralizeRepresentationSmiToDouble) { |
|
Toon Verwaest
2015/01/30 13:02:53
Same here?
|
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Handle<HeapType> any_type = HeapType::Any(isolate); |
| + |
| + const int kPropCount = 5; |
| + 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()); |
| + 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, Representation::Smi(), |
| + any_type); |
| + } |
| + map2 = expectations2.AddDataField(map2, READ_ONLY, Representation::Double(), |
| + any_type); |
| + |
| + for (int i = kSplitProp + 1; i < kPropCount; i++) { |
| + map2 = expectations2.AddDataField(map2, NONE, Representation::Double(), |
| + 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)); |
| + |
| + // |map| should be deprecated and |new_map| should match new expectations. |
| + for (int i = kSplitProp; i < kPropCount; i++) { |
| + expectations.SetDataField(i, Representation::Double(), any_type); |
| + } |
| + 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)); |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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()); |
| + 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, Representation::Smi(), |
| + any_type); |
| + } |
| + map2 = expectations2.AddDataField(map2, READ_ONLY, |
| + Representation::HeapObject(), value_type); |
| + |
| + for (int i = kSplitProp + 1; i < kPropCount; i++) { |
| + map2 = expectations2.AddDataField(map2, NONE, Representation::HeapObject(), |
| + value_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)); |
| + |
| + // |map| should be deprecated and |new_map| should match new expectations. |
| + for (int i = kSplitProp; i < kPropCount; i++) { |
| + expectations.SetDataField(i, Representation::Tagged(), any_type); |
| + } |
| + 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)); |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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::Double(), |
| + any_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, Representation::Double(), any_type); |
| + } |
| + map2 = expectations2.AddDataField(map2, READ_ONLY, |
| + Representation::HeapObject(), value_type); |
| + |
| + for (int i = kSplitProp + 1; i < kPropCount; i++) { |
| + map2 = expectations2.AddDataField(map2, NONE, Representation::HeapObject(), |
| + value_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)); |
| + |
| + // |map| should be deprecated and |new_map| should match new expectations. |
| + for (int i = kSplitProp; i < kPropCount; i++) { |
| + expectations.SetDataField(i, Representation::Tagged(), any_type); |
| + } |
| + 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)); |
| +} |
| + |
| + |
| +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); |
| + |
| + const int kPropCount = 5; |
| + 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::HeapObject(), |
| + value_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, Representation::HeapObject(), value_type); |
| + } |
| + map2 = expectations2.AddDataField(map2, READ_ONLY, Representation::Smi(), |
| + any_type); |
| + |
| + for (int i = kSplitProp + 1; i < kPropCount; i++) { |
| + map2 = |
| + expectations2.AddDataField(map2, NONE, Representation::Smi(), 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)); |
| + |
| + // |map| should be deprecated and |new_map| should match new expectations. |
| + for (int i = kSplitProp; i < kPropCount; i++) { |
| + expectations.SetDataField(i, Representation::Tagged(), any_type); |
| + } |
| + 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)); |
| +} |
| + |
| + |
| +TEST(ReconfigureDataFieldAttribute_SameDataConstantAfterTargetMap) { |
| + 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_func = factory->NewFunction(factory->empty_string()); |
| + Handle<HeapType> value_type = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 7; |
| + const int kConstantPropIndex = 5; |
| + 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 == kConstantPropIndex) { |
| + map = expectations.AddDataConstant(map, NONE, js_func); |
| + |
| + } else { |
| + map = |
| + expectations.AddDataField(map, NONE, Representation::Smi(), any_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 = 2; |
| + Expectations expectations2(isolate); |
| + |
| + Handle<Map> map2 = initial_map; |
| + for (int i = 0; i < kSplitProp; i++) { |
| + map2 = expectations2.FollowDataTransition(map2, NONE, Representation::Smi(), |
| + any_type); |
| + } |
| + map2 = expectations2.AddDataField(map2, READ_ONLY, |
| + Representation::HeapObject(), value_type); |
| + |
| + for (int i = kSplitProp + 1; i < kPropCount; i++) { |
| + if (i == kConstantPropIndex) { |
| + map2 = expectations2.AddDataConstant(map2, NONE, js_func); |
| + |
| + } else { |
| + map2 = expectations2.AddDataField(map2, NONE, Representation::Smi(), |
| + 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)); |
| + |
| + // |map| should be deprecated and |new_map| should match new expectations. |
| + expectations.SetDataField(kSplitProp, Representation::Tagged(), any_type); |
| + |
| + 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)); |
| +} |
| + |
| + |
| +TEST(ReconfigureDataFieldAttribute_DataConstantToDataFieldAfterTargetMap) { |
| + 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_func = factory->NewFunction(factory->empty_string()); |
| + Handle<JSFunction> js_func2 = factory->NewFunction(factory->empty_string()); |
| + Handle<HeapType> value_type = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 7; |
| + const int kConstantPropIndex = 5; |
| + 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 == kConstantPropIndex) { |
| + map = expectations.AddDataConstant(map, NONE, js_func); |
| + |
| + } else { |
| + map = |
| + expectations.AddDataField(map, NONE, Representation::Smi(), any_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 = 2; |
| + Expectations expectations2(isolate); |
| + |
| + Handle<Map> map2 = initial_map; |
| + for (int i = 0; i < kSplitProp; i++) { |
| + map2 = expectations2.FollowDataTransition(map2, NONE, Representation::Smi(), |
| + any_type); |
| + } |
| + map2 = expectations2.AddDataField(map2, READ_ONLY, |
| + Representation::HeapObject(), value_type); |
| + |
| + for (int i = kSplitProp + 1; i < kPropCount; i++) { |
| + if (i == kConstantPropIndex) { |
| + map2 = expectations2.AddDataConstant(map2, NONE, js_func2); |
| + |
| + } else { |
| + map2 = expectations2.AddDataField(map2, NONE, Representation::Smi(), |
| + 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)); |
| + |
| + // |map| should be deprecated and |new_map| should match new expectations. |
| + expectations.SetDataField(kSplitProp, Representation::Tagged(), any_type); |
| + expectations.SetDataField(kConstantPropIndex, Representation::HeapObject(), |
| + any_type); |
| + |
| + 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)); |
| +} |
| + |
| + |
| +TEST(ReconfigureDataFieldAttribute_SameAccessorConstantAfterTargetMap) { |
| + 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); |
| + Handle<HeapType> value_type = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 7; |
| + const int kConstantPropIndex = 5; |
| + 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 == kConstantPropIndex) { |
| + 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 another branch in transition tree (property at index |kSplitProp| |
| + // has different attributes), initialize expectations. |
| + const int kSplitProp = 2; |
| + Expectations expectations2(isolate); |
| + |
| + Handle<Map> map2 = initial_map; |
| + for (int i = 0; i < kSplitProp; i++) { |
| + map2 = expectations2.FollowDataTransition(map2, NONE, Representation::Smi(), |
| + any_type); |
| + } |
| + map2 = expectations2.AddDataField(map2, READ_ONLY, |
| + Representation::HeapObject(), value_type); |
| + |
| + for (int i = kSplitProp + 1; i < kPropCount; i++) { |
| + if (i == kConstantPropIndex) { |
| + map2 = expectations2.AddAccessorConstant(map2, NONE, pair); |
| + |
| + } else { |
| + map2 = expectations2.AddDataField(map2, NONE, Representation::Smi(), |
| + 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)); |
| + |
| + // |map| should be deprecated and |new_map| should match new expectations. |
| + expectations.SetDataField(kSplitProp, Representation::Tagged(), any_type); |
| + |
| + 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)); |
| +} |
| + |
| + |
| +TEST(ReconfigureDataFieldAttribute_AccConstantToAccFieldAfterTargetMap) { |
| + 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); |
| + Handle<AccessorPair> pair2 = CreateAccessorPair(true, true); |
| + Handle<HeapType> value_type = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 7; |
| + const int kConstantPropIndex = 5; |
| + 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 == kConstantPropIndex) { |
| + 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 another branch in transition tree (property at index |kSplitProp| |
| + // has different attributes), initialize expectations. |
| + const int kSplitProp = 2; |
| + Expectations expectations2(isolate); |
| + |
| + Handle<Map> map2 = initial_map; |
| + for (int i = 0; i < kSplitProp; i++) { |
| + map2 = expectations2.FollowDataTransition(map2, NONE, Representation::Smi(), |
| + any_type); |
| + } |
| + map2 = expectations2.AddDataField(map2, READ_ONLY, |
| + Representation::HeapObject(), value_type); |
| + |
| + for (int i = kSplitProp + 1; i < kPropCount; i++) { |
| + if (i == kConstantPropIndex) { |
| + map2 = expectations2.AddAccessorConstant(map2, NONE, pair2); |
| + |
| + } else { |
| + map2 = expectations2.AddDataField(map2, NONE, Representation::Smi(), |
| + 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)); |
| + |
| + // |map| should be deprecated and |new_map| should match new expectations. |
| + if (IS_ACCESSOR_FIELD_SUPPORTED) { |
| + expectations.SetDataField(kSplitProp, Representation::Tagged(), any_type); |
| + expectations.SetAccessorField(kConstantPropIndex); |
| + |
| + 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)); |
| + } else { |
| + // Currently we have a copy-generalize all representations case. |
| + CHECK(new_map->GetBackPointer()->IsUndefined()); |
| + for (int i = 0; i < kPropCount; i++) { |
| + if (i == kConstantPropIndex) { |
| + expectations.SetAccessorConstant(i, pair2); |
| + |
| + } else { |
| + expectations.SetDataField(i, Representation::Tagged(), any_type); |
| + } |
| + } |
| + 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)); |
| + } |
| +} |
| + |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// 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); |
| + |
| + const int kPropCount = 5; |
| + 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. |
| +// |
| + |
| +TEST(ElementsKindTransitionFromMapOwningDescriptor) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Handle<HeapType> value_type1 = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + Handle<HeapType> value_type2 = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 5; |
| + 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::HeapObject(), |
| + value_type1); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + CHECK(map->owns_descriptors()); |
| + |
| + // Create an elements kind transition, it should always match |expectations|. |
| + Handle<Map> map2 = |
| + Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS, INSERT_TRANSITION); |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(map2->is_stable()); |
| + CHECK(expectations.Check(*map2)); |
| + |
| + // |map1| should still match expectations. |
| + CHECK(!map->is_deprecated()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to various |
| + // HeapObject types with stable maps. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty(map, i, kData, NONE, |
| + Representation::HeapObject(), |
| + value_type2, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField( |
| + i, Representation::HeapObject(), |
| + HeapType::Union(value_type2, expectations.GetFieldType(i), isolate)); |
| + |
| + // Maps should stay the same but field type modification should trigger |
| + // respective dependents to deoptimize. |
| + CHECK_EQ(*map, *new_map); |
| + CHECK(!new_map->is_deprecated()); |
| + CHECK(!new_map->is_dictionary_map()); |
| + CHECK(expectations.Check(*new_map)); |
| + |
| + if (IS_NON_EQUIVALENT_TRANSITION_SUPPORTED) { |
| + Handle<Map> new_map2 = Map::Update(map2); |
| + CHECK(!new_map2->is_deprecated()); |
| + CHECK(!new_map2->is_dictionary_map()); |
| + 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(ElementsKindTransitionFromMapNotOwningDescriptor) { |
| + if (!IS_CRBUG_448711_FIXED) return; |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Handle<HeapType> any_type = HeapType::Any(isolate); |
| + Handle<HeapType> value_type1 = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + Handle<HeapType> value_type2 = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 5; |
| + 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::HeapObject(), |
| + value_type1); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // 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()); |
| + |
| + // Create an elements kind transition, it should always match |expectations|. |
| + Handle<Map> map2 = |
| + Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS, INSERT_TRANSITION); |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(map2->is_stable()); |
| + CHECK(expectations.Check(*map2)); |
| + |
| + // |map1| should still match expectations. |
| + CHECK(!map->is_deprecated()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to various |
| + // HeapObject types with stable maps. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty(map, i, kData, NONE, |
| + Representation::HeapObject(), |
| + value_type2, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField( |
| + i, Representation::HeapObject(), |
| + HeapType::Union(value_type2, expectations.GetFieldType(i), isolate)); |
| + |
| + // Maps should stay the same but field type modification should trigger |
| + // respective dependents to deoptimize. |
| + CHECK_EQ(*map, *new_map); |
| + CHECK(!new_map->is_deprecated()); |
| + CHECK(!new_map->is_dictionary_map()); |
| + CHECK(expectations.Check(*new_map)); |
| + |
| + if (IS_NON_EQUIVALENT_TRANSITION_SUPPORTED) { |
| + Handle<Map> new_map2 = Map::Update(map2); |
| + CHECK(!new_map2->is_deprecated()); |
| + CHECK(!new_map2->is_dictionary_map()); |
| + 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(ForObservedTransitionFromMapOwningDescriptor) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Handle<HeapType> value_type1 = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + Handle<HeapType> value_type2 = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 5; |
| + 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::HeapObject(), |
| + value_type1); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + CHECK(map->owns_descriptors()); |
| + |
| + // Create "for-observed" transition, it should always match |expectations|. |
| + Handle<Map> map2 = Map::CopyForObserved(map); |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(map2->is_stable()); |
| + CHECK(expectations.Check(*map2)); |
| + |
| + // |map1| should still match expectations. |
| + CHECK(!map->is_deprecated()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to various |
| + // HeapObject types with stable maps. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty(map, i, kData, NONE, |
| + Representation::HeapObject(), |
| + value_type2, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField( |
| + i, Representation::HeapObject(), |
| + HeapType::Union(value_type2, expectations.GetFieldType(i), isolate)); |
| + |
| + // Maps should stay the same but field type modification should trigger |
| + // respective dependents to deoptimize. |
| + CHECK_EQ(*map, *new_map); |
| + CHECK(!new_map->is_deprecated()); |
| + CHECK(!new_map->is_dictionary_map()); |
| + CHECK(expectations.Check(*new_map)); |
| + |
| + if (IS_NON_EQUIVALENT_TRANSITION_SUPPORTED) { |
| + Handle<Map> new_map2 = Map::Update(map2); |
| + CHECK(!new_map2->is_deprecated()); |
| + CHECK(!new_map2->is_dictionary_map()); |
| + 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(ForObservedTransitionFromMapNotOwningDescriptor) { |
| + if (!IS_CRBUG_448711_FIXED) return; |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Handle<HeapType> any_type = HeapType::Any(isolate); |
| + Handle<HeapType> value_type1 = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + Handle<HeapType> value_type2 = |
| + HeapType::Class(Map::Create(isolate, 0), isolate); |
| + |
| + const int kPropCount = 5; |
| + 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::HeapObject(), |
| + value_type1); |
| + } |
| + CHECK(!map->is_deprecated()); |
| + CHECK(map->is_stable()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // 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()); |
| + |
| + // Create "for-observed" transition, it should always match |expectations|. |
| + Handle<Map> map2 = Map::CopyForObserved(map); |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(map2->is_stable()); |
| + CHECK(expectations.Check(*map2)); |
| + |
| + // |map1| should still match expectations. |
| + CHECK(!map->is_deprecated()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + // Create new maps by generalizing representation of propX field to various |
| + // HeapObject types with stable maps. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty(map, i, kData, NONE, |
| + Representation::HeapObject(), |
| + value_type2, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + expectations.SetDataField( |
| + i, Representation::HeapObject(), |
| + HeapType::Union(value_type2, expectations.GetFieldType(i), isolate)); |
| + |
| + // Maps should stay the same but field type modification should trigger |
| + // respective dependents to deoptimize. |
| + CHECK_EQ(*map, *new_map); |
| + CHECK(!new_map->is_deprecated()); |
| + CHECK(!new_map->is_dictionary_map()); |
| + CHECK(expectations.Check(*new_map)); |
| + |
| + if (IS_NON_EQUIVALENT_TRANSITION_SUPPORTED) { |
| + Handle<Map> new_map2 = Map::Update(map2); |
| + CHECK(!new_map2->is_deprecated()); |
| + CHECK(!new_map2->is_dictionary_map()); |
| + 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(PrototypeTransitionFromMapOwningDescriptor) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Factory* factory = isolate->factory(); |
| + Handle<HeapType> any_type = HeapType::Any(isolate); |
| + Handle<JSObject> prototype = |
| + factory->NewJSObjectFromMap(Map::Create(isolate, 0)); |
| + |
| + const int kPropCount = 5; |
| + 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()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + CHECK(map->owns_descriptors()); |
| + |
| + // Create prototype transition, it should always match |expectations|. |
| + Handle<Map> map2 = |
| + Map::TransitionToPrototype(map, prototype, REGULAR_PROTOTYPE); |
| + |
| + CHECK(!map2->is_deprecated()); |
| + CHECK(map2->is_stable()); |
| + if (IS_CRBUG_448711_FIXED) { |
| + CHECK(expectations.Check(*map2)); |
| + } |
| + |
| + // |map1| should still match expectations. |
| + CHECK(!map->is_deprecated()); |
| + CHECK(expectations.Check(*map)); |
| + |
| + CHECK_EQ(*map2, |
| + *Map::TransitionToPrototype(map, prototype, REGULAR_PROTOTYPE)); |
| + |
| + // Create new maps by generalizing representation of propX field to double. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + 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)); |
| + |
| + if (IS_NON_EQUIVALENT_TRANSITION_SUPPORTED) { |
| + Handle<Map> new_map2 = Map::Update(map2); |
| + CHECK(!new_map2->is_deprecated()); |
| + CHECK(!new_map2->is_dictionary_map()); |
| + CHECK(expectations.Check(*new_map2)); |
| + } |
| + } |
| +} |
| + |
| + |
| +TEST(GeneralizeRepresentationWithPrototypeTransition) { |
| + CcTest::InitializeVM(); |
| + v8::HandleScope scope(CcTest::isolate()); |
| + Isolate* isolate = CcTest::i_isolate(); |
| + Factory* factory = isolate->factory(); |
| + Handle<HeapType> any_type = HeapType::Any(isolate); |
| + Handle<JSObject> prototype = |
| + factory->NewJSObjectFromMap(Map::Create(isolate, 0)); |
| + Handle<JSFunction> js_func = factory->NewFunction(factory->empty_string()); |
| + |
| + const int kPropCount = 5; |
| + 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.AddDataConstant(map, NONE, js_func); |
| + map = Map::TransitionToPrototype(map, prototype, REGULAR_PROTOTYPE); |
| + if (!IS_CRBUG_448711_FIXED) { |
| + expectations.GeneralizeRepresentation(0); |
| + } |
| + for (int i = 1; i < kPropCount; i++) { |
| + 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 to double. |
| + Handle<Map> maps[kPropCount]; |
| + for (int i = 0; i < kPropCount; i++) { |
| + Handle<Map> new_map = Map::ReconfigureProperty( |
| + map, i, kData, NONE, Representation::Double(), any_type, FORCE_FIELD); |
| + maps[i] = new_map; |
| + |
| + if (!IS_NON_EQUIVALENT_TRANSITION_SUPPORTED) { |
| + // If we reconfigure the property that was added before prototype |
| + // transition, we would get Gen |
| + CHECK(i != 0 || new_map->GetBackPointer()->IsUndefined()); |
| + } |
| + |
| + if (IS_NON_EQUIVALENT_TRANSITION_SUPPORTED && IS_CRBUG_448711_FIXED) { |
| + 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)); |
| + } |
| + } |
| +} |