Commit 35841b50 authored by ishell's avatar ishell Committed by Commit bot

Property reconfiguring implemented.

Previous approach for property reconfiguration was to create a free-floating map with generalized representations of all fields. This patch does it right.

When property is reconfigured either by changing its kind (kData <-> kAccessor) or its attributes it implies creation of a new branch in transition tree. If such a branch already existed before reconfiguration then it should be merged with the old (or source) branch of the transition tree. Merging procedure includes all the heavy machinery such as property location changes (kDescriptor -> kField), field representation/field type generalization, map deprecation, etc.

Review URL: https://codereview.chromium.org/888623002

Cr-Commit-Position: refs/heads/master@{#26667}
parent c5f7d2bb
......@@ -105,8 +105,10 @@ void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
PropertyDetails details(attributes, v8::internal::DATA, 0);
JSObject::SetNormalizedProperty(holder, name(), value, details);
} else {
holder_map_ = Map::ReconfigureDataProperty(holder_map_, descriptor_number(),
attributes);
holder_map_ = Map::ReconfigureExistingProperty(
holder_map_, descriptor_number(), i::kData, attributes);
holder_map_ =
Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
JSObject::MigrateToMap(holder, holder_map_);
}
......
......@@ -58,7 +58,7 @@ PropertyDetails PropertyDetails::AsDeleted() const {
int PropertyDetails::field_width_in_words() const {
DCHECK(type() == DATA);
DCHECK(location() == kField);
if (!FLAG_unbox_double_fields) return 1;
if (kDoubleSize == kPointerSize) return 1;
return representation().IsDouble() ? kDoubleSize / kPointerSize : 1;
......@@ -3148,13 +3148,13 @@ PropertyType DescriptorArray::GetType(int descriptor_number) {
int DescriptorArray::GetFieldIndex(int descriptor_number) {
DCHECK(GetDetails(descriptor_number).type() == DATA);
DCHECK(GetDetails(descriptor_number).location() == kField);
return GetDetails(descriptor_number).field_index();
}
HeapType* DescriptorArray::GetFieldType(int descriptor_number) {
DCHECK(GetDetails(descriptor_number).type() == DATA);
DCHECK(GetDetails(descriptor_number).location() == kField);
return HeapType::cast(GetValue(descriptor_number));
}
......
......@@ -424,6 +424,7 @@ void Map::MapPrint(std::ostream& os) { // NOLINT
if (has_instance_call_handler()) os << " - instance_call_handler\n";
if (is_access_check_needed()) os << " - access_check_needed\n";
if (!is_extensible()) os << " - non-extensible\n";
if (is_observed()) os << " - observed\n";
os << " - back pointer: " << Brief(GetBackPointer());
os << "\n - instance descriptors " << (owns_descriptors() ? "(own) " : "")
<< "#" << NumberOfOwnDescriptors() << ": "
......
......@@ -1269,6 +1269,25 @@ void JSObject::PrintElementsTransition(
}
void Map::PrintReconfiguration(FILE* file, int modify_index, PropertyKind kind,
PropertyAttributes attributes) {
OFStream os(file);
os << "[reconfiguring ";
constructor_name()->PrintOn(file);
os << "] ";
Name* name = instance_descriptors()->GetKey(modify_index);
if (name->IsString()) {
String::cast(name)->PrintOn(file);
} else {
os << "{symbol " << static_cast<void*>(name) << "}";
}
os << ": " << (kind == kData ? "kData" : "ACCESSORS") << ", attrs: ";
os << attributes << " [";
JavaScriptFrame::PrintTop(GetIsolate(), file, false, true);
os << "]\n";
}
void Map::PrintGeneralization(FILE* file,
const char* reason,
int modify_index,
......@@ -1977,15 +1996,19 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
PropertyDetails details = new_descriptors->GetDetails(i);
if (details.type() != DATA) continue;
PropertyDetails old_details = old_descriptors->GetDetails(i);
if (old_details.type() == ACCESSOR_CONSTANT) {
DCHECK(details.representation().IsTagged());
continue;
}
Representation old_representation = old_details.representation();
Representation representation = details.representation();
DCHECK(old_details.type() == DATA_CONSTANT || old_details.type() == DATA);
Handle<Object> value;
if (old_details.type() == DATA_CONSTANT) {
if (old_details.type() == ACCESSOR_CONSTANT) {
// In case of kAccessor -> kData property reconfiguration, the property
// must already be prepared for data or certain type.
DCHECK(!details.representation().IsNone());
if (details.representation().IsDouble()) {
value = isolate->factory()->NewHeapNumber(0, MUTABLE);
} else {
value = isolate->factory()->uninitialized_value();
}
} else if (old_details.type() == DATA_CONSTANT) {
value = handle(old_descriptors->GetValue(i), isolate);
DCHECK(!old_representation.IsDouble() && !representation.IsDouble());
} else {
......@@ -2079,17 +2102,15 @@ int Map::NumberOfFields() {
DescriptorArray* descriptors = instance_descriptors();
int result = 0;
for (int i = 0; i < NumberOfOwnDescriptors(); i++) {
if (descriptors->GetDetails(i).type() == DATA) result++;
if (descriptors->GetDetails(i).location() == kField) result++;
}
return result;
}
Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map,
int modify_index,
StoreMode store_mode,
PropertyAttributes attributes,
const char* reason) {
Handle<Map> Map::CopyGeneralizeAllRepresentations(
Handle<Map> map, int modify_index, StoreMode store_mode, PropertyKind kind,
PropertyAttributes attributes, const char* reason) {
Isolate* isolate = map->GetIsolate();
Handle<DescriptorArray> old_descriptors(map->instance_descriptors(), isolate);
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
......@@ -2110,53 +2131,43 @@ Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map,
MaybeHandle<Name>(), reason, SPECIAL_TRANSITION);
// Unless the instance is being migrated, ensure that modify_index is a field.
PropertyDetails details = descriptors->GetDetails(modify_index);
if (store_mode == FORCE_FIELD &&
(details.type() != DATA || details.attributes() != attributes)) {
int field_index = details.type() == DATA ? details.field_index()
: new_map->NumberOfFields();
DataDescriptor d(handle(descriptors->GetKey(modify_index), isolate),
field_index, attributes, Representation::Tagged());
descriptors->Replace(modify_index, &d);
if (details.type() != DATA) {
int unused_property_fields = new_map->unused_property_fields() - 1;
if (unused_property_fields < 0) {
unused_property_fields += JSObject::kFieldsAdded;
if (modify_index >= 0) {
PropertyDetails details = descriptors->GetDetails(modify_index);
if (store_mode == FORCE_FIELD &&
(details.type() != DATA || details.attributes() != attributes)) {
int field_index = details.type() == DATA ? details.field_index()
: new_map->NumberOfFields();
DataDescriptor d(handle(descriptors->GetKey(modify_index), isolate),
field_index, attributes, Representation::Tagged());
descriptors->Replace(modify_index, &d);
if (details.type() != DATA) {
int unused_property_fields = new_map->unused_property_fields() - 1;
if (unused_property_fields < 0) {
unused_property_fields += JSObject::kFieldsAdded;
}
new_map->set_unused_property_fields(unused_property_fields);
}
new_map->set_unused_property_fields(unused_property_fields);
} else {
DCHECK(details.attributes() == attributes);
}
} else {
DCHECK(details.attributes() == attributes);
}
if (FLAG_trace_generalization) {
HeapType* field_type =
(details.type() == DATA)
? map->instance_descriptors()->GetFieldType(modify_index)
: NULL;
map->PrintGeneralization(
stdout, reason, modify_index, new_map->NumberOfOwnDescriptors(),
new_map->NumberOfOwnDescriptors(),
details.type() == DATA_CONSTANT && store_mode == FORCE_FIELD,
details.representation(), Representation::Tagged(), field_type,
HeapType::Any());
if (FLAG_trace_generalization) {
HeapType* field_type =
(details.type() == DATA)
? map->instance_descriptors()->GetFieldType(modify_index)
: NULL;
map->PrintGeneralization(
stdout, reason, modify_index, new_map->NumberOfOwnDescriptors(),
new_map->NumberOfOwnDescriptors(),
details.type() == DATA_CONSTANT && store_mode == FORCE_FIELD,
details.representation(), Representation::Tagged(), field_type,
HeapType::Any());
}
}
return new_map;
}
// static
Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map,
int modify_index,
StoreMode store_mode,
const char* reason) {
PropertyDetails details =
map->instance_descriptors()->GetDetails(modify_index);
return CopyGeneralizeAllRepresentations(map, modify_index, store_mode,
details.attributes(), reason);
}
void Map::DeprecateTransitionTree() {
if (is_deprecated()) return;
if (HasTransitionArray()) {
......@@ -2172,6 +2183,13 @@ void Map::DeprecateTransitionTree() {
}
static inline bool EqualImmutableValues(Object* obj1, Object* obj2) {
if (obj1 == obj2) return true; // Valid for both kData and kAccessor kinds.
// TODO(ishell): compare AccessorPairs.
return false;
}
// Invalidates a transition target at |key|, and installs |new_descriptors| over
// the current instance_descriptors to ensure proper sharing of descriptor
// arrays.
......@@ -2242,16 +2260,22 @@ Map* Map::FindLastMatchMap(int verbatim,
DescriptorArray* next_descriptors = next->instance_descriptors();
PropertyDetails next_details = next_descriptors->GetDetails(i);
if (details.type() != next_details.type()) break;
if (details.attributes() != next_details.attributes()) break;
DCHECK_EQ(details.kind(), next_details.kind());
DCHECK_EQ(details.attributes(), next_details.attributes());
if (details.location() != next_details.location()) break;
if (!details.representation().Equals(next_details.representation())) break;
if (next_details.type() == DATA) {
if (!descriptors->GetFieldType(i)->NowIs(
next_descriptors->GetFieldType(i))) break;
if (next_details.location() == kField) {
HeapType* next_field_type = next_descriptors->GetFieldType(i);
if (!descriptors->GetFieldType(i)->NowIs(next_field_type)) {
break;
}
} else {
if (descriptors->GetValue(i) != next_descriptors->GetValue(i)) break;
if (!EqualImmutableValues(descriptors->GetValue(i),
next_descriptors->GetValue(i))) {
break;
}
}
current = next;
}
return current;
......@@ -2367,16 +2391,42 @@ void Map::GeneralizeFieldType(Handle<Map> map, int modify_index,
}
// Generalize the representation of the descriptor at |modify_index|.
// This method rewrites the transition tree to reflect the new change. To avoid
// high degrees over polymorphism, and to stabilize quickly, on every rewrite
// the new type is deduced by merging the current type with any potential new
// (partial) version of the type in the transition tree.
static inline Handle<HeapType> GetFieldType(Isolate* isolate,
Handle<DescriptorArray> descriptors,
int descriptor,
PropertyLocation location,
Representation representation) {
#ifdef DEBUG
PropertyDetails details = descriptors->GetDetails(descriptor);
DCHECK_EQ(kData, details.kind());
DCHECK_EQ(details.location(), location);
#endif
if (location == kField) {
return handle(descriptors->GetFieldType(descriptor), isolate);
} else {
return descriptors->GetValue(descriptor)
->OptimalType(isolate, representation);
}
}
// Reconfigures property at |modify_index| with |new_kind|, |new_attributes|,
// |store_mode| and/or |new_representation|/|new_field_type|.
// If |modify_index| is negative then no properties are reconfigured but the
// map is migrated to the up-to-date non-deprecated state.
//
// This method rewrites or completes the transition tree to reflect the new
// change. To avoid high degrees over polymorphism, and to stabilize quickly,
// on every rewrite the new type is deduced by merging the current type with
// any potential new (partial) version of the type in the transition tree.
// To do this, on each rewrite:
// - Search the root of the transition tree using FindRootMap.
// - Find |target_map|, the newest matching version of this map using the keys
// in the |old_map|'s descriptor array to walk the transition tree.
// - Merge/generalize the descriptor array of the |old_map| and |target_map|.
// - Find |target_map|, the newest matching version of this map using the
// virtually "enhanced" |old_map|'s descriptor array (i.e. whose entry at
// |modify_index| is considered to be of |new_kind| and having
// |new_attributes|) to walk the transition tree.
// - Merge/generalize the "enhanced" descriptor array of the |old_map| and
// descriptor array of the |target_map|.
// - Generalize the |modify_index| descriptor using |new_representation| and
// |new_field_type|.
// - Walk the tree again starting from the root towards |target_map|. Stop at
......@@ -2386,68 +2436,113 @@ void Map::GeneralizeFieldType(Handle<Map> map, int modify_index,
// Return it.
// - Otherwise, invalidate the outdated transition target from |target_map|, and
// replace its transition tree with a new branch for the updated descriptors.
Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
int modify_index,
Representation new_representation,
Handle<HeapType> new_field_type,
StoreMode store_mode) {
Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index,
PropertyKind new_kind,
PropertyAttributes new_attributes,
Representation new_representation,
Handle<HeapType> new_field_type,
StoreMode store_mode) {
DCHECK_NE(kAccessor, new_kind); // TODO(ishell): not supported yet.
DCHECK(store_mode != FORCE_FIELD || modify_index >= 0);
Isolate* isolate = old_map->GetIsolate();
Handle<DescriptorArray> old_descriptors(
old_map->instance_descriptors(), isolate);
int old_nof = old_map->NumberOfOwnDescriptors();
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
Representation old_representation = old_details.representation();
// It's fine to transition from None to anything but double without any
// modification to the object, because the default uninitialized value for
// representation None can be overwritten by both smi and tagged values.
// Doubles, however, would require a box allocation.
if (old_representation.IsNone() && !new_representation.IsNone() &&
// If it's just a representation generalization case (i.e. property kind and
// attributes stays unchanged) it's fine to transition from None to anything
// but double without any modification to the object, because the default
// uninitialized value for representation None can be overwritten by both
// smi and tagged values. Doubles, however, would require a box allocation.
if (modify_index >= 0 && !new_representation.IsNone() &&
!new_representation.IsDouble()) {
DCHECK(old_details.type() == DATA);
if (FLAG_trace_generalization) {
old_map->PrintGeneralization(
stdout, "uninitialized field",
modify_index, old_map->NumberOfOwnDescriptors(),
old_map->NumberOfOwnDescriptors(), false,
old_representation, new_representation,
old_descriptors->GetFieldType(modify_index), *new_field_type);
}
Handle<Map> field_owner(old_map->FindFieldOwner(modify_index), isolate);
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
Representation old_representation = old_details.representation();
if (old_representation.IsNone()) {
DCHECK_EQ(new_kind, old_details.kind());
DCHECK_EQ(new_attributes, old_details.attributes());
DCHECK_EQ(DATA, old_details.type());
if (FLAG_trace_generalization) {
old_map->PrintGeneralization(
stdout, "uninitialized field", modify_index,
old_map->NumberOfOwnDescriptors(),
old_map->NumberOfOwnDescriptors(), false, old_representation,
new_representation, old_descriptors->GetFieldType(modify_index),
*new_field_type);
}
Handle<Map> field_owner(old_map->FindFieldOwner(modify_index), isolate);
GeneralizeFieldType(field_owner, modify_index, new_representation,
new_field_type);
DCHECK(old_descriptors->GetDetails(modify_index).representation().Equals(
new_representation));
DCHECK(old_descriptors->GetFieldType(modify_index)->NowIs(new_field_type));
return old_map;
GeneralizeFieldType(field_owner, modify_index, new_representation,
new_field_type);
DCHECK(old_descriptors->GetDetails(modify_index)
.representation()
.Equals(new_representation));
DCHECK(
old_descriptors->GetFieldType(modify_index)->NowIs(new_field_type));
return old_map;
}
}
// Check the state of the root map.
Handle<Map> root_map(old_map->FindRootMap(), isolate);
if (!old_map->EquivalentToForTransition(*root_map)) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
new_kind, new_attributes,
"GenAll_NotEquivalent");
}
int root_nof = root_map->NumberOfOwnDescriptors();
if (modify_index < root_nof) {
if (modify_index >= 0 && modify_index < root_nof) {
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
if (old_details.kind() != new_kind ||
old_details.attributes() != new_attributes) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
new_kind, new_attributes,
"GenAll_RootModification1");
}
if ((old_details.type() != DATA && store_mode == FORCE_FIELD) ||
(old_details.type() == DATA &&
(!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) ||
!new_representation.fits_into(old_details.representation())))) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
"GenAll_RootModification");
new_kind, new_attributes,
"GenAll_RootModification2");
}
}
Handle<Map> target_map = root_map;
for (int i = root_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
int j = target_map->SearchTransition(old_details.kind(),
old_descriptors->GetKey(i),
old_details.attributes());
PropertyKind next_kind;
PropertyLocation next_location;
PropertyAttributes next_attributes;
Representation next_representation;
bool property_kind_reconfiguration = false;
if (modify_index == i) {
DCHECK_EQ(FORCE_FIELD, store_mode);
property_kind_reconfiguration = old_details.kind() != new_kind;
next_kind = new_kind;
next_location = kField;
next_attributes = new_attributes;
// If property kind is not reconfigured merge the result with
// representation/field type from the old descriptor.
next_representation = new_representation;
if (!property_kind_reconfiguration) {
next_representation =
next_representation.generalize(old_details.representation());
}
} else {
next_kind = old_details.kind();
next_location = old_details.location();
next_attributes = old_details.attributes();
next_representation = old_details.representation();
}
int j = target_map->SearchTransition(next_kind, old_descriptors->GetKey(i),
next_attributes);
if (j == TransitionArray::kNotFound) break;
Handle<Map> tmp_map(target_map->GetTransition(j), isolate);
Handle<DescriptorArray> tmp_descriptors = handle(
......@@ -2455,42 +2550,48 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
// Check if target map is incompatible.
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
PropertyType old_type = old_details.type();
PropertyType tmp_type = tmp_details.type();
DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
if ((tmp_type == ACCESSOR_CONSTANT || old_type == ACCESSOR_CONSTANT) &&
(tmp_type != old_type ||
tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) {
DCHECK_EQ(next_kind, tmp_details.kind());
DCHECK_EQ(next_attributes, tmp_details.attributes());
if (next_kind == kAccessor &&
!EqualImmutableValues(old_descriptors->GetValue(i),
tmp_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
new_kind, new_attributes,
"GenAll_Incompatible");
}
Representation old_representation = old_details.representation();
if (next_location == kField && tmp_details.location() == kDescriptor) break;
Representation tmp_representation = tmp_details.representation();
if (!old_representation.fits_into(tmp_representation) ||
(!new_representation.fits_into(tmp_representation) &&
modify_index == i)) {
break;
}
if (tmp_type == DATA) {
// Generalize the field type as necessary.
Handle<HeapType> old_field_type =
(old_type == DATA) ? handle(old_descriptors->GetFieldType(i), isolate)
: old_descriptors->GetValue(i)
->OptimalType(isolate, tmp_representation);
if (modify_index == i) {
old_field_type = GeneralizeFieldType(
new_field_type, old_field_type, isolate);
}
GeneralizeFieldType(tmp_map, i, tmp_representation, old_field_type);
} else if (tmp_type == DATA_CONSTANT) {
if (old_type != DATA_CONSTANT ||
old_descriptors->GetConstant(i) != tmp_descriptors->GetConstant(i)) {
break;
if (!next_representation.fits_into(tmp_representation)) break;
PropertyLocation old_location = old_details.location();
PropertyLocation tmp_location = tmp_details.location();
if (tmp_location == kField) {
if (next_kind == kData) {
Handle<HeapType> next_field_type;
if (modify_index == i) {
next_field_type = new_field_type;
if (!property_kind_reconfiguration) {
Handle<HeapType> old_field_type =
GetFieldType(isolate, old_descriptors, i,
old_details.location(), tmp_representation);
next_field_type =
GeneralizeFieldType(next_field_type, old_field_type, isolate);
}
} else {
Handle<HeapType> old_field_type =
GetFieldType(isolate, old_descriptors, i, old_details.location(),
tmp_representation);
next_field_type = old_field_type;
}
GeneralizeFieldType(tmp_map, i, tmp_representation, next_field_type);
}
} else {
DCHECK_EQ(tmp_type, old_type);
DCHECK_EQ(tmp_descriptors->GetValue(i), old_descriptors->GetValue(i));
} else if (old_location == kField ||
!EqualImmutableValues(old_descriptors->GetValue(i),
tmp_descriptors->GetValue(i))) {
break;
}
DCHECK(!tmp_map->is_deprecated());
target_map = tmp_map;
}
......@@ -2500,37 +2601,55 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
int target_nof = target_map->NumberOfOwnDescriptors();
if (target_nof == old_nof &&
(store_mode != FORCE_FIELD ||
target_descriptors->GetDetails(modify_index).type() == DATA)) {
DCHECK(modify_index < target_nof);
DCHECK(new_representation.fits_into(
target_descriptors->GetDetails(modify_index).representation()));
DCHECK(
target_descriptors->GetDetails(modify_index).type() != DATA ||
new_field_type->NowIs(target_descriptors->GetFieldType(modify_index)));
(modify_index >= 0 &&
target_descriptors->GetDetails(modify_index).location() == kField))) {
#ifdef DEBUG
if (modify_index >= 0) {
PropertyDetails details = target_descriptors->GetDetails(modify_index);
DCHECK_EQ(new_kind, details.kind());
DCHECK_EQ(new_attributes, details.attributes());
DCHECK(new_representation.fits_into(details.representation()));
DCHECK(details.location() != kField ||
new_field_type->NowIs(
target_descriptors->GetFieldType(modify_index)));
}
#endif
return target_map;
}
// Find the last compatible target map in the transition tree.
for (int i = target_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
int j = target_map->SearchTransition(old_details.kind(),
old_descriptors->GetKey(i),
old_details.attributes());
PropertyKind next_kind;
PropertyAttributes next_attributes;
if (modify_index == i) {
next_kind = new_kind;
next_attributes = new_attributes;
} else {
next_kind = old_details.kind();
next_attributes = old_details.attributes();
}
int j = target_map->SearchTransition(next_kind, old_descriptors->GetKey(i),
next_attributes);
if (j == TransitionArray::kNotFound) break;
Handle<Map> tmp_map(target_map->GetTransition(j), isolate);
Handle<DescriptorArray> tmp_descriptors(
tmp_map->instance_descriptors(), isolate);
// Check if target map is compatible.
#ifdef DEBUG
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
if ((tmp_details.type() == ACCESSOR_CONSTANT ||
old_details.type() == ACCESSOR_CONSTANT) &&
(tmp_details.type() != old_details.type() ||
tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) {
DCHECK_EQ(next_kind, tmp_details.kind());
DCHECK_EQ(next_attributes, tmp_details.attributes());
#endif
if (next_kind == kAccessor &&
!EqualImmutableValues(old_descriptors->GetValue(i),
tmp_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
new_kind, new_attributes,
"GenAll_Incompatible");
}
DCHECK(!tmp_map->is_deprecated());
target_map = tmp_map;
}
target_nof = target_map->NumberOfOwnDescriptors();
......@@ -2553,7 +2672,7 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
int current_offset = 0;
for (int i = 0; i < root_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
if (old_details.type() == DATA) {
if (old_details.location() == kField) {
current_offset += old_details.field_width_in_words();
}
Descriptor d(handle(old_descriptors->GetKey(i), isolate),
......@@ -2567,43 +2686,85 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
Handle<Name> target_key(target_descriptors->GetKey(i), isolate);
PropertyDetails old_details = old_descriptors->GetDetails(i);
PropertyDetails target_details = target_descriptors->GetDetails(i);
target_details = target_details.CopyWithRepresentation(
old_details.representation().generalize(
target_details.representation()));
PropertyKind next_kind;
PropertyAttributes next_attributes;
PropertyLocation next_location;
Representation next_representation;
bool property_kind_reconfiguration = false;
if (modify_index == i) {
target_details = target_details.CopyWithRepresentation(
new_representation.generalize(target_details.representation()));
}
DCHECK_EQ(old_details.attributes(), target_details.attributes());
if (old_details.type() == DATA || target_details.type() == DATA ||
(modify_index == i && store_mode == FORCE_FIELD) ||
(target_descriptors->GetValue(i) != old_descriptors->GetValue(i))) {
Handle<HeapType> old_field_type =
(old_details.type() == DATA)
? handle(old_descriptors->GetFieldType(i), isolate)
: old_descriptors->GetValue(i)
->OptimalType(isolate, target_details.representation());
Handle<HeapType> target_field_type =
(target_details.type() == DATA)
? handle(target_descriptors->GetFieldType(i), isolate)
: target_descriptors->GetValue(i)
->OptimalType(isolate, target_details.representation());
target_field_type = GeneralizeFieldType(
target_field_type, old_field_type, isolate);
if (modify_index == i) {
target_field_type = GeneralizeFieldType(
target_field_type, new_field_type, isolate);
DCHECK_EQ(FORCE_FIELD, store_mode);
property_kind_reconfiguration = old_details.kind() != new_kind;
next_kind = new_kind;
next_attributes = new_attributes;
next_location = kField;
// Merge new representation/field type with ones from the target
// descriptor. If property kind is not reconfigured merge the result with
// representation/field type from the old descriptor.
next_representation =
new_representation.generalize(target_details.representation());
if (!property_kind_reconfiguration) {
next_representation =
next_representation.generalize(old_details.representation());
}
DataDescriptor d(target_key, current_offset, target_field_type,
target_details.attributes(),
target_details.representation());
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
DCHECK_NE(DATA, target_details.type());
Descriptor d(target_key,
handle(target_descriptors->GetValue(i), isolate),
target_details);
// Merge old_descriptor and target_descriptor entries.
DCHECK_EQ(target_details.kind(), old_details.kind());
next_kind = target_details.kind();
next_attributes = target_details.attributes();
next_location =
old_details.location() == kField ||
target_details.location() == kField ||
!EqualImmutableValues(target_descriptors->GetValue(i),
old_descriptors->GetValue(i))
? kField
: kDescriptor;
next_representation = old_details.representation().generalize(
target_details.representation());
}
DCHECK_EQ(next_kind, target_details.kind());
DCHECK_EQ(next_attributes, target_details.attributes());
if (next_location == kField) {
if (next_kind == kData) {
Handle<HeapType> target_field_type =
GetFieldType(isolate, target_descriptors, i,
target_details.location(), next_representation);
Handle<HeapType> next_field_type;
if (modify_index == i) {
next_field_type =
GeneralizeFieldType(target_field_type, new_field_type, isolate);
if (!property_kind_reconfiguration) {
Handle<HeapType> old_field_type =
GetFieldType(isolate, old_descriptors, i,
old_details.location(), next_representation);
next_field_type =
GeneralizeFieldType(next_field_type, old_field_type, isolate);
}
} else {
Handle<HeapType> old_field_type =
GetFieldType(isolate, old_descriptors, i, old_details.location(),
next_representation);
next_field_type =
GeneralizeFieldType(target_field_type, old_field_type, isolate);
}
DataDescriptor d(target_key, current_offset, next_field_type,
next_attributes, next_representation);
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
UNIMPLEMENTED(); // TODO(ishell): implement.
}
} else {
PropertyDetails details(next_attributes, next_kind, next_location,
next_representation);
Descriptor d(target_key, handle(target_descriptors->GetValue(i), isolate),
details);
new_descriptors->Set(i, &d);
}
}
......@@ -2612,47 +2773,74 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
for (int i = target_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
Handle<Name> old_key(old_descriptors->GetKey(i), isolate);
// Merge old_descriptor entry and modified details together.
PropertyKind next_kind;
PropertyAttributes next_attributes;
PropertyLocation next_location;
Representation next_representation;
bool property_kind_reconfiguration = false;
if (modify_index == i) {
old_details = old_details.CopyWithRepresentation(
new_representation.generalize(old_details.representation()));
}
if (old_details.type() == DATA) {
Handle<HeapType> old_field_type(
old_descriptors->GetFieldType(i), isolate);
if (modify_index == i) {
old_field_type = GeneralizeFieldType(
old_field_type, new_field_type, isolate);
DCHECK_EQ(FORCE_FIELD, store_mode);
// In case of property kind reconfiguration it is not necessary to
// take into account representation/field type of the old descriptor.
property_kind_reconfiguration = old_details.kind() != new_kind;
next_kind = new_kind;
next_attributes = new_attributes;
next_location = kField;
next_representation = new_representation;
if (!property_kind_reconfiguration) {
next_representation =
next_representation.generalize(old_details.representation());
}
DataDescriptor d(old_key, current_offset, old_field_type,
old_details.attributes(), old_details.representation());
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
DCHECK(old_details.type() == DATA_CONSTANT ||
old_details.type() == ACCESSOR_CONSTANT);
if (modify_index == i && store_mode == FORCE_FIELD) {
DataDescriptor d(
old_key, current_offset,
GeneralizeFieldType(old_descriptors->GetValue(i)->OptimalType(
isolate, old_details.representation()),
new_field_type, isolate),
old_details.attributes(), old_details.representation());
next_kind = old_details.kind();
next_attributes = old_details.attributes();
next_location = old_details.location();
next_representation = old_details.representation();
}
if (next_location == kField) {
if (next_kind == kData) {
Handle<HeapType> next_field_type;
if (modify_index == i) {
next_field_type = new_field_type;
if (!property_kind_reconfiguration) {
Handle<HeapType> old_field_type =
GetFieldType(isolate, old_descriptors, i,
old_details.location(), next_representation);
old_field_type =
GeneralizeFieldType(old_field_type, next_field_type, isolate);
}
} else {
Handle<HeapType> old_field_type =
GetFieldType(isolate, old_descriptors, i, old_details.location(),
next_representation);
next_field_type = old_field_type;
}
DataDescriptor d(old_key, current_offset, next_field_type,
next_attributes, next_representation);
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
DCHECK_NE(DATA, old_details.type());
Descriptor d(old_key,
handle(old_descriptors->GetValue(i), isolate),
old_details);
new_descriptors->Set(i, &d);
UNIMPLEMENTED(); // TODO(ishell): implement.
}
} else {
PropertyDetails details(next_attributes, next_kind, next_location,
next_representation);
Descriptor d(old_key, handle(old_descriptors->GetValue(i), isolate),
details);
new_descriptors->Set(i, &d);
}
}
new_descriptors->Sort();
DCHECK(store_mode != FORCE_FIELD ||
new_descriptors->GetDetails(modify_index).type() == DATA);
new_descriptors->GetDetails(modify_index).location() == kField);
Handle<Map> split_map(root_map->FindLastMatchMap(
root_nof, old_nof, *new_descriptors), isolate);
......@@ -2661,21 +2849,31 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
Handle<LayoutDescriptor> new_layout_descriptor =
LayoutDescriptor::New(split_map, new_descriptors, old_nof);
PropertyDetails split_prop_details = old_descriptors->GetDetails(split_nof);
PropertyKind split_kind;
PropertyAttributes split_attributes;
if (modify_index == split_nof) {
split_kind = new_kind;
split_attributes = new_attributes;
} else {
PropertyDetails split_prop_details = old_descriptors->GetDetails(split_nof);
split_kind = split_prop_details.kind();
split_attributes = split_prop_details.attributes();
}
bool transition_target_deprecated = split_map->DeprecateTarget(
split_prop_details.kind(), old_descriptors->GetKey(split_nof),
split_prop_details.attributes(), *new_descriptors,
*new_layout_descriptor);
split_kind, old_descriptors->GetKey(split_nof), split_attributes,
*new_descriptors, *new_layout_descriptor);
// If |transition_target_deprecated| is true then the transition array
// already contains entry for given descriptor. This means that the transition
// could be inserted regardless of whether transitions array is full or not.
if (!transition_target_deprecated && !split_map->CanHaveMoreTransitions()) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
new_kind, new_attributes,
"GenAll_CantHaveMoreTransitions");
}
if (FLAG_trace_generalization) {
if (FLAG_trace_generalization && modify_index >= 0) {
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
PropertyDetails new_details = new_descriptors->GetDetails(modify_index);
Handle<HeapType> old_field_type =
......@@ -2692,7 +2890,7 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
isolate);
old_map->PrintGeneralization(
stdout, "", modify_index, split_nof, old_nof,
old_details.type() == DATA_CONSTANT && store_mode == FORCE_FIELD,
old_details.location() == kDescriptor && store_mode == FORCE_FIELD,
old_details.representation(), new_details.representation(),
*old_field_type, *new_field_type);
}
......@@ -2713,10 +2911,11 @@ Handle<Map> Map::GeneralizeAllFieldRepresentations(
Handle<Map> map) {
Handle<DescriptorArray> descriptors(map->instance_descriptors());
for (int i = 0; i < map->NumberOfOwnDescriptors(); ++i) {
if (descriptors->GetDetails(i).type() == DATA) {
map = GeneralizeRepresentation(map, i, Representation::Tagged(),
HeapType::Any(map->GetIsolate()),
FORCE_FIELD);
PropertyDetails details = descriptors->GetDetails(i);
if (details.type() == DATA) {
map = ReconfigureProperty(map, i, kData, details.attributes(),
Representation::Tagged(),
HeapType::Any(map->GetIsolate()), FORCE_FIELD);
}
}
return map;
......@@ -2740,9 +2939,9 @@ MaybeHandle<Map> Map::TryUpdate(Handle<Map> map) {
// static
Handle<Map> Map::Update(Handle<Map> map) {
if (!map->is_deprecated()) return map;
return GeneralizeRepresentation(map, 0, Representation::None(),
HeapType::None(map->GetIsolate()),
ALLOW_IN_DESCRIPTOR);
return ReconfigureProperty(map, -1, kData, NONE, Representation::None(),
HeapType::None(map->GetIsolate()),
ALLOW_IN_DESCRIPTOR);
}
......@@ -3952,7 +4151,6 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
}
it.ReconfigureDataProperty(value, attributes);
it.PrepareForDataProperty(value);
value = it.WriteDataValue(value);
if (is_observed) {
......@@ -3977,7 +4175,6 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
if (is_observed) old_value = it.GetDataValue();
it.ReconfigureDataProperty(value, attributes);
it.PrepareForDataProperty(value);
value = it.WriteDataValue(value);
if (is_observed) {
......@@ -5846,7 +6043,7 @@ int Map::NextFreePropertyIndex() {
DescriptorArray* descs = instance_descriptors();
for (int i = 0; i < number_of_own_descriptors; i++) {
PropertyDetails details = descs->GetDetails(i);
if (details.type() == DATA) {
if (details.location() == kField) {
int candidate = details.field_index() + details.field_width_in_words();
if (candidate > free_index) free_index = candidate;
}
......@@ -6841,7 +7038,7 @@ Handle<Map> Map::CopyInstallDescriptors(
int unused_property_fields = map->unused_property_fields();
PropertyDetails details = descriptors->GetDetails(new_descriptor);
if (details.type() == DATA) {
if (details.location() == kField) {
unused_property_fields = map->unused_property_fields() - 1;
if (unused_property_fields < 0) {
unused_property_fields += JSObject::kFieldsAdded;
......@@ -7059,11 +7256,13 @@ Handle<Map> Map::PrepareForDataProperty(Handle<Map> map, int descriptor,
if (descriptors->CanHoldValue(descriptor, *value)) return map;
Isolate* isolate = map->GetIsolate();
PropertyAttributes attributes =
descriptors->GetDetails(descriptor).attributes();
Representation representation = value->OptimalRepresentation();
Handle<HeapType> type = value->OptimalType(isolate, representation);
return GeneralizeRepresentation(map, descriptor, representation, type,
FORCE_FIELD);
return ReconfigureProperty(map, descriptor, kData, attributes, representation,
type, FORCE_FIELD);
}
......@@ -7120,15 +7319,29 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name,
}
Handle<Map> Map::ReconfigureDataProperty(Handle<Map> map, int descriptor,
PropertyAttributes attributes) {
Handle<Map> Map::ReconfigureExistingProperty(Handle<Map> map, int descriptor,
PropertyKind kind,
PropertyAttributes attributes) {
// Dictionaries have to be reconfigured in-place.
DCHECK(!map->is_dictionary_map());
// For now, give up on transitioning and just create a unique map.
// TODO(verwaest/ishell): Cache transitions with different attributes.
return CopyGeneralizeAllRepresentations(
map, descriptor, FORCE_FIELD, attributes, "GenAll_AttributesMismatch");
if (!map->GetBackPointer()->IsMap()) {
// There is no benefit from reconstructing transition tree for maps without
// back pointers.
return CopyGeneralizeAllRepresentations(
map, descriptor, FORCE_FIELD, kind, attributes,
"GenAll_AttributesMismatchProtoMap");
}
if (FLAG_trace_generalization) {
map->PrintReconfiguration(stdout, descriptor, kind, attributes);
}
Isolate* isolate = map->GetIsolate();
Handle<Map> new_map = ReconfigureProperty(
map, descriptor, kind, attributes, Representation::None(),
HeapType::None(isolate), FORCE_FIELD);
return new_map;
}
......
......@@ -5889,23 +5889,15 @@ class Map: public HeapObject {
static void GeneralizeFieldType(Handle<Map> map, int modify_index,
Representation new_representation,
Handle<HeapType> new_field_type);
static Handle<Map> GeneralizeRepresentation(
Handle<Map> map,
int modify_index,
Representation new_representation,
Handle<HeapType> new_field_type,
StoreMode store_mode);
static Handle<Map> ReconfigureProperty(Handle<Map> map, int modify_index,
PropertyKind new_kind,
PropertyAttributes new_attributes,
Representation new_representation,
Handle<HeapType> new_field_type,
StoreMode store_mode);
static Handle<Map> CopyGeneralizeAllRepresentations(
Handle<Map> map,
int modify_index,
StoreMode store_mode,
PropertyAttributes attributes,
const char* reason);
static Handle<Map> CopyGeneralizeAllRepresentations(
Handle<Map> map,
int modify_index,
StoreMode store_mode,
const char* reason);
Handle<Map> map, int modify_index, StoreMode store_mode,
PropertyKind kind, PropertyAttributes attributes, const char* reason);
static Handle<Map> PrepareForDataProperty(Handle<Map> old_map,
int descriptor_number,
......@@ -6128,8 +6120,10 @@ class Map: public HeapObject {
static Handle<Map> TransitionToAccessorProperty(
Handle<Map> map, Handle<Name> name, AccessorComponent component,
Handle<Object> accessor, PropertyAttributes attributes);
static Handle<Map> ReconfigureDataProperty(Handle<Map> map, int descriptor,
PropertyAttributes attributes);
static Handle<Map> ReconfigureExistingProperty(Handle<Map> map,
int descriptor,
PropertyKind kind,
PropertyAttributes attributes);
inline void AppendDescriptor(Descriptor* desc);
......@@ -6414,6 +6408,9 @@ class Map: public HeapObject {
Descriptor* descriptor,
int index,
TransitionFlag flag);
static MUST_USE_RESULT MaybeHandle<Map> TryReconfigureExistingProperty(
Handle<Map> map, int descriptor, PropertyKind kind,
PropertyAttributes attributes, const char** reason);
static Handle<Map> CopyNormalized(Handle<Map> map,
PropertyNormalizationMode mode);
......@@ -6447,6 +6444,8 @@ class Map: public HeapObject {
Representation new_representation,
Handle<HeapType> new_type);
void PrintReconfiguration(FILE* file, int modify_index, PropertyKind kind,
PropertyAttributes attributes);
void PrintGeneralization(FILE* file,
const char* reason,
int modify_index,
......@@ -10506,6 +10505,14 @@ class AccessorPair: public Struct {
if (!setter->IsNull()) set_setter(setter);
}
bool Equals(AccessorPair* pair) {
return (this == pair) || pair->Equals(getter(), setter());
}
bool Equals(Object* getter_value, Object* setter_value) {
return (getter() == getter_value) && (setter() == setter_value);
}
bool ContainsAccessor() {
return IsJSAccessor(getter()) || IsJSAccessor(setter());
}
......
......@@ -212,6 +212,15 @@ class PropertyDetails BASE_EMBEDDED {
| FieldIndexField::encode(field_index);
}
PropertyDetails(PropertyAttributes attributes, PropertyKind kind,
PropertyLocation location, Representation representation,
int field_index = 0) {
value_ = KindField::encode(kind) | LocationField::encode(location) |
AttributesField::encode(attributes) |
RepresentationField::encode(EncodeRepresentation(representation)) |
FieldIndexField::encode(field_index);
}
int pointer() const { return DescriptorPointer::decode(value_); }
PropertyDetails set_pointer(int i) { return PropertyDetails(value_, i); }
......
......@@ -69,9 +69,9 @@ std::ostream& operator<<(std::ostream& os,
os << "immutable ";
}
os << (details.kind() == kData ? "data" : "accessor");
os << ": " << details.representation().Mnemonic();
if (details.location() == kField) {
os << ": " << details.representation().Mnemonic()
<< ", field_index: " << details.field_index();
os << ", field_index: " << details.field_index();
}
return os << ", p: " << details.pointer()
<< ", attrs: " << details.attributes() << ")";
......
......@@ -137,6 +137,7 @@
'test-microtask-delivery.cc',
'test-mark-compact.cc',
'test-mementos.cc',
'test-migrations.cc',
'test-object-observe.cc',
'test-ordered-hash-table.cc',
'test-parsing.cc',
......
// 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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment