void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
- Object* raw_transitions = map->raw_transitions();
- if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
- TransitionArray* transitions = TransitionArray::cast(raw_transitions);
+ if (map->HasTransitionArray()) {
+ TransitionArray* transitions = map->transitions();
int transitions_entry = GetEntry(transitions)->index();
if (FLAG_collect_maps && map->CanTransition()) {
- if (transitions->HasPrototypeTransitions()) {
- FixedArray* prototype_transitions =
- transitions->GetPrototypeTransitions();
- MarkAsWeakContainer(prototype_transitions);
- TagObject(prototype_transitions, "(prototype transitions");
- SetInternalReference(transitions, transitions_entry,
- "prototype_transitions", prototype_transitions);
+ if (!transitions->IsSimpleTransition()) {
+ if (transitions->HasPrototypeTransitions()) {
+ FixedArray* prototype_transitions =
+ transitions->GetPrototypeTransitions();
+ MarkAsWeakContainer(prototype_transitions);
+ TagObject(prototype_transitions, "(prototype transitions");
+ SetInternalReference(transitions, transitions_entry,
+ "prototype_transitions", prototype_transitions);
+ }
+ // TODO(alph): transitions keys are strong links.
+ MarkAsWeakContainer(transitions);
}
- // TODO(alph): transitions keys are strong links.
- MarkAsWeakContainer(transitions);
}
TagObject(transitions, "(transition array)");
SetInternalReference(map, entry, "transitions", transitions,
Map::kTransitionsOffset);
- } else if (TransitionArray::IsSimpleTransition(raw_transitions)) {
- TagObject(raw_transitions, "(transition)");
- SetInternalReference(map, entry, "transition", raw_transitions,
- Map::kTransitionsOffset);
}
DescriptorArray* descriptors = map->instance_descriptors();
TagObject(descriptors, "(map descriptors)");
map->set_dependent_code(DependentCode::cast(empty_fixed_array()),
SKIP_WRITE_BARRIER);
map->set_weak_cell_cache(Smi::FromInt(0));
- map->set_raw_transitions(Smi::FromInt(0));
+ map->init_transitions(undefined_value());
map->set_unused_property_fields(0);
map->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
// Fix the instance_descriptors for the existing maps.
meta_map()->set_code_cache(empty_fixed_array());
meta_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
- meta_map()->set_raw_transitions(Smi::FromInt(0));
+ meta_map()->init_transitions(undefined_value());
meta_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
meta_map()->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
fixed_array_map()->set_code_cache(empty_fixed_array());
fixed_array_map()->set_dependent_code(
DependentCode::cast(empty_fixed_array()));
- fixed_array_map()->set_raw_transitions(Smi::FromInt(0));
+ fixed_array_map()->init_transitions(undefined_value());
fixed_array_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
fixed_array_map()->set_layout_descriptor(
undefined_map()->set_code_cache(empty_fixed_array());
undefined_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
- undefined_map()->set_raw_transitions(Smi::FromInt(0));
+ undefined_map()->init_transitions(undefined_value());
undefined_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
undefined_map()->set_layout_descriptor(
null_map()->set_code_cache(empty_fixed_array());
null_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
- null_map()->set_raw_transitions(Smi::FromInt(0));
+ null_map()->init_transitions(undefined_value());
null_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
null_map()->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
constant_pool_array_map()->set_code_cache(empty_fixed_array());
constant_pool_array_map()->set_dependent_code(
DependentCode::cast(empty_fixed_array()));
- constant_pool_array_map()->set_raw_transitions(Smi::FromInt(0));
+ constant_pool_array_map()->init_transitions(undefined_value());
constant_pool_array_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
constant_pool_array_map()->set_layout_descriptor(
heap->RecordFixedArraySubTypeStats(DESCRIPTOR_ARRAY_SUB_TYPE,
fixed_array_size);
}
- if (TransitionArray::IsFullTransitionArray(map_obj->raw_transitions())) {
- int fixed_array_size =
- TransitionArray::cast(map_obj->raw_transitions())->Size();
+ if (map_obj->HasTransitionArray()) {
+ int fixed_array_size = map_obj->transitions()->Size();
heap->RecordFixedArraySubTypeStats(TRANSITION_ARRAY_SUB_TYPE,
fixed_array_size);
}
void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
- FixedArray* prototype_transitions =
- TransitionArray::GetPrototypeTransitions(map);
- int number_of_transitions =
- TransitionArray::NumberOfPrototypeTransitions(prototype_transitions);
+ int number_of_transitions = map->NumberOfProtoTransitions();
+ FixedArray* prototype_transitions = map->GetPrototypeTransitions();
- const int header = TransitionArray::kProtoTransitionHeaderSize;
+ const int header = Map::kProtoTransitionHeaderSize;
int new_number_of_transitions = 0;
for (int i = 0; i < number_of_transitions; i++) {
Object* cached_map = prototype_transitions->get(header + i);
}
if (new_number_of_transitions != number_of_transitions) {
- TransitionArray::SetNumberOfPrototypeTransitions(prototype_transitions,
- new_number_of_transitions);
+ map->SetNumberOfProtoTransitions(new_number_of_transitions);
}
// Fill slots that became free with undefined value.
bool current_is_alive = map_mark.Get();
bool parent_is_alive = Marking::MarkBitFrom(parent).Get();
if (!current_is_alive && parent_is_alive) {
- ClearMapTransitions(parent, map);
+ ClearMapTransitions(parent);
}
}
}
-void MarkCompactCollector::ClearMapTransitions(Map* map, Map* dead_transition) {
- Object* transitions = map->raw_transitions();
- int num_transitions = TransitionArray::NumberOfTransitions(transitions);
+void MarkCompactCollector::ClearMapTransitions(Map* map) {
+ // If there are no transitions to be cleared, return.
+ // TODO(verwaest) Should be an assert, otherwise back pointers are not
+ // properly cleared.
+ if (!map->HasTransitionArray()) return;
- int number_of_own_descriptors = map->NumberOfOwnDescriptors();
- DescriptorArray* descriptors = map->instance_descriptors();
-
- // A previously existing simple transition (stored in a WeakCell) may have
- // been cleared. Clear the useless cell pointer, and take ownership
- // of the descriptor array.
- if (transitions->IsWeakCell() && WeakCell::cast(transitions)->cleared()) {
- map->set_raw_transitions(Smi::FromInt(0));
- }
- if (num_transitions == 0 &&
- descriptors == dead_transition->instance_descriptors() &&
- number_of_own_descriptors > 0) {
- TrimDescriptorArray(map, descriptors, number_of_own_descriptors);
- DCHECK(descriptors->number_of_descriptors() == number_of_own_descriptors);
- map->set_owns_descriptors(true);
- return;
- }
+ TransitionArray* t = map->transitions();
int transition_index = 0;
+ DescriptorArray* descriptors = map->instance_descriptors();
bool descriptors_owner_died = false;
// Compact all live descriptors to the left.
- for (int i = 0; i < num_transitions; ++i) {
- Map* target = TransitionArray::GetTarget(transitions, i);
+ for (int i = 0; i < t->number_of_transitions(); ++i) {
+ Map* target = t->GetTarget(i);
if (ClearMapBackPointer(target)) {
if (target->instance_descriptors() == descriptors) {
descriptors_owner_died = true;
}
} else {
if (i != transition_index) {
- DCHECK(TransitionArray::IsFullTransitionArray(transitions));
- TransitionArray* t = TransitionArray::cast(transitions);
Name* key = t->GetKey(i);
t->SetKey(transition_index, key);
Object** key_slot = t->GetKeySlot(transition_index);
// If there are no transitions to be cleared, return.
// TODO(verwaest) Should be an assert, otherwise back pointers are not
// properly cleared.
- if (transition_index == num_transitions) return;
+ if (transition_index == t->number_of_transitions()) return;
+
+ int number_of_own_descriptors = map->NumberOfOwnDescriptors();
if (descriptors_owner_died) {
if (number_of_own_descriptors > 0) {
// such that number_of_transitions() == 0. If this assumption changes,
// TransitionArray::Insert() will need to deal with the case that a transition
// array disappeared during GC.
- int trim = TransitionArray::Capacity(transitions) - transition_index;
+ int trim = t->number_of_transitions_storage() - transition_index;
if (trim > 0) {
- // Non-full-TransitionArray cases can never reach this point.
- DCHECK(TransitionArray::IsFullTransitionArray(transitions));
- TransitionArray* t = TransitionArray::cast(transitions);
heap_->RightTrimFixedArray<Heap::FROM_GC>(
- t, trim * TransitionArray::kTransitionSize);
+ t, t->IsSimpleTransition() ? trim
+ : trim * TransitionArray::kTransitionSize);
t->SetNumberOfTransitions(transition_index);
- // The map still has a full transition array.
- DCHECK(TransitionArray::IsFullTransitionArray(map->raw_transitions()));
}
+ DCHECK(map->HasTransitionArray());
}
EvacuateNewSpaceAndCandidates();
- // ClearNonLiveReferences depends on precise sweeping of map space to
+ // ClearNonLiveTransitions depends on precise sweeping of map space to
// detect whether unmarked map became dead in this collection or in one
// of the previous ones.
{
void ClearNonLiveReferences();
void ClearNonLivePrototypeTransitions(Map* map);
void ClearNonLiveMapTransitions(Map* map, MarkBit map_mark);
- void ClearMapTransitions(Map* map, Map* dead_transition);
+ void ClearMapTransitions(Map* map);
bool ClearMapBackPointer(Map* map);
void TrimDescriptorArray(Map* map, DescriptorArray* descriptors,
int number_of_own_descriptors);
template <typename StaticVisitor>
void StaticMarkingVisitor<StaticVisitor>::MarkMapContents(Heap* heap,
Map* map) {
- Object* raw_transitions = map->raw_transitions();
- if (TransitionArray::IsSimpleTransition(raw_transitions)) {
- StaticVisitor::VisitPointer(
- heap, HeapObject::RawField(map, Map::kTransitionsOffset));
- }
- if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
- MarkTransitionArray(heap, TransitionArray::cast(raw_transitions));
+ // Make sure that the back pointer stored either in the map itself or
+ // inside its transitions array is marked. Skip recording the back
+ // pointer slot since map space is not compacted.
+ StaticVisitor::MarkObject(heap, HeapObject::cast(map->GetBackPointer()));
+
+ // Treat pointers in the transitions array as weak and also mark that
+ // array to prevent visiting it later. Skip recording the transition
+ // array slot, since it will be implicitly recorded when the pointer
+ // fields of this map are visited.
+ if (map->HasTransitionArray()) {
+ TransitionArray* transitions = map->transitions();
+ MarkTransitionArray(heap, transitions);
}
// Since descriptor arrays are potentially shared, ensure that only the
Heap* heap, TransitionArray* transitions) {
if (!StaticVisitor::MarkObjectWithoutPush(heap, transitions)) return;
+ // Simple transitions do not have keys nor prototype transitions.
+ if (transitions->IsSimpleTransition()) return;
+
if (transitions->HasPrototypeTransitions()) {
// Mark prototype transitions array but do not push it onto marking
// stack, this will make references from it weak. We will clean dead
- // prototype transitions in ClearNonLiveReferences.
+ // prototype transitions in ClearNonLiveTransitions.
Object** slot = transitions->GetPrototypeTransitionsSlot();
HeapObject* obj = HeapObject::cast(*slot);
heap->mark_compact_collector()->RecordSlot(slot, slot, obj);
StaticVisitor::MarkObjectWithoutPush(heap, obj);
}
- int num_transitions = TransitionArray::NumberOfTransitions(transitions);
- for (int i = 0; i < num_transitions; ++i) {
+ for (int i = 0; i < transitions->number_of_transitions(); ++i) {
StaticVisitor::VisitPointer(heap, transitions->GetKeySlot(i));
}
}
number_ = number;
}
void LookupTransition(Map* map, Name* name, PropertyAttributes attributes) {
- Map* target =
- TransitionArray::SearchTransition(map, kData, name, attributes);
- if (target == NULL) return NotFound();
+ int transition_index = map->SearchTransition(kData, name, attributes);
+ if (transition_index == TransitionArray::kNotFound) return NotFound();
lookup_type_ = TRANSITION_TYPE;
- transition_ = handle(target);
+ transition_ = handle(map->GetTransition(transition_index));
number_ = transition_->LastAdded();
details_ = transition_->instance_descriptors()->GetDetails(number_);
}
bool follow_expected = false;
Handle<Map> target;
if (seq_one_byte) {
- key = TransitionArray::ExpectedTransitionKey(map);
+ key = Map::ExpectedTransitionKey(map);
follow_expected = !key.is_null() && ParseJsonString(key);
}
// If the expected transition hits, follow it.
if (follow_expected) {
- target = TransitionArray::ExpectedTransitionTarget(map);
+ target = Map::ExpectedTransitionTarget(map);
} else {
// If the expected transition failed, parse an internalized string and
// try to find a matching transition.
key = ParseJsonInternalizedString();
if (key.is_null()) return ReportUnexpectedCharacter();
- target = TransitionArray::FindTransitionToField(map, key);
+ target = Map::FindTransitionToField(map, key);
// If a transition was found, follow it and continue.
transitioning = !target.is_null();
}
VerifyHeapPointer(prototype());
VerifyHeapPointer(instance_descriptors());
SLOW_DCHECK(instance_descriptors()->IsSortedNoDuplicates());
- SLOW_DCHECK(TransitionArray::IsSortedNoDuplicates(this));
- SLOW_DCHECK(TransitionArray::IsConsistentWithBackPointers(this));
+ if (HasTransitionArray()) {
+ SLOW_DCHECK(transitions()->IsSortedNoDuplicates());
+ SLOW_DCHECK(transitions()->IsConsistentWithBackPointers(this));
+ }
// TODO(ishell): turn it back to SLOW_DCHECK.
CHECK(!FLAG_unbox_double_fields ||
layout_descriptor()->IsConsistentWithMap(this));
if (!FLAG_omit_map_checks_for_leaf_maps) return;
if (!is_stable() ||
is_deprecated() ||
+ HasTransitionArray() ||
is_dictionary_map()) {
CHECK_EQ(0, dependent_code()->number_of_entries(
DependentCode::kPrototypeCheckGroup));
}
-// static
-bool TransitionArray::IsSortedNoDuplicates(Map* map) {
- Object* raw_transitions = map->raw_transitions();
- if (IsFullTransitionArray(raw_transitions)) {
- return TransitionArray::cast(raw_transitions)->IsSortedNoDuplicates();
- }
- // Simple and non-existent transitions are always sorted.
- return true;
-}
-
-
static bool CheckOneBackPointer(Map* current_map, Object* target) {
return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map;
}
-// static
-bool TransitionArray::IsConsistentWithBackPointers(Map* map) {
- Object* transitions = map->raw_transitions();
- for (int i = 0; i < TransitionArray::NumberOfTransitions(transitions); ++i) {
- Map* target = TransitionArray::GetTarget(transitions, i);
- if (!CheckOneBackPointer(map, target)) return false;
+bool TransitionArray::IsConsistentWithBackPointers(Map* current_map) {
+ for (int i = 0; i < number_of_transitions(); ++i) {
+ if (!CheckOneBackPointer(current_map, GetTarget(i))) return false;
}
return true;
}
}
+Handle<String> Map::ExpectedTransitionKey(Handle<Map> map) {
+ DisallowHeapAllocation no_gc;
+ if (!map->HasTransitionArray()) return Handle<String>::null();
+ TransitionArray* transitions = map->transitions();
+ if (!transitions->IsSimpleTransition()) return Handle<String>::null();
+ int transition = TransitionArray::kSimpleTransitionIndex;
+ PropertyDetails details = transitions->GetTargetDetails(transition);
+ Name* name = transitions->GetKey(transition);
+ if (details.type() != DATA) return Handle<String>::null();
+ if (details.attributes() != NONE) return Handle<String>::null();
+ if (!name->IsString()) return Handle<String>::null();
+ return Handle<String>(String::cast(name));
+}
+
+
+Handle<Map> Map::ExpectedTransitionTarget(Handle<Map> map) {
+ DCHECK(!ExpectedTransitionKey(map).is_null());
+ return Handle<Map>(map->transitions()->GetTarget(
+ TransitionArray::kSimpleTransitionIndex));
+}
+
+
+Handle<Map> Map::FindTransitionToField(Handle<Map> map, Handle<Name> key) {
+ DisallowHeapAllocation no_allocation;
+ if (!map->HasTransitionArray()) return Handle<Map>::null();
+ TransitionArray* transitions = map->transitions();
+ int transition = transitions->Search(kData, *key, NONE);
+ if (transition == TransitionArray::kNotFound) return Handle<Map>::null();
+ PropertyDetails details = transitions->GetTargetDetails(transition);
+ if (details.type() != DATA) return Handle<Map>::null();
+ DCHECK_EQ(NONE, details.attributes());
+ return Handle<Map>(transitions->GetTarget(transition));
+}
+
+
ACCESSORS(Oddball, to_string, String, kToStringOffset)
ACCESSORS(Oddball, to_number, Object, kToNumberOffset)
return empty_array;
} else if (has_fixed_typed_array_elements()) {
FixedTypedArrayBase* empty_array =
- GetHeap()->EmptyFixedTypedArrayForMap(this);
+ GetHeap()->EmptyFixedTypedArrayForMap(this);
DCHECK(!GetHeap()->InNewSpace(empty_array));
return empty_array;
} else {
}
+// If the descriptor is using the empty transition array, install a new empty
+// transition array that will have place for an element transition.
+static void EnsureHasTransitionArray(Handle<Map> map) {
+ Handle<TransitionArray> transitions;
+ if (!map->HasTransitionArray()) {
+ transitions = TransitionArray::Allocate(map->GetIsolate(), 0);
+ transitions->set_back_pointer_storage(map->GetBackPointer());
+ } else if (!map->transitions()->IsFullTransitionArray()) {
+ transitions = TransitionArray::ExtendToFullTransitionArray(map);
+ } else {
+ return;
+ }
+ map->set_transitions(*transitions);
+}
+
+
LayoutDescriptor* Map::layout_descriptor_gc_safe() {
Object* layout_desc = READ_FIELD(this, kLayoutDecriptorOffset);
return LayoutDescriptor::cast_gc_safe(layout_desc);
}
-Map* Map::ElementsTransitionMap() {
- return TransitionArray::SearchSpecial(
- this, GetHeap()->elements_transition_symbol());
+bool Map::HasElementsTransition() {
+ return HasTransitionArray() && transitions()->HasElementsTransition();
+}
+
+
+bool Map::HasTransitionArray() const {
+ Object* object = READ_FIELD(this, kTransitionsOffset);
+ return object->IsTransitionArray();
+}
+
+
+Map* Map::elements_transition_map() {
+ int index =
+ transitions()->SearchSpecial(GetHeap()->elements_transition_symbol());
+ return transitions()->GetTarget(index);
}
-ACCESSORS(Map, raw_transitions, Object, kTransitionsOffset)
+bool Map::CanHaveMoreTransitions() {
+ if (!HasTransitionArray()) return true;
+ return transitions()->number_of_transitions() <
+ TransitionArray::kMaxNumberOfTransitions;
+}
+
+
+Map* Map::GetTransition(int transition_index) {
+ return transitions()->GetTarget(transition_index);
+}
+
+
+int Map::SearchSpecialTransition(Symbol* name) {
+ if (HasTransitionArray()) {
+ return transitions()->SearchSpecial(name);
+ }
+ return TransitionArray::kNotFound;
+}
+
+
+int Map::SearchTransition(PropertyKind kind, Name* name,
+ PropertyAttributes attributes) {
+ if (HasTransitionArray()) {
+ return transitions()->Search(kind, name, attributes);
+ }
+ return TransitionArray::kNotFound;
+}
+
+
+FixedArray* Map::GetPrototypeTransitions() {
+ if (!HasTransitionArray()) return GetHeap()->empty_fixed_array();
+ if (!transitions()->HasPrototypeTransitions()) {
+ return GetHeap()->empty_fixed_array();
+ }
+ return transitions()->GetPrototypeTransitions();
+}
+
+
+void Map::SetPrototypeTransitions(
+ Handle<Map> map, Handle<FixedArray> proto_transitions) {
+ EnsureHasTransitionArray(map);
+ int old_number_of_transitions = map->NumberOfProtoTransitions();
+ if (Heap::ShouldZapGarbage() && map->HasPrototypeTransitions()) {
+ DCHECK(map->GetPrototypeTransitions() != *proto_transitions);
+ map->ZapPrototypeTransitions();
+ }
+ map->transitions()->SetPrototypeTransitions(*proto_transitions);
+ map->SetNumberOfProtoTransitions(old_number_of_transitions);
+}
+
+
+bool Map::HasPrototypeTransitions() {
+ return HasTransitionArray() && transitions()->HasPrototypeTransitions();
+}
+
+
+TransitionArray* Map::transitions() const {
+ DCHECK(HasTransitionArray());
+ Object* object = READ_FIELD(this, kTransitionsOffset);
+ return TransitionArray::cast(object);
+}
+
+
+void Map::set_transitions(TransitionArray* transition_array,
+ WriteBarrierMode mode) {
+ // Transition arrays are not shared. When one is replaced, it should not
+ // keep referenced objects alive, so we zap it.
+ // When there is another reference to the array somewhere (e.g. a handle),
+ // not zapping turns from a waste of memory into a source of crashes.
+ if (HasTransitionArray()) {
+#ifdef DEBUG
+ for (int i = 0; i < transitions()->number_of_transitions(); i++) {
+ Map* target = transitions()->GetTarget(i);
+ if (target->instance_descriptors() == instance_descriptors()) {
+ Name* key = transitions()->GetKey(i);
+ 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.kind(), key,
+ details.attributes());
+ }
+ DCHECK_NE(TransitionArray::kNotFound, new_target_index);
+ DCHECK_EQ(target, transition_array->GetTarget(new_target_index));
+ }
+ }
+#endif
+ DCHECK(transitions() != transition_array);
+ ZapTransitions();
+ }
+
+ WRITE_FIELD(this, kTransitionsOffset, transition_array);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kTransitionsOffset,
+ transition_array, mode);
+}
+
+
+void Map::init_transitions(Object* undefined) {
+ DCHECK(undefined->IsUndefined());
+ WRITE_FIELD(this, kTransitionsOffset, undefined);
+}
void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
ACCESSORS(Map, constructor_or_backpointer, Object,
kConstructorOrBackPointerOffset)
-
Object* Map::GetConstructor() const {
Object* maybe_constructor = constructor_or_backpointer();
// Follow any back pointers.
}
return maybe_constructor;
}
-
-
void Map::SetConstructor(Object* constructor, WriteBarrierMode mode) {
// Never overwrite a back pointer with a constructor.
DCHECK(!constructor_or_backpointer()->IsMap());
set_constructor_or_backpointer(constructor, mode);
}
-
ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset)
ACCESSORS(JSFunction, literals_or_bindings, FixedArray, kLiteralsOffset)
ACCESSORS(JSFunction, next_function_link, Object, kNextFunctionLinkOffset)
if (FLAG_unbox_double_fields) {
os << "\n - layout descriptor: " << Brief(layout_descriptor());
}
- if (TransitionArray::NumberOfTransitions(raw_transitions()) > 0) {
- os << "\n - transitions: ";
- TransitionArray::PrintTransitions(os, raw_transitions());
+ if (HasTransitionArray()) {
+ os << "\n - transitions: " << Brief(transitions());
}
os << "\n - prototype: " << Brief(prototype());
os << "\n - constructor: " << Brief(GetConstructor());
void TransitionArray::Print() {
OFStream os(stdout);
- TransitionArray::PrintTransitions(os, this);
+ this->PrintTransitions(os);
os << std::flush;
}
-void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
+void TransitionArray::PrintTransitions(std::ostream& os,
bool print_header) { // NOLINT
- int num_transitions = NumberOfTransitions(transitions);
if (print_header) {
- os << "Transition array " << num_transitions << "\n";
+ os << "Transition array " << number_of_transitions() << "\n";
}
- for (int i = 0; i < num_transitions; i++) {
- Name* key = GetKey(transitions, i);
- Map* target = GetTarget(transitions, i);
+ for (int i = 0; i < number_of_transitions(); i++) {
+ Name* key = GetKey(i);
+ Map* target = GetTarget(i);
os << " ";
#ifdef OBJECT_PRINT
key->NamePrint(os);
key->ShortPrint(os);
#endif
os << ": ";
- Heap* heap = key->GetHeap();
- if (key == heap->nonextensible_symbol()) {
+ if (key == GetHeap()->nonextensible_symbol()) {
os << " (transition to non-extensible)";
- } else if (key == heap->sealed_symbol()) {
+ } else if (key == GetHeap()->sealed_symbol()) {
os << " (transition to sealed)";
- } else if (key == heap->frozen_symbol()) {
+ } else if (key == GetHeap()->frozen_symbol()) {
os << " (transition to frozen)";
- } else if (key == heap->elements_transition_symbol()) {
+ } else if (key == GetHeap()->elements_transition_symbol()) {
os << " (transition to " << ElementsKindToString(target->elements_kind())
<< ")";
- } else if (key == heap->observed_symbol()) {
+ } else if (key == GetHeap()->observed_symbol()) {
os << " (transition to Object.observe)";
} else {
PropertyDetails details = GetTargetDetails(key, target);
}
os << (details.kind() == kData ? "data" : "accessor");
if (details.location() == kDescriptor) {
- Object* value =
- target->instance_descriptors()->GetValue(target->LastAdded());
- os << " " << Brief(value);
+ os << " " << Brief(GetTargetValue(i));
}
os << "), attrs: " << details.attributes();
}
void JSObject::PrintTransitions(std::ostream& os) { // NOLINT
- TransitionArray::PrintTransitions(os, map()->raw_transitions());
+ if (!map()->HasTransitionArray()) return;
+ map()->transitions()->PrintTransitions(os, false);
}
#endif // defined(DEBUG) || defined(OBJECT_PRINT)
} } // namespace v8::internal
old_map->GetHeap()->empty_descriptor_array(),
LayoutDescriptor::FastPointerLayout());
// Ensure that no transition was inserted for prototype migrations.
- DCHECK_EQ(0, TransitionArray::NumberOfTransitions(
- old_map->raw_transitions()));
+ DCHECK(!old_map->HasTransitionArray());
DCHECK(new_map->GetBackPointer()->IsUndefined());
}
} else {
void Map::DeprecateTransitionTree() {
if (is_deprecated()) return;
- Object* transitions = raw_transitions();
- int num_transitions = TransitionArray::NumberOfTransitions(transitions);
- for (int i = 0; i < num_transitions; ++i) {
- TransitionArray::GetTarget(transitions, i)->DeprecateTransitionTree();
+ if (HasTransitionArray()) {
+ TransitionArray* transitions = this->transitions();
+ for (int i = 0; i < transitions->number_of_transitions(); i++) {
+ transitions->GetTarget(i)->DeprecateTransitionTree();
+ }
}
deprecate();
dependent_code()->DeoptimizeDependentCodeGroup(
DescriptorArray* new_descriptors,
LayoutDescriptor* new_layout_descriptor) {
bool transition_target_deprecated = false;
- Map* maybe_transition =
- TransitionArray::SearchTransition(this, kind, key, attributes);
- if (maybe_transition != NULL) {
- maybe_transition->DeprecateTransitionTree();
- transition_target_deprecated = true;
+ if (HasTransitionArray()) {
+ TransitionArray* transitions = this->transitions();
+ int transition = transitions->Search(kind, key, attributes);
+ if (transition != TransitionArray::kNotFound) {
+ transitions->GetTarget(transition)->DeprecateTransitionTree();
+ transition_target_deprecated = true;
+ }
}
// Don't overwrite the empty descriptor array.
Map* current = this;
for (int i = verbatim; i < length; i++) {
+ if (!current->HasTransitionArray()) break;
Name* name = descriptors->GetKey(i);
PropertyDetails details = descriptors->GetDetails(i);
- Map* next = TransitionArray::SearchTransition(current, details.kind(), name,
- details.attributes());
- if (next == NULL) break;
+ TransitionArray* transitions = current->transitions();
+ int transition =
+ transitions->Search(details.kind(), name, details.attributes());
+ if (transition == TransitionArray::kNotFound) break;
+
+ Map* next = transitions->GetTarget(transition);
DescriptorArray* next_descriptors = next->instance_descriptors();
PropertyDetails next_details = next_descriptors->GetDetails(i);
DisallowHeapAllocation no_allocation;
PropertyDetails details = instance_descriptors()->GetDetails(descriptor);
if (details.type() != DATA) return;
- Object* transitions = raw_transitions();
- int num_transitions = TransitionArray::NumberOfTransitions(transitions);
- for (int i = 0; i < num_transitions; ++i) {
- Map* target = TransitionArray::GetTarget(transitions, i);
- target->UpdateFieldType(descriptor, name, new_representation,
- new_wrapped_type);
+ if (HasTransitionArray()) {
+ TransitionArray* transitions = this->transitions();
+ for (int i = 0; i < transitions->number_of_transitions(); ++i) {
+ transitions->GetTarget(i)->UpdateFieldType(
+ descriptor, name, new_representation, new_wrapped_type);
+ }
}
// It is allowed to change representation here only from None to something.
DCHECK(details.representation().Equals(new_representation) ||
next_attributes = old_details.attributes();
next_representation = old_details.representation();
}
- Map* transition = TransitionArray::SearchTransition(
- *target_map, next_kind, old_descriptors->GetKey(i), next_attributes);
- if (transition == NULL) break;
- Handle<Map> tmp_map(transition, isolate);
-
+ 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(
tmp_map->instance_descriptors(), isolate);
next_kind = old_details.kind();
next_attributes = old_details.attributes();
}
- Map* transition = TransitionArray::SearchTransition(
- *target_map, next_kind, old_descriptors->GetKey(i), next_attributes);
- if (transition == NULL) break;
- Handle<Map> tmp_map(transition, isolate);
+ 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);
// 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 &&
- !TransitionArray::CanHaveMoreTransitions(split_map)) {
+ if (!transition_target_deprecated && !split_map->CanHaveMoreTransitions()) {
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
new_kind, new_attributes,
"GenAll_CantHaveMoreTransitions");
Map* new_map = root_map;
for (int i = root_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
- Map* transition = TransitionArray::SearchTransition(
- new_map, old_details.kind(), old_descriptors->GetKey(i),
- old_details.attributes());
- if (transition == NULL) return MaybeHandle<Map>();
- new_map = transition;
+ int j = new_map->SearchTransition(old_details.kind(),
+ 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);
// have the cached transition.
if (IsExternalArrayElementsKind(to_kind) &&
!IsFixedTypedArrayElementsKind(map->elements_kind())) {
- Map* next_map = map->ElementsTransitionMap();
- if (next_map != NULL && next_map->elements_kind() == to_kind) {
- return next_map;
+ if (map->HasElementsTransition()) {
+ Map* next_map = map->elements_transition_map();
+ if (next_map->elements_kind() == to_kind) return next_map;
}
return map;
}
ElementsKind kind = map->elements_kind();
while (kind != target_kind) {
kind = GetNextTransitionElementsKind(kind);
- Map* next_map = current_map->ElementsTransitionMap();
- if (next_map == NULL) return current_map;
- current_map = next_map;
+ if (!current_map->HasElementsTransition()) return current_map;
+ current_map = current_map->elements_transition_map();
}
- Map* next_map = current_map->ElementsTransitionMap();
- if (to_kind != kind && next_map != NULL) {
+ if (to_kind != kind && current_map->HasElementsTransition()) {
DCHECK(to_kind == DICTIONARY_ELEMENTS);
+ Map* next_map = current_map->elements_transition_map();
if (next_map->elements_kind() == to_kind) return next_map;
}
}
Handle<Map> old_map(object->map(), isolate);
- Map* transition =
- TransitionArray::SearchSpecial(*old_map, *transition_marker);
- if (transition != NULL) {
- Handle<Map> transition_map(transition, isolate);
+ int transition_index = old_map->SearchSpecialTransition(*transition_marker);
+ if (transition_index != TransitionArray::kNotFound) {
+ Handle<Map> transition_map(old_map->GetTransition(transition_index));
DCHECK(transition_map->has_dictionary_elements());
DCHECK(!transition_map->is_extensible());
JSObject::MigrateToMap(object, transition_map);
- } else if (object->HasFastProperties() &&
- TransitionArray::CanHaveMoreTransitions(old_map)) {
+ } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
// Create a new descriptor array with the appropriate property attributes
Handle<Map> new_map = Map::CopyForPreventExtensions(
old_map, attrs, transition_marker, "CopyForPreventExtensions");
Handle<Map> new_map;
Handle<Map> old_map(object->map(), isolate);
DCHECK(!old_map->is_observed());
- Map* transition = TransitionArray::SearchSpecial(
- *old_map, isolate->heap()->observed_symbol());
- if (transition != NULL) {
- new_map = handle(transition, isolate);
+ 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());
- } else if (object->HasFastProperties() &&
- TransitionArray::CanHaveMoreTransitions(old_map)) {
+ } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
new_map = Map::CopyForObserved(old_map);
} else {
new_map = Map::Copy(old_map, "SlowObserved");
// static
void Map::TraceAllTransitions(Map* map) {
- Object* transitions = map->raw_transitions();
- int num_transitions = TransitionArray::NumberOfTransitions(transitions);
- for (int i = -0; i < num_transitions; ++i) {
- Map* target = TransitionArray::GetTarget(transitions, i);
- Name* key = TransitionArray::GetKey(transitions, i);
- Map::TraceTransition("Transition", map, target, key);
+ if (!map->HasTransitionArray()) return;
+ TransitionArray* transitions = map->transitions();
+ for (int i = 0; i < transitions->number_of_transitions(); ++i) {
+ Map* target = transitions->GetTarget(i);
+ Map::TraceTransition("Transition", map, target, transitions->GetKey(i));
Map::TraceAllTransitions(target);
}
}
Map::TraceTransition("NoTransition", *parent, *child, *name);
#endif
} else {
- TransitionArray::Insert(parent, name, child, flag);
+ Handle<TransitionArray> transitions =
+ TransitionArray::Insert(parent, name, child, flag);
+ if (!parent->HasTransitionArray() ||
+ *transitions != parent->transitions()) {
+ parent->set_transitions(*transitions);
+ }
+ child->SetBackPointer(*parent);
if (child->prototype()->IsJSObject()) {
Handle<JSObject> proto(JSObject::cast(child->prototype()));
if (!child->ShouldRegisterAsPrototypeUser(proto)) {
Handle<Map> result = CopyDropDescriptors(map);
if (!map->is_prototype_map()) {
- if (flag == INSERT_TRANSITION &&
- TransitionArray::CanHaveMoreTransitions(map)) {
+ if (flag == INSERT_TRANSITION && map->CanHaveMoreTransitions()) {
result->InitializeDescriptors(*descriptors, *layout_descriptor);
Handle<Name> name;
if (FLAG_trace_maps &&
// Mirror conditions above that did not call ConnectTransition().
(map->is_prototype_map() ||
- !(flag == INSERT_TRANSITION &&
- TransitionArray::CanHaveMoreTransitions(map)))) {
+ !(flag == INSERT_TRANSITION && map->CanHaveMoreTransitions()))) {
PrintF("[TraceMaps: ReplaceDescriptors from= %p to= %p reason= %s ]\n",
reinterpret_cast<void*>(*map), reinterpret_cast<void*>(*result),
reason);
Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
TransitionFlag flag) {
- Map* maybe_elements_transition_map = NULL;
if (flag == INSERT_TRANSITION) {
- maybe_elements_transition_map = map->ElementsTransitionMap();
- DCHECK(
- maybe_elements_transition_map == NULL ||
- ((maybe_elements_transition_map->elements_kind() ==
- DICTIONARY_ELEMENTS ||
+ DCHECK(!map->HasElementsTransition() ||
+ ((map->elements_transition_map()->elements_kind() ==
+ DICTIONARY_ELEMENTS ||
IsExternalArrayElementsKind(
- maybe_elements_transition_map->elements_kind())) &&
- (kind == DICTIONARY_ELEMENTS || IsExternalArrayElementsKind(kind))));
+ map->elements_transition_map()->elements_kind())) &&
+ (kind == DICTIONARY_ELEMENTS ||
+ IsExternalArrayElementsKind(kind))));
DCHECK(!IsFastElementsKind(kind) ||
IsMoreGeneralElementsKindTransition(map->elements_kind(), kind));
DCHECK(kind != map->elements_kind());
}
bool insert_transition = flag == INSERT_TRANSITION &&
- TransitionArray::CanHaveMoreTransitions(map) &&
- maybe_elements_transition_map == NULL;
+ map->CanHaveMoreTransitions() &&
+ !map->HasElementsTransition();
if (insert_transition) {
Handle<Map> new_map = CopyForTransition(map, "CopyAsElementsKind");
Isolate* isolate = map->GetIsolate();
bool insert_transition =
- TransitionArray::CanHaveMoreTransitions(map) && !map->is_prototype_map();
+ map->CanHaveMoreTransitions() && !map->is_prototype_map();
if (insert_transition) {
Handle<Map> new_map = CopyForTransition(map, "CopyForObserved");
// Migrate to the newest map before storing the property.
map = Update(map);
- Map* maybe_transition =
- TransitionArray::SearchTransition(*map, kData, *name, attributes);
- if (maybe_transition != NULL) {
- Handle<Map> transition(maybe_transition);
+ int index = map->SearchTransition(kData, *name, attributes);
+ if (index != TransitionArray::kNotFound) {
+ Handle<Map> transition(map->GetTransition(index));
int descriptor = transition->LastAdded();
DCHECK_EQ(attributes, transition->instance_descriptors()
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
- Map* maybe_transition =
- TransitionArray::SearchTransition(*map, kAccessor, *name, attributes);
- if (maybe_transition != NULL) {
- Handle<Map> transition(maybe_transition, isolate);
+ int index = map->SearchTransition(kAccessor, *name, attributes);
+ if (index != TransitionArray::kNotFound) {
+ Handle<Map> transition(map->GetTransition(index));
DescriptorArray* descriptors = transition->instance_descriptors();
int descriptor = transition->LastAdded();
DCHECK(descriptors->GetKey(descriptor)->Equals(*name));
// Ensure the key is unique.
descriptor->KeyToUniqueName();
- if (flag == INSERT_TRANSITION && map->owns_descriptors() &&
- TransitionArray::CanHaveMoreTransitions(map)) {
+ if (flag == INSERT_TRANSITION &&
+ map->owns_descriptors() &&
+ map->CanHaveMoreTransitions()) {
return ShareDescriptor(map, descriptors, descriptor);
}
}
+static void TraverseTransitionTreeInternal(Map* map,
+ Map::TraverseCallback callback,
+ void* data) {
+ if (map->HasTransitionArray()) {
+ TransitionArray* transitions = map->transitions();
+ if (transitions->HasPrototypeTransitions()) {
+ FixedArray* proto_trans = transitions->GetPrototypeTransitions();
+ Object* num_obj =
+ proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset);
+ int num = Smi::cast(num_obj)->value();
+ for (int i = 0; i < num; ++i) {
+ int index = Map::kProtoTransitionHeaderSize + i;
+ TraverseTransitionTreeInternal(Map::cast(proto_trans->get(index)),
+ callback, data);
+ }
+ }
+ for (int i = 0; i < transitions->number_of_transitions(); ++i) {
+ TraverseTransitionTreeInternal(transitions->GetTarget(i), callback, data);
+ }
+ }
+ callback(map, data);
+}
+
+
+// Traverse the transition tree in postorder.
+void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
+ // Make sure that we do not allocate in the callback.
+ DisallowHeapAllocation no_allocation;
+ TraverseTransitionTreeInternal(this, callback, data);
+}
+
+
void CodeCache::Update(
Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) {
// The number of monomorphic stubs for normal load/store/call IC's can grow to
map->set_counter(Map::kRetainingCounterStart);
int slack = map->unused_property_fields();
- TransitionArray::TraverseTransitionTree(map, &GetMinInobjectSlack, &slack);
+ map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
if (slack != 0) {
// Resize the initial map and all maps in its transition tree.
- TransitionArray::TraverseTransitionTree(map, &ShrinkInstanceSize, &slack);
+ map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
}
}
i < kFastElementsKindCount; ++i) {
Handle<Map> new_map;
ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
- Map* maybe_elements_transition = current_map->ElementsTransitionMap();
- if (maybe_elements_transition != NULL) {
- new_map = handle(maybe_elements_transition);
+ if (current_map->HasElementsTransition()) {
+ new_map = handle(current_map->elements_transition_map());
DCHECK(new_map->elements_kind() == next_kind);
} else {
new_map = Map::CopyAsElementsKind(
}
+Handle<Map> Map::GetPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype) {
+ DisallowHeapAllocation no_gc;
+ FixedArray* cache = map->GetPrototypeTransitions();
+ int number_of_transitions = map->NumberOfProtoTransitions();
+ for (int i = 0; i < number_of_transitions; i++) {
+ Map* map = Map::cast(cache->get(kProtoTransitionHeaderSize + i));
+ if (map->prototype() == *prototype) return handle(map);
+ }
+ return Handle<Map>();
+}
+
+
+Handle<Map> Map::PutPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype,
+ Handle<Map> target_map) {
+ DCHECK(target_map->IsMap());
+ DCHECK(HeapObject::cast(*prototype)->map()->IsMap());
+ // Don't cache prototype transition if this map is either shared, or a map of
+ // a prototype.
+ if (map->is_prototype_map()) return map;
+ if (map->is_dictionary_map() || !FLAG_cache_prototype_transitions) return map;
+
+ const int header = kProtoTransitionHeaderSize;
+
+ Handle<FixedArray> cache(map->GetPrototypeTransitions());
+ int capacity = cache->length() - header;
+ int transitions = map->NumberOfProtoTransitions() + 1;
+
+ if (transitions > capacity) {
+ // Grow array by factor 2 up to MaxCachedPrototypeTransitions.
+ int new_capacity = Min(kMaxCachedPrototypeTransitions, transitions * 2);
+ if (new_capacity == capacity) return map;
+
+ cache = FixedArray::CopySize(cache, header + new_capacity);
+
+ SetPrototypeTransitions(map, cache);
+ }
+
+ // Reload number of transitions as GC might shrink them.
+ int last = map->NumberOfProtoTransitions();
+ int entry = header + last;
+
+ cache->set(entry, *target_map);
+ map->SetNumberOfProtoTransitions(last + 1);
+
+ return map;
+}
+
+
+void Map::ZapTransitions() {
+ TransitionArray* transition_array = transitions();
+ // TODO(mstarzinger): Temporarily use a slower version instead of the faster
+ // MemsetPointer to investigate a crasher. Switch back to MemsetPointer.
+ Object** data = transition_array->data_start();
+ Object* the_hole = GetHeap()->the_hole_value();
+ int length = transition_array->length();
+ for (int i = 0; i < length; i++) {
+ data[i] = the_hole;
+ }
+}
+
+
+void Map::ZapPrototypeTransitions() {
+ FixedArray* proto_transitions = GetPrototypeTransitions();
+ MemsetPointer(proto_transitions->data_start(),
+ GetHeap()->the_hole_value(),
+ proto_transitions->length());
+}
+
+
// static
void Map::AddDependentCompilationInfo(Handle<Map> map,
DependentCode::DependencyGroup group,
Handle<Map> Map::TransitionToPrototype(Handle<Map> map,
Handle<Object> prototype,
PrototypeOptimizationMode mode) {
- Handle<Map> new_map = TransitionArray::GetPrototypeTransition(map, prototype);
+ Handle<Map> new_map = GetPrototypeTransition(map, prototype);
if (new_map.is_null()) {
new_map = Copy(map, "TransitionToPrototype");
- TransitionArray::PutPrototypeTransition(map, prototype, new_map);
+ PutPrototypeTransition(map, prototype, new_map);
new_map->SetPrototype(prototype, mode);
}
return new_map;
// map with DICTIONARY_ELEMENTS was found in the prototype chain.
bool DictionaryElementsInPrototypeChainOnly();
- inline Map* ElementsTransitionMap();
+ inline bool HasTransitionArray() const;
+ inline bool HasElementsTransition();
+ inline Map* elements_transition_map();
+ inline Map* GetTransition(int transition_index);
+ inline int SearchSpecialTransition(Symbol* name);
+ inline int SearchTransition(PropertyKind kind, Name* name,
+ PropertyAttributes attributes);
inline FixedArrayBase* GetInitialElements();
- DECL_ACCESSORS(raw_transitions, Object)
+ DECL_ACCESSORS(transitions, TransitionArray)
+ inline void init_transitions(Object* undefined);
+
+ static inline Handle<String> ExpectedTransitionKey(Handle<Map> map);
+ static inline Handle<Map> ExpectedTransitionTarget(Handle<Map> map);
+
+ // Try to follow an existing transition to a field with attributes NONE. The
+ // return value indicates whether the transition was successful.
+ static inline Handle<Map> FindTransitionToField(Handle<Map> map,
+ Handle<Name> key);
Map* FindRootMap();
Map* FindFieldOwner(int descriptor);
inline Object* GetConstructor() const;
inline void SetConstructor(Object* constructor,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
- // [back pointer]: points back to the parent map from which a transition
- // leads to this map. The field overlaps with the constructor (see above).
inline Object* GetBackPointer();
inline void SetBackPointer(Object* value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
// [weak cell cache]: cache that stores a weak cell pointing to this map.
DECL_ACCESSORS(weak_cell_cache, Object)
+ // [prototype transitions]: cache of prototype transitions.
+ // Prototype transition is a transition that happens
+ // when we change object's prototype to a new one.
+ // Cache format:
+ // 0: finger - index of the first free cell in the cache
+ // 1 + i: target map
+ inline FixedArray* GetPrototypeTransitions();
+ inline bool HasPrototypeTransitions();
+
+ static const int kProtoTransitionNumberOfEntriesOffset = 0;
+ static const int kProtoTransitionHeaderSize = 1;
+
+ inline int NumberOfProtoTransitions() {
+ FixedArray* cache = GetPrototypeTransitions();
+ if (cache->length() == 0) return 0;
+ return
+ Smi::cast(cache->get(kProtoTransitionNumberOfEntriesOffset))->value();
+ }
+
+ inline void SetNumberOfProtoTransitions(int value) {
+ FixedArray* cache = GetPrototypeTransitions();
+ DCHECK(cache->length() != 0);
+ cache->set(kProtoTransitionNumberOfEntriesOffset, Smi::FromInt(value));
+ }
+
inline PropertyDetails GetLastDescriptorDetails();
+ // The size of transition arrays are limited so they do not end up in large
+ // object space. Otherwise ClearNonLiveTransitions would leak memory while
+ // applying in-place right trimming.
+ inline bool CanHaveMoreTransitions();
+
int LastAdded() {
int number_of_own_descriptors = NumberOfOwnDescriptors();
DCHECK(number_of_own_descriptors > 0);
// Removes a code object from the code cache at the given index.
void RemoveFromCodeCache(Name* name, Code* code, int index);
+ // Set all map transitions from this map to dead maps to null. Also clear
+ // back pointers in transition targets so that we do not process this map
+ // again while following back pointers.
+ void ClearNonLiveTransitions(Heap* heap);
+
// Computes a hash value for this map, to be used in HashTables and such.
int Hash();
inline int visitor_id();
inline void set_visitor_id(int visitor_id);
+ typedef void (*TraverseCallback)(Map* map, void* data);
+
+ void TraverseTransitionTree(TraverseCallback callback, void* data);
+
+ // When you set the prototype of an object using the __proto__ accessor you
+ // need a new map for the object (the prototype is stored in the map). In
+ // order not to multiply maps unnecessarily we store these as transitions in
+ // the original map. That way we can transition to the same map if the same
+ // prototype is set, rather than creating a new map every time. The
+ // transitions are in the form of a map where the keys are prototype objects
+ // and the values are the maps they transition to.
+ static const int kMaxCachedPrototypeTransitions = 256;
static Handle<Map> TransitionToPrototype(Handle<Map> map,
Handle<Object> prototype,
PrototypeOptimizationMode mode);
static const int kPrototypeOffset = kBitField3Offset + kPointerSize;
static const int kConstructorOrBackPointerOffset =
kPrototypeOffset + kPointerSize;
- // When there is only one transition, it is stored directly in this field;
- // otherwise a transition array is used.
- // For prototype maps, this slot is used to store a pointer to the prototype
- // object using this map.
static const int kTransitionsOffset =
kConstructorOrBackPointerOffset + kPointerSize;
static const int kDescriptorsOffset = kTransitionsOffset + kPointerSize;
static Handle<Map> TransitionElementsToSlow(Handle<Map> object,
ElementsKind to_kind);
+ // Zaps the contents of backing data structures. Note that the
+ // heap verifier (i.e. VerifyMarkingVisitor) relies on zapping of objects
+ // holding weak references when incremental marking is used, because it also
+ // iterates over objects that are otherwise unreachable.
+ // In general we only want to call these functions in release mode when
+ // heap verification is turned on.
+ void ZapPrototypeTransitions();
+ void ZapTransitions();
+
void DeprecateTransitionTree();
bool DeprecateTarget(PropertyKind kind, Name* key,
PropertyAttributes attributes,
HeapType* old_field_type,
HeapType* new_field_type);
+ static inline void SetPrototypeTransitions(
+ Handle<Map> map,
+ Handle<FixedArray> prototype_transitions);
+
+ static Handle<Map> GetPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype);
+ static Handle<Map> PutPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype,
+ Handle<Map> target_map);
+
static const int kFastPropertiesSoftLimit = 12;
static const int kMaxFastProperties = 128;
namespace internal {
+#define FIELD_ADDR(p, offset) \
+ (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
+
+#define WRITE_FIELD(p, offset, value) \
+ (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
+
+#define CONDITIONAL_WRITE_BARRIER(heap, object, offset, value, mode) \
+ if (mode == UPDATE_WRITE_BARRIER) { \
+ heap->incremental_marking()->RecordWrite( \
+ object, HeapObject::RawField(object, offset), value); \
+ if (heap->InNewSpace(value)) { \
+ heap->RecordWrite(object->address(), offset); \
+ } \
+ }
+
+
TransitionArray* TransitionArray::cast(Object* object) {
DCHECK(object->IsTransitionArray());
return reinterpret_cast<TransitionArray*>(object);
}
+bool TransitionArray::HasElementsTransition() {
+ return SearchSpecial(GetHeap()->elements_transition_symbol()) != kNotFound;
+}
+
+
+Object* TransitionArray::back_pointer_storage() {
+ return get(kBackPointerStorageIndex);
+}
+
+
+void TransitionArray::set_back_pointer_storage(Object* back_pointer,
+ WriteBarrierMode mode) {
+ Heap* heap = GetHeap();
+ WRITE_FIELD(this, kBackPointerStorageOffset, back_pointer);
+ CONDITIONAL_WRITE_BARRIER(
+ heap, this, kBackPointerStorageOffset, back_pointer, mode);
+}
+
+
bool TransitionArray::HasPrototypeTransitions() {
- return get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
+ return IsFullTransitionArray() &&
+ get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
}
FixedArray* TransitionArray::GetPrototypeTransitions() {
- DCHECK(HasPrototypeTransitions()); // Callers must check first.
+ DCHECK(IsFullTransitionArray());
Object* prototype_transitions = get(kPrototypeTransitionsIndex);
return FixedArray::cast(prototype_transitions);
}
void TransitionArray::SetPrototypeTransitions(FixedArray* transitions,
WriteBarrierMode mode) {
+ DCHECK(IsFullTransitionArray());
DCHECK(transitions->IsFixedArray());
- set(kPrototypeTransitionsIndex, transitions, mode);
+ Heap* heap = GetHeap();
+ WRITE_FIELD(this, kPrototypeTransitionsOffset, transitions);
+ CONDITIONAL_WRITE_BARRIER(
+ heap, this, kPrototypeTransitionsOffset, transitions, mode);
}
Object** TransitionArray::GetPrototypeTransitionsSlot() {
- return RawFieldOfElementAt(kPrototypeTransitionsIndex);
+ return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
+ kPrototypeTransitionsOffset);
}
Object** TransitionArray::GetKeySlot(int transition_number) {
+ DCHECK(!IsSimpleTransition());
DCHECK(transition_number < number_of_transitions());
return RawFieldOfElementAt(ToKeyIndex(transition_number));
}
Name* TransitionArray::GetKey(int transition_number) {
+ if (IsSimpleTransition()) {
+ Map* target = GetTarget(kSimpleTransitionIndex);
+ int descriptor = target->LastAdded();
+ Name* key = target->instance_descriptors()->GetKey(descriptor);
+ return key;
+ }
DCHECK(transition_number < number_of_transitions());
return Name::cast(get(ToKeyIndex(transition_number)));
}
-Name* TransitionArray::GetKey(Object* raw_transitions, int transition_number) {
- if (IsSimpleTransition(raw_transitions)) {
- DCHECK(transition_number == 0);
- return GetSimpleTransitionKey(GetSimpleTransition(raw_transitions));
- }
- DCHECK(IsFullTransitionArray(raw_transitions));
- return TransitionArray::cast(raw_transitions)->GetKey(transition_number);
-}
-
-
void TransitionArray::SetKey(int transition_number, Name* key) {
+ DCHECK(!IsSimpleTransition());
DCHECK(transition_number < number_of_transitions());
set(ToKeyIndex(transition_number), key);
}
Map* TransitionArray::GetTarget(int transition_number) {
+ if (IsSimpleTransition()) {
+ DCHECK(transition_number == kSimpleTransitionIndex);
+ return Map::cast(get(kSimpleTransitionTarget));
+ }
DCHECK(transition_number < number_of_transitions());
return Map::cast(get(ToTargetIndex(transition_number)));
}
-Map* TransitionArray::GetTarget(Object* raw_transitions,
- int transition_number) {
- if (IsSimpleTransition(raw_transitions)) {
- DCHECK(transition_number == 0);
- return GetSimpleTransition(raw_transitions);
+void TransitionArray::SetTarget(int transition_number, Map* value) {
+ if (IsSimpleTransition()) {
+ DCHECK(transition_number == kSimpleTransitionIndex);
+ return set(kSimpleTransitionTarget, value);
}
- DCHECK(IsFullTransitionArray(raw_transitions));
- return TransitionArray::cast(raw_transitions)->GetTarget(transition_number);
+ DCHECK(transition_number < number_of_transitions());
+ set(ToTargetIndex(transition_number), value);
}
-void TransitionArray::SetTarget(int transition_number, Map* value) {
- DCHECK(transition_number < number_of_transitions());
- set(ToTargetIndex(transition_number), value);
+PropertyDetails TransitionArray::GetTargetDetails(int transition_number) {
+ Map* map = GetTarget(transition_number);
+ return map->GetLastDescriptorDetails();
+}
+
+
+Object* TransitionArray::GetTargetValue(int transition_number) {
+ Map* map = GetTarget(transition_number);
+ return map->instance_descriptors()->GetValue(map->LastAdded());
}
int TransitionArray::SearchName(Name* name, int* out_insertion_index) {
+ if (IsSimpleTransition()) {
+ Name* key = GetKey(kSimpleTransitionIndex);
+ if (key->Equals(name)) return kSimpleTransitionIndex;
+ if (out_insertion_index != NULL) {
+ *out_insertion_index = key->Hash() > name->Hash() ? 0 : 1;
+ }
+ return kNotFound;
+ }
return internal::Search<ALL_ENTRIES>(this, name, 0, out_insertion_index);
}
void TransitionArray::SetNumberOfTransitions(int number_of_transitions) {
- DCHECK(number_of_transitions <= Capacity(this));
- set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions));
+ if (IsFullTransitionArray()) {
+ DCHECK(number_of_transitions <= number_of_transitions_storage());
+ WRITE_FIELD(this, kTransitionLengthOffset,
+ Smi::FromInt(number_of_transitions));
+ }
}
+
+#undef FIELD_ADDR
+#undef WRITE_FIELD
+#undef CONDITIONAL_WRITE_BARRIER
+
+
} } // namespace v8::internal
#endif // V8_TRANSITIONS_INL_H_
namespace internal {
-// static
-void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
- Handle<Map> target, SimpleTransitionFlag flag) {
- Isolate* isolate = map->GetIsolate();
- target->SetBackPointer(*map);
-
- // If the map doesn't have any transitions at all yet, install the new one.
- if (CanStoreSimpleTransition(map->raw_transitions())) {
- if (flag == SIMPLE_PROPERTY_TRANSITION) {
- Handle<WeakCell> cell = Map::WeakCellForMap(target);
- map->set_raw_transitions(*cell);
- return;
- }
- // If the flag requires a full TransitionArray, allocate one.
- Handle<TransitionArray> result = Allocate(isolate, 0, 1);
- map->set_raw_transitions(*result);
+Handle<TransitionArray> TransitionArray::Allocate(Isolate* isolate,
+ int number_of_transitions,
+ int slack) {
+ Handle<FixedArray> array = isolate->factory()->NewFixedArray(
+ LengthFor(number_of_transitions + slack));
+ array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
+ array->set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions));
+ return Handle<TransitionArray>::cast(array);
+}
+
+
+Handle<TransitionArray> TransitionArray::AllocateSimple(Isolate* isolate,
+ Handle<Map> target) {
+ Handle<FixedArray> array =
+ isolate->factory()->NewFixedArray(kSimpleTransitionSize);
+ array->set(kSimpleTransitionTarget, *target);
+ return Handle<TransitionArray>::cast(array);
+}
+
+
+void TransitionArray::NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin,
+ int origin_transition,
+ int target_transition) {
+ NoIncrementalWriteBarrierSet(target_transition,
+ origin->GetKey(origin_transition),
+ origin->GetTarget(origin_transition));
+}
+
+
+Handle<TransitionArray> TransitionArray::NewWith(Handle<Map> map,
+ Handle<Name> name,
+ Handle<Map> target,
+ SimpleTransitionFlag flag) {
+ Handle<TransitionArray> result;
+ Isolate* isolate = name->GetIsolate();
+
+ if (flag == SIMPLE_PROPERTY_TRANSITION) {
+ result = AllocateSimple(isolate, target);
+ } else {
+ result = Allocate(isolate, 1);
+ result->NoIncrementalWriteBarrierSet(0, *name, *target);
}
+ result->set_back_pointer_storage(map->GetBackPointer());
+ return result;
+}
- bool is_special_transition = flag == SPECIAL_TRANSITION;
- // If the map has a simple transition, check if it should be overwritten.
- if (IsSimpleTransition(map->raw_transitions())) {
- Map* old_target = GetSimpleTransition(map->raw_transitions());
- Name* key = GetSimpleTransitionKey(old_target);
- PropertyDetails old_details = GetSimpleTargetDetails(old_target);
- PropertyDetails new_details = is_special_transition
- ? PropertyDetails(NONE, DATA, 0)
- : GetTargetDetails(*name, *target);
- if (flag == SIMPLE_PROPERTY_TRANSITION && key->Equals(*name) &&
- old_details.kind() == new_details.kind() &&
- old_details.attributes() == new_details.attributes()) {
- Handle<WeakCell> cell = Map::WeakCellForMap(target);
- map->set_raw_transitions(*cell);
- return;
- }
- // Otherwise allocate a full TransitionArray with slack for a new entry.
- Handle<TransitionArray> result = Allocate(isolate, 1, 1);
- // Re-read existing data; the allocation might have caused it to be cleared.
- if (IsSimpleTransition(map->raw_transitions())) {
- old_target = GetSimpleTransition(map->raw_transitions());
- result->NoIncrementalWriteBarrierSet(
- 0, GetSimpleTransitionKey(old_target), old_target);
- } else {
- result->SetNumberOfTransitions(0);
- }
- map->set_raw_transitions(*result);
+
+Handle<TransitionArray> TransitionArray::ExtendToFullTransitionArray(
+ Handle<Map> containing_map) {
+ DCHECK(!containing_map->transitions()->IsFullTransitionArray());
+ int nof = containing_map->transitions()->number_of_transitions();
+
+ // A transition array may shrink during GC.
+ Handle<TransitionArray> result = Allocate(containing_map->GetIsolate(), nof);
+ DisallowHeapAllocation no_gc;
+ int new_nof = containing_map->transitions()->number_of_transitions();
+ if (new_nof != nof) {
+ DCHECK(new_nof == 0);
+ result->Shrink(ToKeyIndex(0));
+ result->SetNumberOfTransitions(0);
+ } else if (nof == 1) {
+ result->NoIncrementalWriteBarrierCopyFrom(
+ containing_map->transitions(), kSimpleTransitionIndex, 0);
}
- // At this point, we know that the map has a full TransitionArray.
- DCHECK(IsFullTransitionArray(map->raw_transitions()));
+ result->set_back_pointer_storage(
+ containing_map->transitions()->back_pointer_storage());
+ return result;
+}
- int number_of_transitions = 0;
- int new_nof = 0;
- int insertion_index = kNotFound;
+
+Handle<TransitionArray> TransitionArray::Insert(Handle<Map> map,
+ Handle<Name> name,
+ Handle<Map> target,
+ SimpleTransitionFlag flag) {
+ if (!map->HasTransitionArray()) {
+ return TransitionArray::NewWith(map, name, target, flag);
+ }
+
+ int number_of_transitions = map->transitions()->number_of_transitions();
+ int new_nof = number_of_transitions;
+
+ bool is_special_transition = flag == SPECIAL_TRANSITION;
DCHECK_EQ(is_special_transition, IsSpecialTransition(*name));
PropertyDetails details = is_special_transition
? PropertyDetails(NONE, DATA, 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.kind(), *name,
+ details.attributes(), &insertion_index);
+ if (index == kNotFound) {
+ ++new_nof;
+ } else {
+ insertion_index = index;
+ }
+ DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
+
+ CHECK(new_nof <= kMaxNumberOfTransitions);
+
+ if (new_nof <= map->transitions()->number_of_transitions_storage()) {
DisallowHeapAllocation no_gc;
- TransitionArray* array = TransitionArray::cast(map->raw_transitions());
- number_of_transitions = array->number_of_transitions();
- new_nof = number_of_transitions;
+ TransitionArray* array = map->transitions();
- int index =
- is_special_transition
- ? array->SearchSpecial(Symbol::cast(*name), &insertion_index)
- : array->Search(details.kind(), *name, details.attributes(),
- &insertion_index);
- // If an existing entry was found, overwrite it and return.
if (index != kNotFound) {
array->SetTarget(index, *target);
- return;
+ return handle(array);
}
- ++new_nof;
- CHECK(new_nof <= kMaxNumberOfTransitions);
- DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
-
- // If there is enough capacity, insert new entry into the existing array.
- if (new_nof <= Capacity(array)) {
- array->SetNumberOfTransitions(new_nof);
- for (index = number_of_transitions; index > insertion_index; --index) {
- array->SetKey(index, array->GetKey(index - 1));
- array->SetTarget(index, array->GetTarget(index - 1));
- }
- array->SetKey(index, *name);
- array->SetTarget(index, *target);
- SLOW_DCHECK(array->IsSortedNoDuplicates());
- return;
+ array->SetNumberOfTransitions(new_nof);
+ for (index = number_of_transitions; index > insertion_index; --index) {
+ Name* key = array->GetKey(index - 1);
+ 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);
}
- // We're gonna need a bigger TransitionArray.
Handle<TransitionArray> result = Allocate(
map->GetIsolate(), new_nof,
Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions));
- // The map's transition array may have shrunk during the allocation above as
+ // The map's transition array may grown smaller during the allocation above as
// it was weakly traversed, though it is guaranteed not to disappear. Trim the
// result copy if needed, and recompute variables.
- DCHECK(IsFullTransitionArray(map->raw_transitions()));
+ DCHECK(map->HasTransitionArray());
DisallowHeapAllocation no_gc;
- TransitionArray* array = TransitionArray::cast(map->raw_transitions());
+ TransitionArray* array = map->transitions();
if (array->number_of_transitions() != number_of_transitions) {
DCHECK(array->number_of_transitions() < number_of_transitions);
new_nof = number_of_transitions;
insertion_index = kNotFound;
- int index =
- is_special_transition
- ? array->SearchSpecial(Symbol::cast(*name), &insertion_index)
- : array->Search(details.kind(), *name, details.attributes(),
- &insertion_index);
+ index = is_special_transition ? map->transitions()->SearchSpecial(
+ Symbol::cast(*name), &insertion_index)
+ : map->transitions()->Search(
+ details.kind(), *name,
+ details.attributes(), &insertion_index);
if (index == kNotFound) {
++new_nof;
} else {
result->NoIncrementalWriteBarrierCopyFrom(array, i, i + 1);
}
+ result->set_back_pointer_storage(array->back_pointer_storage());
SLOW_DCHECK(result->IsSortedNoDuplicates());
- map->set_raw_transitions(*result);
-}
-
-
-// static
-Map* TransitionArray::SearchTransition(Map* map, PropertyKind kind, Name* name,
- PropertyAttributes attributes) {
- Object* raw_transitions = map->raw_transitions();
- if (IsSimpleTransition(raw_transitions)) {
- Map* target = GetSimpleTransition(raw_transitions);
- Name* key = GetSimpleTransitionKey(target);
- if (!key->Equals(name)) return NULL;
- PropertyDetails details = GetSimpleTargetDetails(target);
- if (details.attributes() != attributes) return NULL;
- if (details.kind() != kind) return NULL;
- return target;
- }
- if (IsFullTransitionArray(raw_transitions)) {
- TransitionArray* transitions = TransitionArray::cast(raw_transitions);
- int transition = transitions->Search(kind, name, attributes);
- if (transition == kNotFound) return NULL;
- return transitions->GetTarget(transition);
- }
- return NULL;
+ return result;
}
-// static
-Map* TransitionArray::SearchSpecial(Map* map, Symbol* name) {
- Object* raw_transitions = map->raw_transitions();
- if (IsFullTransitionArray(raw_transitions)) {
- TransitionArray* transitions = TransitionArray::cast(raw_transitions);
- int transition = transitions->SearchSpecial(name);
- if (transition == kNotFound) return NULL;
- return transitions->GetTarget(transition);
- }
- return NULL;
-}
-
-
-// static
-Handle<Map> TransitionArray::FindTransitionToField(Handle<Map> map,
- Handle<Name> name) {
- DisallowHeapAllocation no_gc;
- Map* target = SearchTransition(*map, kData, *name, NONE);
- if (target == NULL) return Handle<Map>::null();
- PropertyDetails details = target->GetLastDescriptorDetails();
- DCHECK_EQ(NONE, details.attributes());
- if (details.type() != DATA) return Handle<Map>::null();
- return Handle<Map>(target);
-}
-
-
-// static
-Handle<String> TransitionArray::ExpectedTransitionKey(Handle<Map> map) {
- DisallowHeapAllocation no_gc;
- Object* raw_transition = map->raw_transitions();
- if (!IsSimpleTransition(raw_transition)) return Handle<String>::null();
- Map* target = GetSimpleTransition(raw_transition);
- PropertyDetails details = GetSimpleTargetDetails(target);
- if (details.type() != DATA) return Handle<String>::null();
- if (details.attributes() != NONE) return Handle<String>::null();
- Name* name = GetSimpleTransitionKey(target);
- if (!name->IsString()) return Handle<String>::null();
- return Handle<String>(String::cast(name));
-}
-
-
-// static
-bool TransitionArray::CanHaveMoreTransitions(Handle<Map> map) {
- Object* raw_transitions = map->raw_transitions();
- if (IsFullTransitionArray(raw_transitions)) {
- TransitionArray* transitions = TransitionArray::cast(raw_transitions);
- return transitions->number_of_transitions() < kMaxNumberOfTransitions;
- }
- return true;
-}
-
-
-// static
-Handle<Map> TransitionArray::PutPrototypeTransition(Handle<Map> map,
- Handle<Object> prototype,
- Handle<Map> target_map) {
- DCHECK(HeapObject::cast(*prototype)->map()->IsMap());
- // Don't cache prototype transition if this map is either shared, or a map of
- // a prototype.
- if (map->is_prototype_map()) return map;
- if (map->is_dictionary_map() || !FLAG_cache_prototype_transitions) return map;
-
- const int header = kProtoTransitionHeaderSize;
-
- Handle<FixedArray> cache(GetPrototypeTransitions(*map));
- int capacity = cache->length() - header;
- int transitions = NumberOfPrototypeTransitions(*cache) + 1;
-
- if (transitions > capacity) {
- // Grow array by factor 2 up to MaxCachedPrototypeTransitions.
- int new_capacity = Min(kMaxCachedPrototypeTransitions, transitions * 2);
- if (new_capacity == capacity) return map;
-
- cache = FixedArray::CopySize(cache, header + new_capacity);
- if (capacity < 0) {
- // There was no prototype transitions array before, so the size
- // couldn't be copied. Initialize it explicitly.
- SetNumberOfPrototypeTransitions(*cache, 0);
- }
-
- SetPrototypeTransitions(map, cache);
- }
-
- // Reload number of transitions as GC might shrink them.
- int last = NumberOfPrototypeTransitions(*cache);
- int entry = header + last;
-
- cache->set(entry, *target_map);
- SetNumberOfPrototypeTransitions(*cache, last + 1);
-
- return map;
-}
-
-
-// static
-Handle<Map> TransitionArray::GetPrototypeTransition(Handle<Map> map,
- Handle<Object> prototype) {
- DisallowHeapAllocation no_gc;
- FixedArray* cache = GetPrototypeTransitions(*map);
- int number_of_transitions = NumberOfPrototypeTransitions(cache);
- for (int i = 0; i < number_of_transitions; i++) {
- Map* target = Map::cast(cache->get(kProtoTransitionHeaderSize + i));
- if (target->prototype() == *prototype) return handle(target);
- }
- return Handle<Map>();
-}
-
-
-// static
-FixedArray* TransitionArray::GetPrototypeTransitions(Map* map) {
- Object* raw_transitions = map->raw_transitions();
- Heap* heap = map->GetHeap();
- if (!IsFullTransitionArray(raw_transitions)) {
- return heap->empty_fixed_array();
- }
- TransitionArray* transitions = TransitionArray::cast(raw_transitions);
- if (!transitions->HasPrototypeTransitions()) {
- return heap->empty_fixed_array();
- }
- return transitions->GetPrototypeTransitions();
-}
-
-
-// static
-void TransitionArray::SetNumberOfPrototypeTransitions(
- FixedArray* proto_transitions, int value) {
- DCHECK(proto_transitions->length() != 0);
- proto_transitions->set(kProtoTransitionNumberOfEntriesOffset,
- Smi::FromInt(value));
-}
-
-
-// static
-int TransitionArray::NumberOfTransitions(Object* raw_transitions) {
- if (CanStoreSimpleTransition(raw_transitions)) return 0;
- if (IsSimpleTransition(raw_transitions)) return 1;
- DCHECK(IsFullTransitionArray(raw_transitions));
- return TransitionArray::cast(raw_transitions)->number_of_transitions();
-}
-
-
-// static
-int TransitionArray::Capacity(Object* raw_transitions) {
- if (!IsFullTransitionArray(raw_transitions)) return 1;
- TransitionArray* t = TransitionArray::cast(raw_transitions);
- if (t->length() <= kFirstIndex) return 0;
- return (t->length() - kFirstIndex) / kTransitionSize;
-}
-
-
-// Private static helper functions.
-
-Handle<TransitionArray> TransitionArray::Allocate(Isolate* isolate,
- int number_of_transitions,
- int slack) {
- Handle<FixedArray> array = isolate->factory()->NewFixedArray(
- LengthFor(number_of_transitions + slack));
- array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
- array->set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions));
- return Handle<TransitionArray>::cast(array);
-}
-
-
-void TransitionArray::NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin,
- int origin_transition,
- int target_transition) {
- NoIncrementalWriteBarrierSet(target_transition,
- origin->GetKey(origin_transition),
- origin->GetTarget(origin_transition));
-}
-
-
-static void ZapTransitionArray(TransitionArray* transitions) {
- MemsetPointer(transitions->data_start(),
- transitions->GetHeap()->the_hole_value(),
- transitions->length());
-}
-
-
-void TransitionArray::ReplaceTransitions(Handle<Map> map,
- Object* new_transitions) {
- Object* raw_transitions = map->raw_transitions();
- if (IsFullTransitionArray(raw_transitions)) {
- TransitionArray* old_transitions = TransitionArray::cast(raw_transitions);
-#ifdef DEBUG
- CheckNewTransitionsAreConsistent(map, old_transitions, new_transitions);
- DCHECK(old_transitions != new_transitions);
-#endif
- // Transition arrays are not shared. When one is replaced, it should not
- // keep referenced objects alive, so we zap it.
- // When there is another reference to the array somewhere (e.g. a handle),
- // not zapping turns from a waste of memory into a source of crashes.
- ZapTransitionArray(old_transitions);
- }
- map->set_raw_transitions(new_transitions);
-}
-
-
-static void ZapPrototypeTransitions(Object* raw_transitions) {
- DCHECK(TransitionArray::IsFullTransitionArray(raw_transitions));
- TransitionArray* transitions = TransitionArray::cast(raw_transitions);
- if (!transitions->HasPrototypeTransitions()) return;
- FixedArray* proto_transitions = transitions->GetPrototypeTransitions();
- MemsetPointer(proto_transitions->data_start(),
- proto_transitions->GetHeap()->the_hole_value(),
- proto_transitions->length());
-}
-
-
-void TransitionArray::SetPrototypeTransitions(
- Handle<Map> map, Handle<FixedArray> proto_transitions) {
- EnsureHasFullTransitionArray(map);
- if (Heap::ShouldZapGarbage()) {
- Object* raw_transitions = map->raw_transitions();
- DCHECK(raw_transitions != *proto_transitions);
- ZapPrototypeTransitions(raw_transitions);
- }
- TransitionArray* transitions = TransitionArray::cast(map->raw_transitions());
- transitions->SetPrototypeTransitions(*proto_transitions);
-}
-
-
-void TransitionArray::EnsureHasFullTransitionArray(Handle<Map> map) {
- Object* raw_transitions = map->raw_transitions();
- if (IsFullTransitionArray(raw_transitions)) return;
- int nof = IsSimpleTransition(raw_transitions) ? 1 : 0;
- Handle<TransitionArray> result = Allocate(map->GetIsolate(), nof);
- DisallowHeapAllocation no_gc;
- // Reload pointer after the allocation that just happened.
- raw_transitions = map->raw_transitions();
- int new_nof = IsSimpleTransition(raw_transitions) ? 1 : 0;
- if (new_nof != nof) {
- DCHECK(new_nof == 0);
- result->Shrink(ToKeyIndex(0));
- result->SetNumberOfTransitions(0);
- } else if (nof == 1) {
- Map* target = GetSimpleTransition(raw_transitions);
- Name* key = GetSimpleTransitionKey(target);
- result->NoIncrementalWriteBarrierSet(0, key, target);
- }
- ReplaceTransitions(map, *result);
-}
-
-
-void TransitionArray::TraverseTransitionTreeInternal(Map* map,
- TraverseCallback callback,
- void* data) {
- Object* raw_transitions = map->raw_transitions();
- if (IsFullTransitionArray(raw_transitions)) {
- TransitionArray* transitions = TransitionArray::cast(raw_transitions);
- if (transitions->HasPrototypeTransitions()) {
- FixedArray* proto_trans = transitions->GetPrototypeTransitions();
- for (int i = 0; i < NumberOfPrototypeTransitions(proto_trans); ++i) {
- int index = TransitionArray::kProtoTransitionHeaderSize + i;
- TraverseTransitionTreeInternal(Map::cast(proto_trans->get(index)),
- callback, data);
- }
- }
- for (int i = 0; i < transitions->number_of_transitions(); ++i) {
- TraverseTransitionTreeInternal(transitions->GetTarget(i), callback, data);
- }
- } else if (IsSimpleTransition(raw_transitions)) {
- TraverseTransitionTreeInternal(GetSimpleTransition(raw_transitions),
- callback, data);
- }
- callback(map, data);
-}
-
-
-#ifdef DEBUG
-void TransitionArray::CheckNewTransitionsAreConsistent(
- Handle<Map> map, TransitionArray* old_transitions, Object* transitions) {
- // This function only handles full transition arrays.
- DCHECK(IsFullTransitionArray(transitions));
- TransitionArray* new_transitions = TransitionArray::cast(transitions);
- for (int i = 0; i < old_transitions->number_of_transitions(); i++) {
- Map* target = old_transitions->GetTarget(i);
- if (target->instance_descriptors() == map->instance_descriptors()) {
- Name* key = old_transitions->GetKey(i);
- int new_target_index;
- if (TransitionArray::IsSpecialTransition(key)) {
- new_target_index = new_transitions->SearchSpecial(Symbol::cast(key));
- } else {
- PropertyDetails details =
- TransitionArray::GetTargetDetails(key, target);
- new_target_index =
- new_transitions->Search(details.kind(), key, details.attributes());
- }
- DCHECK_NE(TransitionArray::kNotFound, new_target_index);
- DCHECK_EQ(target, new_transitions->GetTarget(new_target_index));
- }
- }
-}
-#endif
-
-
-// Private non-static helper functions (operating on full transition arrays).
-
int TransitionArray::SearchDetails(int transition, PropertyKind kind,
PropertyAttributes attributes,
int* out_insertion_index) {
// TransitionArrays are fixed arrays used to hold map transitions for property,
-// constant, and element changes. "Simple" transitions storing only a single
-// property transition are stored inline (i.e. the target map is stored
-// directly); otherwise a full transition array is used that has
+// constant, and element changes. They can either be simple transition arrays
+// that store a single property transition, or a full transition array that has
// prototype transitions and multiple property transitons. The details related
// to property transitions are accessed in the descriptor array of the target
// map. In the case of a simple transition, the key is also read from the
// descriptor array of the target map.
//
-// This class provides a static interface that operates directly on maps
-// and handles the distinction between simple and full transitions storage.
+// The simple format of the these objects is:
+// [0] Undefined or back pointer map
+// [1] Single transition
//
// The full format is:
-// [0] Smi(0) or fixed array of prototype transitions
-// [1] Number of transitions
-// [2] First transition
-// [2 + number of transitions * kTransitionSize]: start of slack
+// [0] Undefined or back pointer map
+// [1] Smi(0) or fixed array of prototype transitions
+// [2] Number of transitions
+// [3] First transition
+// [3 + number of transitions * kTransitionSize]: start of slack
class TransitionArray: public FixedArray {
public:
- // Insert a new transition into |map|'s transition array, extending it
- // as necessary.
- static void Insert(Handle<Map> map, Handle<Name> name, Handle<Map> target,
- SimpleTransitionFlag flag);
-
- static Map* SearchTransition(Map* map, PropertyKind kind, Name* name,
- PropertyAttributes attributes);
-
- static Map* SearchSpecial(Map* map, Symbol* name);
+ // Accessors for fetching instance transition at transition number.
+ inline Name* GetKey(int transition_number);
+ inline void SetKey(int transition_number, Name* value);
+ inline Object** GetKeySlot(int transition_number);
+ int GetSortedKeyIndex(int transition_number) { return transition_number; }
- static Handle<Map> FindTransitionToField(Handle<Map> map, Handle<Name> name);
+ Name* GetSortedKey(int transition_number) {
+ return GetKey(transition_number);
+ }
- static Handle<String> ExpectedTransitionKey(Handle<Map> map);
+ inline Map* GetTarget(int transition_number);
+ inline void SetTarget(int transition_number, Map* target);
- static Handle<Map> ExpectedTransitionTarget(Handle<Map> map) {
- DCHECK(!ExpectedTransitionKey(map).is_null());
- return Handle<Map>(GetSimpleTransition(map->raw_transitions()));
- }
- // Returns true if |raw_transition| can be overwritten with a simple
- // transition (because it's either uninitialized, or has been cleared).
- static inline bool CanStoreSimpleTransition(Object* raw_transition) {
- return raw_transition->IsSmi() ||
- (raw_transition->IsWeakCell() &&
- WeakCell::cast(raw_transition)->cleared());
- }
- static inline bool IsSimpleTransition(Object* raw_transition) {
- DCHECK(!raw_transition->IsWeakCell() ||
- WeakCell::cast(raw_transition)->cleared() ||
- WeakCell::cast(raw_transition)->value()->IsMap());
- return raw_transition->IsWeakCell() &&
- !WeakCell::cast(raw_transition)->cleared();
- }
- static inline Map* GetSimpleTransition(Object* raw_transition) {
- DCHECK(IsSimpleTransition(raw_transition));
- return Map::cast(WeakCell::cast(raw_transition)->value());
- }
- static inline bool IsFullTransitionArray(Object* raw_transitions) {
- return raw_transitions->IsTransitionArray();
- }
+ inline PropertyDetails GetTargetDetails(int transition_number);
+ inline Object* GetTargetValue(int transition_number);
- // The size of transition arrays are limited so they do not end up in large
- // object space. Otherwise ClearNonLiveReferences would leak memory while
- // applying in-place right trimming.
- static bool CanHaveMoreTransitions(Handle<Map> map);
-
- // ===== PROTOTYPE TRANSITIONS =====
- // When you set the prototype of an object using the __proto__ accessor you
- // need a new map for the object (the prototype is stored in the map). In
- // order not to multiply maps unnecessarily we store these as transitions in
- // the original map. That way we can transition to the same map if the same
- // prototype is set, rather than creating a new map every time. The
- // transitions are in the form of a map where the keys are prototype objects
- // and the values are the maps they transition to.
- // Cache format:
- // 0: finger - index of the first free cell in the cache
- // 1 + i: target map
- static const int kMaxCachedPrototypeTransitions = 256;
- static Handle<Map> PutPrototypeTransition(Handle<Map> map,
- Handle<Object> prototype,
- Handle<Map> target_map);
-
- static Handle<Map> GetPrototypeTransition(Handle<Map> map,
- Handle<Object> prototype);
-
- static FixedArray* GetPrototypeTransitions(Map* map);
-
- static int NumberOfPrototypeTransitions(FixedArray* proto_transitions) {
- if (proto_transitions->length() == 0) return 0;
- Object* raw = proto_transitions->get(kProtoTransitionNumberOfEntriesOffset);
- return Smi::cast(raw)->value();
- }
+ inline bool HasElementsTransition();
- static void SetNumberOfPrototypeTransitions(FixedArray* proto_transitions,
- int value);
+ inline Object* back_pointer_storage();
+ inline void set_back_pointer_storage(
+ Object* back_pointer,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline FixedArray* GetPrototypeTransitions();
inline void SetPrototypeTransitions(
inline Object** GetPrototypeTransitionsSlot();
inline bool HasPrototypeTransitions();
- // ===== ITERATION =====
+ // Returns the number of transitions in the array.
+ int number_of_transitions() {
+ if (IsSimpleTransition()) return 1;
+ if (length() <= kFirstIndex) return 0;
+ return Smi::cast(get(kTransitionLengthIndex))->value();
+ }
- typedef void (*TraverseCallback)(Map* map, void* data);
+ int number_of_transitions_storage() {
+ if (IsSimpleTransition()) return 1;
+ if (length() <= kFirstIndex) return 0;
+ return (length() - kFirstIndex) / kTransitionSize;
+ }
- // Traverse the transition tree in postorder.
- static void TraverseTransitionTree(Map* map, TraverseCallback callback,
- void* data) {
- // Make sure that we do not allocate in the callback.
- DisallowHeapAllocation no_allocation;
- TraverseTransitionTreeInternal(map, callback, data);
+ int NumberOfSlackTransitions() {
+ return number_of_transitions_storage() - number_of_transitions();
}
- // ===== LOW-LEVEL ACCESSORS =====
+ inline void SetNumberOfTransitions(int number_of_transitions);
+ inline int number_of_entries() { return number_of_transitions(); }
- // Accessors for fetching instance transition at transition number.
- static inline Name* GetKey(Object* raw_transitions, int transition_number);
- inline Name* GetKey(int transition_number);
- inline void SetKey(int transition_number, Name* value);
- inline Object** GetKeySlot(int transition_number);
- int GetSortedKeyIndex(int transition_number) { return transition_number; }
+ // Creates a FullTransitionArray from a SimpleTransitionArray in
+ // containing_map.
+ static Handle<TransitionArray> ExtendToFullTransitionArray(
+ Handle<Map> containing_map);
+
+ // Return a transition array, using the array from the owning map if it
+ // already has one (copying into a larger array if necessary), otherwise
+ // creating a new one according to flag.
+ // TODO(verwaest): This should not cause an existing transition to be
+ // overwritten.
+ static Handle<TransitionArray> Insert(Handle<Map> map, Handle<Name> name,
+ Handle<Map> target,
+ SimpleTransitionFlag flag);
+ // Search a transition for a given kind, property name and attributes.
+ int Search(PropertyKind kind, Name* name, PropertyAttributes attributes,
+ int* out_insertion_index = NULL);
- Name* GetSortedKey(int transition_number) {
- return GetKey(transition_number);
+ // 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);
}
- static inline Map* GetTarget(Object* raw_transitions, int transition_number);
- inline Map* GetTarget(int transition_number);
- inline void SetTarget(int transition_number, Map* target);
-
static inline PropertyDetails GetTargetDetails(Name* name, Map* target);
- // Returns the number of transitions in the array.
- static int NumberOfTransitions(Object* raw_transitions);
- // Required for templatized Search interface.
- inline int number_of_entries() { return number_of_transitions(); }
+ // Allocates a TransitionArray.
+ static Handle<TransitionArray> Allocate(Isolate* isolate,
+ int number_of_transitions,
+ int slack = 0);
- inline void SetNumberOfTransitions(int number_of_transitions);
+ bool IsSimpleTransition() {
+ return length() == kSimpleTransitionSize &&
+ get(kSimpleTransitionTarget)->IsHeapObject() &&
+ // The IntrusivePrototypeTransitionIterator may have set the map of the
+ // prototype transitions array to a smi. In that case, there are
+ // prototype transitions, hence this transition array is a full
+ // transition array.
+ HeapObject::cast(get(kSimpleTransitionTarget))->map()->IsMap() &&
+ get(kSimpleTransitionTarget)->IsMap();
+ }
- static int Capacity(Object* raw_transitions);
+ bool IsFullTransitionArray() {
+ return length() > kFirstIndex ||
+ (length() == kFirstIndex && !IsSimpleTransition());
+ }
// Casting.
static inline TransitionArray* cast(Object* obj);
+ // Constant for denoting key was not found.
+ static const int kNotFound = -1;
+
+ static const int kBackPointerStorageIndex = 0;
+
+ // Layout for full transition arrays.
+ static const int kPrototypeTransitionsIndex = 1;
+ static const int kTransitionLengthIndex = 2;
+ static const int kFirstIndex = 3;
+
+ // Layout for simple transition arrays.
+ static const int kSimpleTransitionTarget = 1;
+ static const int kSimpleTransitionSize = 2;
+ static const int kSimpleTransitionIndex = 0;
+ STATIC_ASSERT(kSimpleTransitionIndex != kNotFound);
+
+ static const int kBackPointerStorageOffset = FixedArray::kHeaderSize;
+
+ // Layout for the full transition array header.
+ static const int kPrototypeTransitionsOffset = kBackPointerStorageOffset +
+ kPointerSize;
+ static const int kTransitionLengthOffset =
+ kPrototypeTransitionsOffset + kPointerSize;
+
+ // Layout of map transition entries in full transition arrays.
+ static const int kTransitionKey = 0;
+ static const int kTransitionTarget = 1;
static const int kTransitionSize = 2;
- static const int kProtoTransitionHeaderSize = 1;
#if defined(DEBUG) || defined(OBJECT_PRINT)
// For our gdb macros, we should perhaps change these in the future.
void Print();
// Print all the transitions.
- static void PrintTransitions(std::ostream& os, Object* transitions,
- bool print_header = true); // NOLINT
+ void PrintTransitions(std::ostream& os, bool print_header = true); // NOLINT
#endif
#ifdef DEBUG
bool IsSortedNoDuplicates(int valid_entries = -1);
- static bool IsSortedNoDuplicates(Map* map);
- static bool IsConsistentWithBackPointers(Map* map);
+ 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
- // Constant for denoting key was not found.
- static const int kNotFound = -1;
-
// The maximum number of transitions we want in a transition array (should
// fit in a page).
static const int kMaxNumberOfTransitions = 1024 + 512;
- private:
- // Layout for full transition arrays.
- static const int kPrototypeTransitionsIndex = 0;
- static const int kTransitionLengthIndex = 1;
- static const int kFirstIndex = 2;
-
- // Layout of map transition entries in full transition arrays.
- static const int kTransitionKey = 0;
- static const int kTransitionTarget = 1;
- STATIC_ASSERT(kTransitionSize == 2);
-
- static const int kProtoTransitionNumberOfEntriesOffset = 0;
- STATIC_ASSERT(kProtoTransitionHeaderSize == 1);
+ // Returns the fixed array length required to hold number_of_transitions
+ // transitions.
+ static int LengthFor(int number_of_transitions) {
+ return ToKeyIndex(number_of_transitions);
+ }
+ private:
// Conversion from transition number to array indices.
static int ToKeyIndex(int transition_number) {
return kFirstIndex +
kTransitionTarget;
}
- // Returns the fixed array length required to hold number_of_transitions
- // transitions.
- static int LengthFor(int number_of_transitions) {
- return ToKeyIndex(number_of_transitions);
- }
-
- // Allocates a TransitionArray.
- static Handle<TransitionArray> Allocate(Isolate* isolate,
- int number_of_transitions,
- int slack = 0);
-
- static void EnsureHasFullTransitionArray(Handle<Map> map);
- static void ReplaceTransitions(Handle<Map> map, Object* new_transitions);
+ static Handle<TransitionArray> AllocateSimple(
+ Isolate* isolate, Handle<Map> target);
- // Search a transition for a given kind, property name and attributes.
- int Search(PropertyKind kind, Name* name, PropertyAttributes attributes,
- int* out_insertion_index = NULL);
+ // Allocate a new transition array with a single entry.
+ static Handle<TransitionArray> NewWith(Handle<Map> map,
+ Handle<Name> name,
+ Handle<Map> target,
+ SimpleTransitionFlag flag);
- // 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 first transition for a given property name.
inline int SearchName(Name* name, int* out_insertion_index = NULL);
int SearchDetails(int transition, PropertyKind kind,
PropertyAttributes attributes, int* out_insertion_index);
- int number_of_transitions() {
- if (length() < kFirstIndex) return 0;
- return Smi::cast(get(kTransitionLengthIndex))->value();
- }
-
- static inline PropertyDetails GetSimpleTargetDetails(Map* transition) {
- return transition->GetLastDescriptorDetails();
- }
-
- static inline Name* GetSimpleTransitionKey(Map* transition) {
- int descriptor = transition->LastAdded();
- return transition->instance_descriptors()->GetKey(descriptor);
- }
-
- static void TraverseTransitionTreeInternal(Map* map,
- TraverseCallback callback,
- void* data);
-
- static void SetPrototypeTransitions(Handle<Map> map,
- Handle<FixedArray> proto_transitions);
-
// Compares two tuples <key, kind, 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, PropertyKind kind1,
int origin_transition,
int target_transition);
-#ifdef DEBUG
- static void CheckNewTransitionsAreConsistent(Handle<Map> map,
- TransitionArray* old_transitions,
- Object* transitions);
-#endif
-
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
};
}
-static int NumberOfProtoTransitions(Map* map) {
- return TransitionArray::NumberOfPrototypeTransitions(
- TransitionArray::GetPrototypeTransitions(map));
-}
-
-
TEST(PrototypeTransitionClearing) {
if (FLAG_never_compact) return;
CcTest::InitializeVM();
v8::Utils::OpenHandle(
*v8::Handle<v8::Object>::Cast(
CcTest::global()->Get(v8_str("base"))));
- int initialTransitions = NumberOfProtoTransitions(baseObject->map());
+ int initialTransitions = baseObject->map()->NumberOfProtoTransitions();
CompileRun(
"var live = [];"
// Verify that only dead prototype transitions are cleared.
CHECK_EQ(initialTransitions + 10,
- NumberOfProtoTransitions(baseObject->map()));
+ baseObject->map()->NumberOfProtoTransitions());
CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
const int transitions = 10 - 3;
CHECK_EQ(initialTransitions + transitions,
- NumberOfProtoTransitions(baseObject->map()));
+ baseObject->map()->NumberOfProtoTransitions());
// Verify that prototype transitions array was compacted.
- FixedArray* trans =
- TransitionArray::GetPrototypeTransitions(baseObject->map());
+ FixedArray* trans = baseObject->map()->GetPrototypeTransitions();
for (int i = initialTransitions; i < initialTransitions + transitions; i++) {
- int j = TransitionArray::kProtoTransitionHeaderSize + i;
+ int j = Map::kProtoTransitionHeaderSize + i;
CHECK(trans->get(j)->IsMap());
}
i::FLAG_always_compact = true;
Handle<Map> map(baseObject->map());
CHECK(!space->LastPage()->Contains(
- TransitionArray::GetPrototypeTransitions(*map)->address()));
+ map->GetPrototypeTransitions()->address()));
CHECK(space->LastPage()->Contains(prototype->address()));
}
static int CountMapTransitions(Map* map) {
- return TransitionArray::NumberOfTransitions(map->raw_transitions());
+ return map->transitions()->number_of_transitions();
}
CompileRun("o = new F;"
"root = new F");
root = GetByName("root");
- DCHECK(TransitionArray::IsSimpleTransition(root->map()->raw_transitions()));
+ DCHECK(root->map()->transitions()->IsSimpleTransition());
AddPropertyTo(2, root, "happy");
// Count number of live transitions after marking. Note that one transition
SetDataField(property_index, attributes, representation, heap_type);
Handle<String> name = MakeName("prop", property_index);
- Map* target =
- TransitionArray::SearchTransition(*map, kData, *name, attributes);
- CHECK(target != NULL);
- return handle(target);
+ int t = map->SearchTransition(kData, *name, attributes);
+ CHECK_NE(TransitionArray::kNotFound, t);
+ return handle(map->GetTransition(t));
}
Handle<Map> AddAccessorConstant(Handle<Map> map,
}
Handle<String> name = MakeName("prop", i);
- Map* target =
- TransitionArray::SearchTransition(*map2, kData, *name, NONE);
- CHECK(target != NULL);
- map2 = handle(target);
+ int t = map2->SearchTransition(kData, *name, NONE);
+ CHECK_NE(TransitionArray::kNotFound, t);
+ map2 = handle(map2->GetTransition(t));
}
map2 = Map::ReconfigureProperty(map2, kSplitProp, kData, NONE,
// Fill in transition tree of |map2| so that it can't have more transitions.
for (int i = 0; i < TransitionArray::kMaxNumberOfTransitions; i++) {
- CHECK(TransitionArray::CanHaveMoreTransitions(map2));
+ CHECK(map2->CanHaveMoreTransitions());
Handle<String> name = MakeName("foo", i);
Map::CopyWithField(map2, name, any_type, NONE, Representation::Smi(),
INSERT_TRANSITION).ToHandleChecked();
}
- CHECK(!TransitionArray::CanHaveMoreTransitions(map2));
+ CHECK(!map2->CanHaveMoreTransitions());
// Try to update |map|, since there is no place for propX transition at |map2|
// |map| should become "copy-generalized".
// 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);
+}
+
+
static void CheckPropertyDetailsFieldsConsistency(PropertyType type,
PropertyKind kind,
PropertyLocation location) {
attributes, Representation::Tagged(),
OMIT_TRANSITION).ToHandleChecked();
- CHECK(map0->raw_transitions()->IsSmi());
-
- TransitionArray::Insert(map0, name1, map1, SIMPLE_PROPERTY_TRANSITION);
- CHECK(TransitionArray::IsSimpleTransition(map0->raw_transitions()));
- CHECK_EQ(*map1,
- TransitionArray::SearchTransition(*map0, kData, *name1, attributes));
- CHECK_EQ(1, TransitionArray::NumberOfTransitions(map0->raw_transitions()));
- CHECK_EQ(*name1, TransitionArray::GetKey(map0->raw_transitions(), 0));
- CHECK_EQ(*map1, TransitionArray::GetTarget(map0->raw_transitions(), 0));
-
- TransitionArray::Insert(map0, name2, map2, SIMPLE_PROPERTY_TRANSITION);
- CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions()));
-
- CHECK_EQ(*map1,
- TransitionArray::SearchTransition(*map0, kData, *name1, attributes));
- CHECK_EQ(*map2,
- TransitionArray::SearchTransition(*map0, kData, *name2, attributes));
- CHECK_EQ(2, TransitionArray::NumberOfTransitions(map0->raw_transitions()));
- for (int i = 0; i < 2; i++) {
- Name* key = TransitionArray::GetKey(map0->raw_transitions(), i);
- Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i);
- CHECK((key == *name1 && target == *map1) ||
- (key == *name2 && target == *map2));
- }
-
- DCHECK(TransitionArray::IsSortedNoDuplicates(*map0));
+ 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(kData, *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(kData, *name1, attributes);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transition = transitions->Search(kData, *name2, attributes);
+ CHECK_EQ(*name2, transitions->GetKey(transition));
+ CHECK_EQ(*map2, transitions->GetTarget(transition));
+
+ DCHECK(transitions->IsSortedNoDuplicates());
}
attributes, Representation::Tagged(),
OMIT_TRANSITION).ToHandleChecked();
- CHECK(map0->raw_transitions()->IsSmi());
-
- TransitionArray::Insert(map0, name1, map1, PROPERTY_TRANSITION);
- CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions()));
- CHECK_EQ(*map1,
- TransitionArray::SearchTransition(*map0, kData, *name1, attributes));
- CHECK_EQ(1, TransitionArray::NumberOfTransitions(map0->raw_transitions()));
- CHECK_EQ(*name1, TransitionArray::GetKey(map0->raw_transitions(), 0));
- CHECK_EQ(*map1, TransitionArray::GetTarget(map0->raw_transitions(), 0));
-
- TransitionArray::Insert(map0, name2, map2, PROPERTY_TRANSITION);
- CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions()));
-
- CHECK_EQ(*map1,
- TransitionArray::SearchTransition(*map0, kData, *name1, attributes));
- CHECK_EQ(*map2,
- TransitionArray::SearchTransition(*map0, kData, *name2, attributes));
- CHECK_EQ(2, TransitionArray::NumberOfTransitions(map0->raw_transitions()));
- for (int i = 0; i < 2; i++) {
- Name* key = TransitionArray::GetKey(map0->raw_transitions(), i);
- Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i);
- CHECK((key == *name1 && target == *map1) ||
- (key == *name2 && target == *map2));
- }
+ 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(kData, *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());
- DCHECK(TransitionArray::IsSortedNoDuplicates(*map0));
+ transition = transitions->Search(kData, *name1, attributes);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transition = transitions->Search(kData, *name2, attributes);
+ CHECK_EQ(*name2, transitions->GetKey(transition));
+ CHECK_EQ(*map2, transitions->GetTarget(transition));
+
+ DCHECK(transitions->IsSortedNoDuplicates());
}
PropertyAttributes attributes = NONE;
Handle<Map> map0 = Map::Create(isolate, 0);
- CHECK(map0->raw_transitions()->IsSmi());
+ 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;
names[i] = name;
maps[i] = map;
- TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION);
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
}
for (int i = 0; i < PROPS_COUNT; i++) {
- CHECK_EQ(*maps[i], TransitionArray::SearchTransition(
- *map0, kData, *names[i], attributes));
- }
- for (int i = 0; i < PROPS_COUNT; i++) {
- Name* key = TransitionArray::GetKey(map0->raw_transitions(), i);
- Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i);
- for (int j = 0; j < PROPS_COUNT; j++) {
- if (*names[i] == key) {
- CHECK_EQ(*maps[i], target);
- break;
- }
- }
+ int transition = transitions->Search(kData, *names[i], attributes);
+ CHECK_EQ(*names[i], transitions->GetKey(transition));
+ CHECK_EQ(*maps[i], transitions->GetTarget(transition));
}
- DCHECK(TransitionArray::IsSortedNoDuplicates(*map0));
+ DCHECK(transitions->IsSortedNoDuplicates());
}
Factory* factory = isolate->factory();
Handle<Map> map0 = Map::Create(isolate, 0);
- CHECK(map0->raw_transitions()->IsSmi());
+ 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);
OMIT_TRANSITION).ToHandleChecked();
attr_maps[i] = map;
- TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION);
+ 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);
- CHECK_EQ(*attr_maps[i], TransitionArray::SearchTransition(
- *map0, kData, *name, attributes));
- // All transitions use the same key, so this check doesn't need to
- // care about ordering.
- CHECK_EQ(*name, TransitionArray::GetKey(map0->raw_transitions(), i));
+
+ int transition = transitions->Search(kData, *name, attributes);
+ CHECK_EQ(*name, transitions->GetKey(transition));
+ CHECK_EQ(*attr_maps[i], transitions->GetTarget(transition));
}
- DCHECK(TransitionArray::IsSortedNoDuplicates(*map0));
+ DCHECK(transitions->IsSortedNoDuplicates());
}
Handle<Map> maps[PROPS_COUNT];
Handle<Map> map0 = Map::Create(isolate, 0);
- CHECK(map0->raw_transitions()->IsSmi());
+ 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++) {
names[i] = name;
maps[i] = map;
- TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION);
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
}
const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1;
OMIT_TRANSITION).ToHandleChecked();
attr_maps[i] = map;
- TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION);
+ 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 attr = static_cast<PropertyAttributes>(i);
- CHECK_EQ(*attr_maps[i],
- TransitionArray::SearchTransition(*map0, kData, *name, attr));
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ int transition = transitions->Search(kData, *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.
- CHECK_EQ(PROPS_COUNT + ATTRS_COUNT,
- TransitionArray::NumberOfTransitions(map0->raw_transitions()));
- for (int i = 0; i < PROPS_COUNT + ATTRS_COUNT; i++) {
- Name* key = TransitionArray::GetKey(map0->raw_transitions(), i);
- Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i);
- if (key == *name) {
- // Attributes transition.
- PropertyAttributes attributes =
- target->GetLastDescriptorDetails().attributes();
- CHECK_EQ(*attr_maps[static_cast<int>(attributes)], target);
- } else {
- for (int j = 0; j < PROPS_COUNT; j++) {
- if (*names[j] == key) {
- CHECK_EQ(*maps[j], target);
- break;
- }
- }
- }
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ int transition = transitions->Search(kData, *names[i], NONE);
+ CHECK_EQ(*names[i], transitions->GetKey(transition));
+ CHECK_EQ(*maps[i], transitions->GetTarget(transition));
}
- DCHECK(TransitionArray::IsSortedNoDuplicates(*map0));
+ DCHECK(transitions->IsSortedNoDuplicates());
}