class String;
class Name;
class Struct;
+class Symbol;
class Variable;
class RelocInfo;
class Deserializer;
if (IsAccessor()) return true;
Handle<Map> map = this->map();
- map->LookupTransition(NULL, *name_, &lookup_);
+ map->LookupTransition(NULL, *name_, NONE, &lookup_);
if (lookup_.IsTransitionToField() && map->unused_property_fields() > 0) {
// Construct the object field access.
int descriptor = transition()->LastAdded();
for (int i = 0; i < number_of_descriptors(); i++) {
Name* key = GetSortedKey(i);
if (key == current_key) {
- OFStream os(stdout);
- PrintDescriptors(os);
+ Print();
return false;
}
current_key = key;
uint32_t hash = GetSortedKey(i)->Hash();
if (hash < current) {
- OFStream os(stdout);
- PrintDescriptors(os);
+ Print();
return false;
}
current = hash;
bool TransitionArray::IsSortedNoDuplicates(int valid_entries) {
DCHECK(valid_entries == -1);
- Name* current_key = NULL;
- uint32_t current = 0;
+ Name* prev_key = NULL;
+ bool prev_is_data_property = false;
+ PropertyAttributes prev_attributes = NONE;
+ uint32_t prev_hash = 0;
for (int i = 0; i < number_of_transitions(); i++) {
Name* key = GetSortedKey(i);
- if (key == current_key) {
- OFStream os(stdout);
- PrintTransitions(os);
- return false;
+ uint32_t hash = key->Hash();
+ bool is_data_property = false;
+ PropertyAttributes attributes = NONE;
+ if (!IsSpecialTransition(key)) {
+ Map* target = GetTarget(i);
+ PropertyDetails details = GetTargetDetails(key, target);
+ is_data_property = details.type() == FIELD || details.type() == CONSTANT;
+ attributes = details.attributes();
+ } else {
+ // Duplicate entries are not allowed for non-property transitions.
+ CHECK_NE(prev_key, key);
}
- current_key = key;
- uint32_t hash = GetSortedKey(i)->Hash();
- if (hash < current) {
- OFStream os(stdout);
- PrintTransitions(os);
+
+ int cmp =
+ CompareKeys(prev_key, prev_hash, prev_is_data_property, prev_attributes,
+ key, hash, is_data_property, attributes);
+ if (cmp >= 0) {
+ Print();
return false;
}
- current = hash;
+ prev_key = key;
+ prev_hash = hash;
+ prev_attributes = attributes;
+ prev_is_data_property = is_data_property;
}
return true;
}
DisallowHeapAllocation no_allocation;
if (!map->HasTransitionArray()) return Handle<Map>::null();
TransitionArray* transitions = map->transitions();
- int transition = transitions->Search(*key);
+ int transition = transitions->Search(FIELD, *key, NONE);
if (transition == TransitionArray::kNotFound) return Handle<Map>::null();
- PropertyDetails target_details = transitions->GetTargetDetails(transition);
- if (target_details.type() != FIELD) return Handle<Map>::null();
- if (target_details.attributes() != NONE) return Handle<Map>::null();
+ DCHECK_EQ(FIELD, transitions->GetTargetDetails(transition).type());
+ DCHECK_EQ(NONE, transitions->GetTargetDetails(transition).attributes());
return Handle<Map>(transitions->GetTarget(transition));
}
}
-void Map::LookupTransition(JSObject* holder,
- Name* name,
+void Map::LookupTransition(JSObject* holder, Name* name,
+ PropertyAttributes attributes,
LookupResult* result) {
- int transition_index = this->SearchTransition(name);
+ int transition_index = this->SearchTransition(FIELD, name, attributes);
if (transition_index == TransitionArray::kNotFound) return result->NotFound();
result->TransitionResult(holder, this->GetTransition(transition_index));
}
Map* Map::elements_transition_map() {
- int index = transitions()->Search(GetHeap()->elements_transition_symbol());
+ int index =
+ transitions()->SearchSpecial(GetHeap()->elements_transition_symbol());
return transitions()->GetTarget(index);
}
}
-int Map::SearchTransition(Name* name) {
- if (HasTransitionArray()) return transitions()->Search(name);
+int Map::SearchSpecialTransition(Symbol* name) {
+ if (HasTransitionArray()) {
+ return transitions()->SearchSpecial(name);
+ }
+ return TransitionArray::kNotFound;
+}
+
+
+int Map::SearchTransition(PropertyType type, Name* name,
+ PropertyAttributes attributes) {
+ if (HasTransitionArray()) {
+ return transitions()->Search(type, name, attributes);
+ }
return TransitionArray::kNotFound;
}
Map* target = transitions()->GetTarget(i);
if (target->instance_descriptors() == instance_descriptors()) {
Name* key = transitions()->GetKey(i);
- int new_target_index = transition_array->Search(key);
- DCHECK(new_target_index != TransitionArray::kNotFound);
- DCHECK(transition_array->GetTarget(new_target_index) == target);
+ int new_target_index;
+ if (TransitionArray::IsSpecialTransition(key)) {
+ new_target_index = transition_array->SearchSpecial(Symbol::cast(key));
+ } else {
+ PropertyDetails details =
+ TransitionArray::GetTargetDetails(key, target);
+ new_target_index = transition_array->Search(details.type(), key,
+ details.attributes());
+ }
+ DCHECK_NE(TransitionArray::kNotFound, new_target_index);
+ DCHECK_EQ(target, transition_array->GetTarget(new_target_index));
}
}
#endif
}
for (int i = 0; i < number_of_transitions(); i++) {
Name* key = GetKey(i);
+ Map* target = GetTarget(i);
os << " ";
key->NamePrint(os);
os << ": ";
if (key == GetHeap()->frozen_symbol()) {
os << " (transition to frozen)";
} else if (key == GetHeap()->elements_transition_symbol()) {
- os << " (transition to "
- << ElementsKindToString(GetTarget(i)->elements_kind()) << ")";
+ os << " (transition to " << ElementsKindToString(target->elements_kind())
+ << ")";
} else if (key == GetHeap()->observed_symbol()) {
os << " (transition to Object.observe)";
} else {
- PropertyDetails details = GetTargetDetails(i);
+ PropertyDetails details = GetTargetDetails(key, target);
switch (details.type()) {
case FIELD: {
os << " (transition to field)";
}
os << ", attrs: " << details.attributes();
}
- os << " -> " << Brief(GetTarget(i)) << "\n";
+ os << " -> " << Brief(target) << "\n";
}
}
void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) {
Isolate* isolate = parent->GetIsolate();
Handle<Name> name = isolate->factory()->elements_transition_symbol();
- ConnectTransition(parent, child, name, FULL_TRANSITION);
+ ConnectTransition(parent, child, name, SPECIAL_TRANSITION);
}
// Invalidates a transition target at |key|, and installs |new_descriptors| over
// the current instance_descriptors to ensure proper sharing of descriptor
// arrays.
-void Map::DeprecateTarget(Name* key, DescriptorArray* new_descriptors) {
+void Map::DeprecateTarget(PropertyType type, Name* key,
+ PropertyAttributes attributes,
+ DescriptorArray* new_descriptors) {
if (HasTransitionArray()) {
TransitionArray* transitions = this->transitions();
- int transition = transitions->Search(key);
+ int transition = transitions->Search(type, key, attributes);
if (transition != TransitionArray::kNotFound) {
transitions->GetTarget(transition)->DeprecateTransitionTree();
}
for (int i = verbatim; i < length; i++) {
if (!current->HasTransitionArray()) break;
Name* name = descriptors->GetKey(i);
+ PropertyDetails details = descriptors->GetDetails(i);
TransitionArray* transitions = current->transitions();
- int transition = transitions->Search(name);
+ int transition =
+ transitions->Search(details.type(), name, details.attributes());
if (transition == TransitionArray::kNotFound) break;
Map* next = transitions->GetTarget(transition);
DescriptorArray* next_descriptors = next->instance_descriptors();
- PropertyDetails details = descriptors->GetDetails(i);
PropertyDetails next_details = next_descriptors->GetDetails(i);
if (details.type() != next_details.type()) break;
if (details.attributes() != next_details.attributes()) break;
Handle<Map> target_map = root_map;
for (int i = root_nof; i < old_nof; ++i) {
- int j = target_map->SearchTransition(old_descriptors->GetKey(i));
+ PropertyDetails old_details = old_descriptors->GetDetails(i);
+ int j = target_map->SearchTransition(old_details.type(),
+ old_descriptors->GetKey(i),
+ old_details.attributes());
if (j == TransitionArray::kNotFound) break;
Handle<Map> tmp_map(target_map->GetTransition(j), isolate);
Handle<DescriptorArray> tmp_descriptors = handle(
tmp_map->instance_descriptors(), isolate);
// Check if target map is incompatible.
- PropertyDetails old_details = old_descriptors->GetDetails(i);
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
PropertyType old_type = old_details.type();
PropertyType tmp_type = tmp_details.type();
- if (tmp_details.attributes() != old_details.attributes() ||
- ((tmp_type == CALLBACKS || old_type == CALLBACKS) &&
- (tmp_type != old_type ||
- tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i)))) {
+ DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
+ if ((tmp_type == CALLBACKS || old_type == CALLBACKS) &&
+ (tmp_type != old_type ||
+ tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations(
old_map, modify_index, store_mode, "incompatible");
}
// Find the last compatible target map in the transition tree.
for (int i = target_nof; i < old_nof; ++i) {
- int j = target_map->SearchTransition(old_descriptors->GetKey(i));
+ PropertyDetails old_details = old_descriptors->GetDetails(i);
+ int j = target_map->SearchTransition(old_details.type(),
+ old_descriptors->GetKey(i),
+ old_details.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.
- PropertyDetails old_details = old_descriptors->GetDetails(i);
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
- if (tmp_details.attributes() != old_details.attributes() ||
- ((tmp_details.type() == CALLBACKS || old_details.type() == CALLBACKS) &&
- (tmp_details.type() != old_details.type() ||
- tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i)))) {
+ DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
+ if ((tmp_details.type() == CALLBACKS || old_details.type() == CALLBACKS) &&
+ (tmp_details.type() != old_details.type() ||
+ tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations(
old_map, modify_index, store_mode, "incompatible");
}
int split_nof = split_map->NumberOfOwnDescriptors();
DCHECK_NE(old_nof, split_nof);
- split_map->DeprecateTarget(
- old_descriptors->GetKey(split_nof), *new_descriptors);
+ PropertyDetails split_prop_details = old_descriptors->GetDetails(split_nof);
+ split_map->DeprecateTarget(split_prop_details.type(),
+ old_descriptors->GetKey(split_nof),
+ split_prop_details.attributes(), *new_descriptors);
if (FLAG_trace_generalization) {
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
Map* new_map = root_map;
for (int i = root_nof; i < old_nof; ++i) {
- int j = new_map->SearchTransition(old_descriptors->GetKey(i));
+ PropertyDetails old_details = old_descriptors->GetDetails(i);
+ int j = new_map->SearchTransition(old_details.type(),
+ old_descriptors->GetKey(i),
+ old_details.attributes());
if (j == TransitionArray::kNotFound) return MaybeHandle<Map>();
new_map = new_map->GetTransition(j);
DescriptorArray* new_descriptors = new_map->instance_descriptors();
PropertyDetails new_details = new_descriptors->GetDetails(i);
- PropertyDetails old_details = old_descriptors->GetDetails(i);
if (old_details.attributes() != new_details.attributes() ||
!old_details.representation().fits_into(new_details.representation())) {
return MaybeHandle<Map>();
}
Handle<Map> old_map(object->map(), isolate);
- int transition_index = old_map->SearchTransition(
- isolate->heap()->frozen_symbol());
+ int transition_index =
+ old_map->SearchSpecialTransition(isolate->heap()->frozen_symbol());
if (transition_index != TransitionArray::kNotFound) {
Handle<Map> transition_map(old_map->GetTransition(transition_index));
DCHECK(transition_map->has_dictionary_elements());
Handle<Map> new_map;
Handle<Map> old_map(object->map(), isolate);
DCHECK(!old_map->is_observed());
- int transition_index = old_map->SearchTransition(
- isolate->heap()->observed_symbol());
+ int transition_index =
+ old_map->SearchSpecialTransition(isolate->heap()->observed_symbol());
if (transition_index != TransitionArray::kNotFound) {
new_map = handle(old_map->GetTransition(transition_index), isolate);
DCHECK(new_map->is_observed());
}
DCHECK(result->NumberOfOwnDescriptors() == map->NumberOfOwnDescriptors() + 1);
- ConnectTransition(map, result, name, SIMPLE_TRANSITION);
+ ConnectTransition(map, result, name, SIMPLE_PROPERTY_TRANSITION);
return result;
}
result->set_unused_property_fields(unused_property_fields);
Handle<Name> name = handle(descriptors->GetKey(new_descriptor));
- ConnectTransition(map, result, name, SIMPLE_TRANSITION);
+ ConnectTransition(map, result, name, SIMPLE_PROPERTY_TRANSITION);
return result;
}
if (map->CanHaveMoreTransitions()) {
Handle<Name> name = isolate->factory()->observed_symbol();
- ConnectTransition(map, new_map, name, FULL_TRANSITION);
+ ConnectTransition(map, new_map, name, SPECIAL_TRANSITION);
}
return new_map;
}
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
Handle<DescriptorArray> new_descriptors =
DescriptorArray::CopyUpTo(descriptors, number_of_own_descriptors);
- return CopyReplaceDescriptors(
- map, new_descriptors, OMIT_TRANSITION, MaybeHandle<Name>());
+ return CopyReplaceDescriptors(map, new_descriptors, OMIT_TRANSITION,
+ MaybeHandle<Name>(), SPECIAL_TRANSITION);
}
Handle<DescriptorArray> new_desc = DescriptorArray::CopyUpToAddAttributes(
handle(map->instance_descriptors(), isolate), num_descriptors, FROZEN);
Handle<Map> new_map = CopyReplaceDescriptors(
- map, new_desc, INSERT_TRANSITION, isolate->factory()->frozen_symbol());
+ map, new_desc, INSERT_TRANSITION, isolate->factory()->frozen_symbol(),
+ SPECIAL_TRANSITION);
new_map->freeze();
new_map->set_is_extensible(false);
new_map->set_elements_kind(DICTIONARY_ELEMENTS);
// Migrate to the newest map before storing the property.
map = Update(map);
- int index = map->SearchTransition(*name);
+ int index = map->SearchTransition(FIELD, *name, attributes);
if (index != TransitionArray::kNotFound) {
Handle<Map> transition(map->GetTransition(index));
int descriptor = transition->LastAdded();
- // TODO(verwaest): Handle attributes better.
- DescriptorArray* descriptors = transition->instance_descriptors();
- if (descriptors->GetDetails(descriptor).attributes() != attributes) {
- return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES);
- }
+ DCHECK_EQ(attributes, transition->instance_descriptors()
+ ->GetDetails(descriptor)
+ .attributes());
return Map::PrepareForDataProperty(transition, descriptor, value);
}
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
- int index = map->SearchTransition(*name);
+ int index = map->SearchTransition(CALLBACKS, *name, attributes);
if (index != TransitionArray::kNotFound) {
Handle<Map> transition(map->GetTransition(index));
DescriptorArray* descriptors = transition->instance_descriptors();
- // Fast path, assume that we're modifying the last added descriptor.
int descriptor = transition->LastAdded();
- if (descriptors->GetKey(descriptor) != *name) {
- // If not, search for the descriptor.
- descriptor = descriptors->SearchWithCache(*name, *transition);
- }
+ DCHECK(descriptors->GetKey(descriptor)->Equals(*name));
- if (descriptors->GetDetails(descriptor).type() != CALLBACKS) {
- return Map::Normalize(map, mode);
- }
-
- // TODO(verwaest): Handle attributes better.
- if (descriptors->GetDetails(descriptor).attributes() != attributes) {
- return Map::Normalize(map, mode);
- }
+ DCHECK_EQ(CALLBACKS, descriptors->GetDetails(descriptor).type());
+ DCHECK_EQ(attributes, descriptors->GetDetails(descriptor).attributes());
Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate);
if (!maybe_pair->IsAccessorPair()) {
DescriptorArray* old_descriptors = map->instance_descriptors();
int descriptor = old_descriptors->SearchWithCache(*name, *map);
if (descriptor != DescriptorArray::kNotFound) {
+ if (descriptor != map->LastAdded()) {
+ return Map::Normalize(map, mode);
+ }
PropertyDetails old_details = old_descriptors->GetDetails(descriptor);
if (old_details.type() != CALLBACKS) {
return Map::Normalize(map, mode);
descriptors, map->NumberOfOwnDescriptors(), 1);
new_descriptors->Append(descriptor);
- return CopyReplaceDescriptors(
- map, new_descriptors, flag, descriptor->GetKey(), SIMPLE_TRANSITION);
+ return CopyReplaceDescriptors(map, new_descriptors, flag,
+ descriptor->GetKey(),
+ SIMPLE_PROPERTY_TRANSITION);
}
SimpleTransitionFlag simple_flag =
(insertion_index == descriptors->number_of_descriptors() - 1)
- ? SIMPLE_TRANSITION
- : FULL_TRANSITION;
+ ? SIMPLE_PROPERTY_TRANSITION
+ : PROPERTY_TRANSITION;
return CopyReplaceDescriptors(map, new_descriptors, flag, key, simple_flag);
}
// either extends the current map with a new property, or it modifies the
// property that was added last to the current map.
enum SimpleTransitionFlag {
- SIMPLE_TRANSITION,
- FULL_TRANSITION
+ SIMPLE_PROPERTY_TRANSITION,
+ PROPERTY_TRANSITION,
+ SPECIAL_TRANSITION
};
inline Map* elements_transition_map();
inline Map* GetTransition(int transition_index);
- inline int SearchTransition(Name* name);
+ inline int SearchSpecialTransition(Symbol* name);
+ inline int SearchTransition(PropertyType type, Name* name,
+ PropertyAttributes attributes);
inline FixedArrayBase* GetInitialElements();
DECL_ACCESSORS(transitions, TransitionArray)
Name* name,
LookupResult* result);
- inline void LookupTransition(JSObject* holder,
- Name* name,
+ inline void LookupTransition(JSObject* holder, Name* name,
+ PropertyAttributes attributes,
LookupResult* result);
inline PropertyDetails GetLastDescriptorDetails();
static Handle<Map> CopyAddDescriptor(Handle<Map> map,
Descriptor* descriptor,
TransitionFlag flag);
- static Handle<Map> CopyReplaceDescriptors(
- Handle<Map> map,
- Handle<DescriptorArray> descriptors,
- TransitionFlag flag,
- MaybeHandle<Name> maybe_name,
- SimpleTransitionFlag simple_flag = FULL_TRANSITION);
+ static Handle<Map> CopyReplaceDescriptors(Handle<Map> map,
+ Handle<DescriptorArray> descriptors,
+ TransitionFlag flag,
+ MaybeHandle<Name> maybe_name,
+ SimpleTransitionFlag simple_flag);
static Handle<Map> CopyReplaceDescriptor(Handle<Map> map,
Handle<DescriptorArray> descriptors,
Descriptor* descriptor,
void ZapTransitions();
void DeprecateTransitionTree();
- void DeprecateTarget(Name* key, DescriptorArray* new_descriptors);
+ void DeprecateTarget(PropertyType type, Name* key,
+ PropertyAttributes attributes,
+ DescriptorArray* new_descriptors);
Map* FindLastMatchMap(int verbatim, int length, DescriptorArray* descriptors);
bool TransitionArray::HasElementsTransition() {
- return Search(GetHeap()->elements_transition_symbol()) != kNotFound;
+ return SearchSpecial(GetHeap()->elements_transition_symbol()) != kNotFound;
}
}
-int TransitionArray::Search(Name* name, int* out_insertion_index) {
+int TransitionArray::SearchName(Name* name, int* out_insertion_index) {
if (IsSimpleTransition()) {
Name* key = GetKey(kSimpleTransitionIndex);
if (key->Equals(name)) return kSimpleTransitionIndex;
}
+#ifdef DEBUG
+bool TransitionArray::IsSpecialTransition(Name* name) {
+ if (!name->IsSymbol()) return false;
+ Heap* heap = name->GetHeap();
+ return name == heap->frozen_symbol() ||
+ name == heap->elements_transition_symbol() ||
+ name == heap->observed_symbol();
+}
+#endif
+
+
+int TransitionArray::CompareKeys(Name* key1, uint32_t hash1,
+ bool is_data_property1,
+ PropertyAttributes attributes1, Name* key2,
+ uint32_t hash2, bool is_data_property2,
+ PropertyAttributes attributes2) {
+ int cmp = CompareNames(key1, hash1, key2, hash2);
+ if (cmp != 0) return cmp;
+
+ return CompareDetails(is_data_property1, attributes1, is_data_property2,
+ attributes2);
+}
+
+
+int TransitionArray::CompareNames(Name* key1, uint32_t hash1, Name* key2,
+ uint32_t hash2) {
+ if (key1 != key2) {
+ // In case of hash collisions key1 is always "less" than key2.
+ return hash1 <= hash2 ? -1 : 1;
+ }
+
+ return 0;
+}
+
+
+int TransitionArray::CompareDetails(bool is_data_property1,
+ PropertyAttributes attributes1,
+ bool is_data_property2,
+ PropertyAttributes attributes2) {
+ if (is_data_property1 != is_data_property2) {
+ return static_cast<int>(is_data_property1) <
+ static_cast<int>(is_data_property2)
+ ? -1
+ : 1;
+ }
+
+ if (attributes1 != attributes2) {
+ return static_cast<int>(attributes1) < static_cast<int>(attributes2) ? -1
+ : 1;
+ }
+
+ return 0;
+}
+
+
+PropertyDetails TransitionArray::GetTargetDetails(Name* name, Map* target) {
+ DCHECK(!IsSpecialTransition(name));
+ int descriptor = target->LastAdded();
+ DescriptorArray* descriptors = target->instance_descriptors();
+ // Transitions are allowed only for the last added property.
+ DCHECK(descriptors->GetKey(descriptor)->Equals(name));
+ return descriptors->GetDetails(descriptor);
+}
+
+
void TransitionArray::NoIncrementalWriteBarrierSet(int transition_number,
Name* key,
Map* target) {
Handle<TransitionArray> result;
Isolate* isolate = name->GetIsolate();
- if (flag == SIMPLE_TRANSITION) {
+ if (flag == SIMPLE_PROPERTY_TRANSITION) {
result = AllocateSimple(isolate, target);
} else {
result = Allocate(isolate, 1);
int number_of_transitions = map->transitions()->number_of_transitions();
int new_nof = number_of_transitions;
- int insertion_index = kNotFound;
- int index = map->transitions()->Search(*name, &insertion_index);
+ bool is_special_transition = flag == SPECIAL_TRANSITION;
+ DCHECK_EQ(is_special_transition, IsSpecialTransition(*name));
+ PropertyDetails details = is_special_transition
+ ? PropertyDetails(NONE, NORMAL, 0)
+ : GetTargetDetails(*name, *target);
+ int insertion_index = kNotFound;
+ int index =
+ is_special_transition
+ ? map->transitions()->SearchSpecial(Symbol::cast(*name),
+ &insertion_index)
+ : map->transitions()->Search(details.type(), *name,
+ details.attributes(), &insertion_index);
if (index == kNotFound) {
++new_nof;
} else {
array->SetNumberOfTransitions(new_nof);
for (index = number_of_transitions; index > insertion_index; --index) {
Name* key = array->GetKey(index - 1);
- DCHECK(key->Hash() > name->Hash());
array->SetKey(index, key);
array->SetTarget(index, array->GetTarget(index - 1));
}
array->SetKey(index, *name);
array->SetTarget(index, *target);
+ SLOW_DCHECK(array->IsSortedNoDuplicates());
return handle(array);
}
new_nof = number_of_transitions;
insertion_index = kNotFound;
- index = array->Search(*name, &insertion_index);
+ index = is_special_transition ? map->transitions()->SearchSpecial(
+ Symbol::cast(*name), &insertion_index)
+ : map->transitions()->Search(
+ details.type(), *name,
+ details.attributes(), &insertion_index);
if (index == kNotFound) {
++new_nof;
} else {
}
result->set_back_pointer_storage(array->back_pointer_storage());
+ SLOW_DCHECK(result->IsSortedNoDuplicates());
return result;
}
+int TransitionArray::SearchDetails(int transition, PropertyType type,
+ PropertyAttributes attributes,
+ int* out_insertion_index) {
+ int nof_transitions = number_of_transitions();
+ DCHECK(transition < nof_transitions);
+ Name* key = GetKey(transition);
+ bool is_data = type == FIELD || type == CONSTANT;
+ for (; transition < nof_transitions && GetKey(transition) == key;
+ transition++) {
+ Map* target = GetTarget(transition);
+ PropertyDetails target_details = GetTargetDetails(key, target);
+
+ bool target_is_data =
+ target_details.type() == FIELD || target_details.type() == CONSTANT;
+
+ int cmp = CompareDetails(is_data, attributes, target_is_data,
+ target_details.attributes());
+ if (cmp == 0) {
+ return transition;
+ } else if (cmp < 0) {
+ break;
+ }
+ }
+ if (out_insertion_index != NULL) *out_insertion_index = transition;
+ return kNotFound;
+}
+
+
+int TransitionArray::Search(PropertyType type, Name* name,
+ PropertyAttributes attributes,
+ int* out_insertion_index) {
+ int transition = SearchName(name, out_insertion_index);
+ if (transition == kNotFound) {
+ return kNotFound;
+ }
+ return SearchDetails(transition, type, attributes, out_insertion_index);
+}
} } // namespace v8::internal
static Handle<TransitionArray> Insert(Handle<Map> map, Handle<Name> name,
Handle<Map> target,
SimpleTransitionFlag flag);
+ // Search a transition for a given type, property name and attributes.
+ int Search(PropertyType type, Name* name, PropertyAttributes attributes,
+ int* out_insertion_index = NULL);
+
+ // Search a non-property transition (like elements kind, observe or frozen
+ // transitions).
+ inline int SearchSpecial(Symbol* symbol, int* out_insertion_index = NULL) {
+ return SearchName(symbol, out_insertion_index);
+ }
- // Search a transition for a given property name.
- inline int Search(Name* name, int* out_insertion_index = NULL);
+ static inline PropertyDetails GetTargetDetails(Name* name, Map* target);
// Allocates a TransitionArray.
static Handle<TransitionArray> Allocate(Isolate* isolate,
bool IsSortedNoDuplicates(int valid_entries = -1);
bool IsConsistentWithBackPointers(Map* current_map);
bool IsEqualTo(TransitionArray* other);
+
+ // Returns true for a non-property transitions like elements kind, observed
+ // or frozen transitions.
+ static inline bool IsSpecialTransition(Name* name);
#endif
// The maximum number of transitions we want in a transition array (should
Handle<Map> target,
SimpleTransitionFlag flag);
+ // Search a first transition for a given property name.
+ inline int SearchName(Name* name, int* out_insertion_index = NULL);
+ int SearchDetails(int transition, PropertyType type,
+ PropertyAttributes attributes, int* out_insertion_index);
+
+ // Compares two tuples <key, is_data_property, attributes>, returns -1 if
+ // tuple1 is "less" than tuple2, 0 if tuple1 equal to tuple2 and 1 otherwise.
+ static inline int CompareKeys(Name* key1, uint32_t hash1,
+ bool is_data_property1,
+ PropertyAttributes attributes1, Name* key2,
+ uint32_t hash2, bool is_data_property2,
+ PropertyAttributes attributes2);
+
+ // Compares keys, returns -1 if key1 is "less" than key2,
+ // 0 if key1 equal to key2 and 1 otherwise.
+ static inline int CompareNames(Name* key1, uint32_t hash1, Name* key2,
+ uint32_t hash2);
+
+ // Compares two details, returns -1 if details1 is "less" than details2,
+ // 0 if details1 equal to details2 and 1 otherwise.
+ static inline int CompareDetails(bool is_data_property1,
+ PropertyAttributes attributes1,
+ bool is_data_property2,
+ PropertyAttributes attributes2);
+
inline void NoIncrementalWriteBarrierSet(int transition_number,
Name* key,
Map* target);
'test-strtod.cc',
'test-thread-termination.cc',
'test-threads.cc',
+ 'test-transitions.cc',
'test-types.cc',
'test-unbound-queue.cc',
'test-unique.cc',
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdlib.h>
+#include <utility>
+
+#include "src/v8.h"
+
+#include "src/compilation-cache.h"
+#include "src/execution.h"
+#include "src/factory.h"
+#include "src/global-handles.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+
+
+//
+// Helper functions.
+//
+
+static void ConnectTransition(Handle<Map> parent,
+ Handle<TransitionArray> transitions,
+ Handle<Map> child) {
+ if (!parent->HasTransitionArray() || *transitions != parent->transitions()) {
+ parent->set_transitions(*transitions);
+ }
+ child->SetBackPointer(*parent);
+}
+
+
+TEST(TransitionArray_SimpleFieldTransitions) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ Handle<String> name1 = factory->InternalizeUtf8String("foo");
+ Handle<String> name2 = factory->InternalizeUtf8String("bar");
+ PropertyAttributes attributes = NONE;
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ Handle<Map> map1 =
+ Map::CopyWithField(map0, name1, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ Handle<Map> map2 =
+ Map::CopyWithField(map0, name2, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ int transition;
+ transitions =
+ transitions->Insert(map0, name1, map1, SIMPLE_PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map1);
+ CHECK(transitions->IsSimpleTransition());
+ transition = transitions->Search(FIELD, *name1, attributes);
+ CHECK_EQ(TransitionArray::kSimpleTransitionIndex, transition);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transitions =
+ transitions->Insert(map0, name2, map2, SIMPLE_PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map2);
+ CHECK(transitions->IsFullTransitionArray());
+
+ transition = transitions->Search(FIELD, *name1, attributes);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transition = transitions->Search(FIELD, *name2, attributes);
+ CHECK_EQ(*name2, transitions->GetKey(transition));
+ CHECK_EQ(*map2, transitions->GetTarget(transition));
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
+
+
+TEST(TransitionArray_FullFieldTransitions) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ Handle<String> name1 = factory->InternalizeUtf8String("foo");
+ Handle<String> name2 = factory->InternalizeUtf8String("bar");
+ PropertyAttributes attributes = NONE;
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ Handle<Map> map1 =
+ Map::CopyWithField(map0, name1, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ Handle<Map> map2 =
+ Map::CopyWithField(map0, name2, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ int transition;
+ transitions = transitions->Insert(map0, name1, map1, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map1);
+ CHECK(transitions->IsFullTransitionArray());
+ transition = transitions->Search(FIELD, *name1, attributes);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transitions = transitions->Insert(map0, name2, map2, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map2);
+ CHECK(transitions->IsFullTransitionArray());
+
+ transition = transitions->Search(FIELD, *name1, attributes);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transition = transitions->Search(FIELD, *name2, attributes);
+ CHECK_EQ(*name2, transitions->GetKey(transition));
+ CHECK_EQ(*map2, transitions->GetTarget(transition));
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
+
+
+TEST(TransitionArray_DifferentFieldNames) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ const int PROPS_COUNT = 10;
+ Handle<String> names[PROPS_COUNT];
+ Handle<Map> maps[PROPS_COUNT];
+ PropertyAttributes attributes = NONE;
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ EmbeddedVector<char, 64> buffer;
+ SNPrintF(buffer, "prop%d", i);
+ Handle<String> name = factory->InternalizeUtf8String(buffer.start());
+ Handle<Map> map =
+ Map::CopyWithField(map0, name, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ names[i] = name;
+ maps[i] = map;
+
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
+ }
+
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ int transition = transitions->Search(FIELD, *names[i], attributes);
+ CHECK_EQ(*names[i], transitions->GetKey(transition));
+ CHECK_EQ(*maps[i], transitions->GetTarget(transition));
+ }
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
+
+
+TEST(TransitionArray_SameFieldNamesDifferentAttributesSimple) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1;
+ STATIC_ASSERT(ATTRS_COUNT == 8);
+ Handle<Map> attr_maps[ATTRS_COUNT];
+ Handle<String> name = factory->InternalizeUtf8String("foo");
+
+ // Add transitions for same field name but different attributes.
+ for (int i = 0; i < ATTRS_COUNT; i++) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ Handle<Map> map =
+ Map::CopyWithField(map0, name, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ attr_maps[i] = map;
+
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
+ }
+
+ // Ensure that transitions for |name| field are valid.
+ for (int i = 0; i < ATTRS_COUNT; i++) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ int transition = transitions->Search(FIELD, *name, attributes);
+ CHECK_EQ(*name, transitions->GetKey(transition));
+ CHECK_EQ(*attr_maps[i], transitions->GetTarget(transition));
+ }
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
+
+
+TEST(TransitionArray_SameFieldNamesDifferentAttributes) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ const int PROPS_COUNT = 10;
+ Handle<String> names[PROPS_COUNT];
+ Handle<Map> maps[PROPS_COUNT];
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ // Some number of fields.
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ EmbeddedVector<char, 64> buffer;
+ SNPrintF(buffer, "prop%d", i);
+ Handle<String> name = factory->InternalizeUtf8String(buffer.start());
+ Handle<Map> map =
+ Map::CopyWithField(map0, name, handle(HeapType::Any(), isolate), NONE,
+ Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ names[i] = name;
+ maps[i] = map;
+
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
+ }
+
+ const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1;
+ STATIC_ASSERT(ATTRS_COUNT == 8);
+ Handle<Map> attr_maps[ATTRS_COUNT];
+ Handle<String> name = factory->InternalizeUtf8String("foo");
+
+ // Add transitions for same field name but different attributes.
+ for (int i = 0; i < ATTRS_COUNT; i++) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ Handle<Map> map =
+ Map::CopyWithField(map0, name, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ attr_maps[i] = map;
+
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
+ }
+
+ // Ensure that transitions for |name| field are valid.
+ for (int i = 0; i < ATTRS_COUNT; i++) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ int transition = transitions->Search(FIELD, *name, attributes);
+ CHECK_EQ(*name, transitions->GetKey(transition));
+ CHECK_EQ(*attr_maps[i], transitions->GetTarget(transition));
+ }
+
+ // Ensure that info about the other fields still valid.
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ int transition = transitions->Search(FIELD, *names[i], NONE);
+ CHECK_EQ(*names[i], transitions->GetKey(transition));
+ CHECK_EQ(*maps[i], transitions->GetTarget(transition));
+ }
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
Object.defineProperty(o, "foo", { set: setter, configurable: true });
Object.defineProperty(o2, "foo", { set: setter, configurable: true });
-assertTrue(%HaveSameMap(o, o2));
+// TODO(ishell): this should eventually become assertTrue().
+assertFalse(%HaveSameMap(o, o2));