}
+enum TransitionsKind { DESCRIPTORS_HOLDER, FULL_TRANSITION_ARRAY };
+
+
// If the descriptor is using the empty transition array, install a new empty
// transition array that will have place for an element transition.
-static MaybeObject* EnsureHasTransitionArray(Map* map) {
- if (map->HasTransitionArray()) return map;
-
+static MaybeObject* EnsureHasTransitionArray(Map* map, TransitionsKind kind) {
TransitionArray* transitions;
- JSGlobalPropertyCell* pointer = map->RetrieveDescriptorsPointer();
- MaybeObject* maybe_transitions = TransitionArray::Allocate(0, pointer);
- if (!maybe_transitions->To(&transitions)) return maybe_transitions;
-
- transitions->set_back_pointer_storage(map->GetBackPointer());
+ MaybeObject* maybe_transitions;
+ if (map->HasTransitionArray()) {
+ if (kind != FULL_TRANSITION_ARRAY ||
+ map->transitions()->IsFullTransitionArray()) {
+ return map;
+ }
+ maybe_transitions = map->transitions()->ExtendToFullTransitionArray();
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ } else {
+ JSGlobalPropertyCell* pointer = map->RetrieveDescriptorsPointer();
+ if (kind == FULL_TRANSITION_ARRAY) {
+ maybe_transitions = TransitionArray::Allocate(0, pointer);
+ } else {
+ maybe_transitions = TransitionArray::AllocateDescriptorsHolder(pointer);
+ }
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ transitions->set_back_pointer_storage(map->GetBackPointer());
+ }
map->set_transitions(transitions);
return transitions;
}
MaybeObject* Map::SetDescriptors(DescriptorArray* value) {
ASSERT(!is_shared());
- MaybeObject* maybe_failure = EnsureHasTransitionArray(this);
+ MaybeObject* maybe_failure =
+ EnsureHasTransitionArray(this, DESCRIPTORS_HOLDER);
if (maybe_failure->IsFailure()) return maybe_failure;
ASSERT(NumberOfOwnDescriptors() <= value->number_of_descriptors());
}
-MaybeObject* Map::AddTransition(String* key, Map* target) {
+MaybeObject* Map::AddTransition(String* key,
+ Map* target,
+ SimpleTransitionFlag flag) {
if (HasTransitionArray()) return transitions()->CopyInsert(key, target);
JSGlobalPropertyCell* descriptors_pointer = RetrieveDescriptorsPointer();
return TransitionArray::NewWith(
- key, target, descriptors_pointer, GetBackPointer());
+ flag, key, target, descriptors_pointer, GetBackPointer());
}
MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) {
- MaybeObject* allow_elements = EnsureHasTransitionArray(this);
+ MaybeObject* allow_elements =
+ EnsureHasTransitionArray(this, FULL_TRANSITION_ARRAY);
if (allow_elements->IsFailure()) return allow_elements;
transitions()->set_elements_transition(transitioned_map);
return this;
MaybeObject* Map::SetPrototypeTransitions(FixedArray* proto_transitions) {
- MaybeObject* allow_prototype = EnsureHasTransitionArray(this);
+ MaybeObject* allow_prototype =
+ EnsureHasTransitionArray(this, FULL_TRANSITION_ARRAY);
if (allow_prototype->IsFailure()) return allow_prototype;
#ifdef DEBUG
if (HasPrototypeTransitions()) {
// is not compacted and descriptors are referenced through a cell.
StaticVisitor::MarkObject(heap, transitions->descriptors_pointer());
+ // 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
String* name = descriptor->GetKey();
TransitionArray* transitions;
- MaybeObject* maybe_transitions = AddTransition(name, result);
+ MaybeObject* maybe_transitions =
+ AddTransition(name, result, SIMPLE_TRANSITION);
if (!maybe_transitions->To(&transitions)) return maybe_transitions;
DescriptorArray* descriptors = instance_descriptors();
MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
String* name,
- TransitionFlag flag) {
+ TransitionFlag flag,
+ int descriptor_index) {
ASSERT(descriptors->IsSortedNoDuplicates());
Map* result;
if (flag == INSERT_TRANSITION && CanHaveMoreTransitions()) {
TransitionArray* transitions;
- MaybeObject* maybe_transitions = AddTransition(name, result);
+ SimpleTransitionFlag simple_flag =
+ (descriptor_index == descriptors->number_of_descriptors() - 1)
+ ? SIMPLE_TRANSITION
+ : FULL_TRANSITION;
+ MaybeObject* maybe_transitions = AddTransition(name, result, simple_flag);
if (!maybe_transitions->To(&transitions)) return maybe_transitions;
if (descriptors->IsEmpty()) {
descriptors->CopyUpTo(number_of_own_descriptors);
if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
- return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION);
+ return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0);
}
descriptors->CopyUpTo(number_of_own_descriptors);
if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
- return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION);
+ return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0);
}
}
String* key = descriptor->GetKey();
+ int insertion_index = new_descriptors->number_of_descriptors() - 1;
- return CopyReplaceDescriptors(new_descriptors, key, flag);
+ return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index);
}
// Re-sort if descriptors were removed.
if (new_size != descriptors->length()) new_descriptors->Sort();
- return CopyReplaceDescriptors(new_descriptors, key, flag);
+ return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index);
}
int trim = t->number_of_transitions() - transition_index;
if (trim > 0) {
- RightTrimFixedArray<FROM_GC>(
- heap, t, trim * TransitionArray::kTransitionSize);
+ RightTrimFixedArray<FROM_GC>(heap, t, t->IsSimpleTransition()
+ ? trim : trim * TransitionArray::kTransitionSize);
}
}
OMIT_TRANSITION
};
+
+// Indicates whether the transition is simple: the target map of the transition
+// either extends the current map with a new property, or it modifies the
+// property that was added last to the current map.
+enum SimpleTransitionFlag {
+ SIMPLE_TRANSITION,
+ FULL_TRANSITION
+};
+
+
// Indicates whether we are only interested in the descriptors of a particular
// map, or in all descriptors in the descriptor array.
enum DescriptorFlag {
Map* transitioned_map);
inline void SetTransition(int transition_index, Map* target);
inline Map* GetTransition(int transition_index);
- MUST_USE_RESULT inline MaybeObject* AddTransition(String* key, Map* target);
+ MUST_USE_RESULT inline MaybeObject* AddTransition(String* key,
+ Map* target,
+ SimpleTransitionFlag flag);
DECL_ACCESSORS(transitions, TransitionArray)
inline void ClearTransitions(Heap* heap,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
MUST_USE_RESULT MaybeObject* CopyReplaceDescriptors(
DescriptorArray* descriptors,
String* name,
- TransitionFlag flag);
+ TransitionFlag flag,
+ int descriptor_index);
MUST_USE_RESULT MaybeObject* ShareDescriptor(Descriptor* descriptor);
MUST_USE_RESULT MaybeObject* CopyAddDescriptor(Descriptor* descriptor,
TransitionFlag flag);
bool TransitionArray::HasElementsTransition() {
- return get(kElementsTransitionIndex) != Smi::FromInt(0);
+ return IsFullTransitionArray() &&
+ get(kElementsTransitionIndex) != Smi::FromInt(0);
}
void TransitionArray::set_elements_transition(Map* transition_map,
WriteBarrierMode mode) {
+ ASSERT(IsFullTransitionArray());
Heap* heap = GetHeap();
WRITE_FIELD(this, kElementsTransitionOffset, transition_map);
CONDITIONAL_WRITE_BARRIER(
bool TransitionArray::HasPrototypeTransitions() {
- Object* prototype_transitions = get(kPrototypeTransitionsIndex);
- return prototype_transitions != Smi::FromInt(0);
+ return IsFullTransitionArray() &&
+ get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
}
FixedArray* TransitionArray::GetPrototypeTransitions() {
+ ASSERT(IsFullTransitionArray());
Object* prototype_transitions = get(kPrototypeTransitionsIndex);
return FixedArray::cast(prototype_transitions);
}
void TransitionArray::SetPrototypeTransitions(FixedArray* transitions,
WriteBarrierMode mode) {
- ASSERT(this != NULL);
+ ASSERT(IsFullTransitionArray());
ASSERT(transitions->IsFixedArray());
Heap* heap = GetHeap();
WRITE_FIELD(this, kPrototypeTransitionsOffset, transitions);
Object** TransitionArray::GetKeySlot(int transition_number) {
+ ASSERT(!IsSimpleTransition());
ASSERT(transition_number < number_of_transitions());
return HeapObject::RawField(
reinterpret_cast<HeapObject*>(this),
String* TransitionArray::GetKey(int transition_number) {
+ if (IsSimpleTransition()) {
+ Map* target = GetTarget(kSimpleTransitionIndex);
+ int descriptor = target->LastAdded();
+ String* key = target->instance_descriptors()->GetKey(descriptor);
+ return key;
+ }
ASSERT(transition_number < number_of_transitions());
return String::cast(get(ToKeyIndex(transition_number)));
}
void TransitionArray::SetKey(int transition_number, String* key) {
+ ASSERT(!IsSimpleTransition());
ASSERT(transition_number < number_of_transitions());
set(ToKeyIndex(transition_number), key);
}
Map* TransitionArray::GetTarget(int transition_number) {
+ if (IsSimpleTransition()) {
+ ASSERT(transition_number == kSimpleTransitionIndex);
+ return Map::cast(get(kSimpleTransitionTarget));
+ }
ASSERT(transition_number < number_of_transitions());
return Map::cast(get(ToTargetIndex(transition_number)));
}
void TransitionArray::SetTarget(int transition_number, Map* value) {
+ if (IsSimpleTransition()) {
+ ASSERT(transition_number == kSimpleTransitionIndex);
+ return set(kSimpleTransitionTarget, value);
+ }
ASSERT(transition_number < number_of_transitions());
set(ToTargetIndex(transition_number), value);
}
namespace internal {
-MaybeObject* TransitionArray::Allocate(int number_of_transitions,
- JSGlobalPropertyCell* descriptors_cell) {
+static MaybeObject* AllocateRaw(int length,
+ JSGlobalPropertyCell* descriptors_cell) {
Heap* heap = Isolate::Current()->heap();
if (descriptors_cell == NULL) {
if (!maybe_cell->To(&descriptors_cell)) return maybe_cell;
}
- // Use FixedArray to not use DescriptorArray::cast on incomplete object.
+ // Use FixedArray to not use TransitionArray::cast on incomplete object.
FixedArray* array;
- MaybeObject* maybe_array =
- heap->AllocateFixedArray(ToKeyIndex(number_of_transitions));
+ MaybeObject* maybe_array = heap->AllocateFixedArray(length);
if (!maybe_array->To(&array)) return maybe_array;
- array->set(kDescriptorsPointerIndex, descriptors_cell);
+ array->set(TransitionArray::kDescriptorsPointerIndex, descriptors_cell);
+ return array;
+}
+
+
+MaybeObject* TransitionArray::Allocate(int number_of_transitions,
+ JSGlobalPropertyCell* descriptors_cell) {
+ FixedArray* array;
+ MaybeObject* maybe_array =
+ AllocateRaw(ToKeyIndex(number_of_transitions), descriptors_cell);
+ if (!maybe_array->To(&array)) return maybe_array;
array->set(kElementsTransitionIndex, Smi::FromInt(0));
array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
return array;
}
-MaybeObject* TransitionArray::NewWith(
- String* name,
- Map* target,
- JSGlobalPropertyCell* descriptors_pointer,
- Object* back_pointer) {
+MaybeObject* TransitionArray::NewWith(SimpleTransitionFlag flag,
+ String* key,
+ Map* target,
+ JSGlobalPropertyCell* descriptors_pointer,
+ Object* back_pointer) {
+ TransitionArray* result;
+ MaybeObject* maybe_result;
+
+ if (flag == SIMPLE_TRANSITION) {
+ maybe_result = AllocateRaw(kSimpleTransitionSize, descriptors_pointer);
+ if (!maybe_result->To(&result)) return maybe_result;
+ result->set(kSimpleTransitionTarget, target);
+ } else {
+ maybe_result = Allocate(1, descriptors_pointer);
+ if (!maybe_result->To(&result)) return maybe_result;
+ result->NoIncrementalWriteBarrierSet(0, key, target);
+ }
+ result->set_back_pointer_storage(back_pointer);
+ return result;
+}
+
+
+MaybeObject* TransitionArray::AllocateDescriptorsHolder(
+ JSGlobalPropertyCell* descriptors_pointer) {
+ return AllocateRaw(kDescriptorsHolderSize, descriptors_pointer);
+}
+
+
+MaybeObject* TransitionArray::ExtendToFullTransitionArray() {
+ ASSERT(!IsFullTransitionArray());
+ int nof = number_of_transitions();
TransitionArray* result;
+ MaybeObject* maybe_result = Allocate(nof, descriptors_pointer());
+ if (!maybe_result->To(&result)) return maybe_result;
- MaybeObject* maybe_array = TransitionArray::Allocate(1, descriptors_pointer);
- if (!maybe_array->To(&result)) return maybe_array;
+ if (nof == 1) {
+ result->NoIncrementalWriteBarrierCopyFrom(this, kSimpleTransitionIndex, 0);
+ }
- result->NoIncrementalWriteBarrierSet(0, name, target);
- result->set_back_pointer_storage(back_pointer);
+ result->set_back_pointer_storage(back_pointer_storage());
return result;
}
// Returns the number of transitions in the array.
int number_of_transitions() {
- ASSERT(length() >= kFirstIndex);
+ if (IsSimpleTransition()) return 1;
int len = length();
return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kTransitionSize;
}
// Allocate a new transition array with a single entry.
static MUST_USE_RESULT MaybeObject* NewWith(
- String* name,
+ SimpleTransitionFlag flag,
+ String* key,
Map* target,
JSGlobalPropertyCell* descriptor_pointer,
Object* back_pointer);
+ static MUST_USE_RESULT MaybeObject* AllocateDescriptorsHolder(
+ JSGlobalPropertyCell* descriptor_pointer);
+
+ MUST_USE_RESULT MaybeObject* ExtendToFullTransitionArray();
+
// Copy the transition array, inserting a new transition.
// TODO(verwaest): This should not cause an existing transition to be
// overwritten.
int number_of_transitions,
JSGlobalPropertyCell* descriptors_cell);
+ bool IsDescriptorsHolder() { return length() == kDescriptorsHolderSize; }
+ bool IsSimpleTransition() { return length() == kSimpleTransitionSize; }
+ bool IsFullTransitionArray() { return length() >= kFirstIndex; }
+
// Casting.
static inline TransitionArray* cast(Object* obj);
static const int kDescriptorsPointerIndex = 0;
static const int kBackPointerStorageIndex = 1;
+ static const int kDescriptorsHolderSize = 2;
+
+ // Layout for full transition arrays.
static const int kElementsTransitionIndex = 2;
static const int kPrototypeTransitionsIndex = 3;
static const int kFirstIndex = 4;
- // Layout transition array header.
+ // Layout for simple transition arrays.
+ static const int kSimpleTransitionTarget = 2;
+ static const int kSimpleTransitionSize = 3;
+ static const int kSimpleTransitionIndex = 0;
+ STATIC_ASSERT(kSimpleTransitionIndex != kNotFound);
+
static const int kDescriptorsPointerOffset = FixedArray::kHeaderSize;
static const int kBackPointerStorageOffset = kDescriptorsPointerOffset +
kPointerSize;
+
+ // Layout for the full transition array header.
static const int kElementsTransitionOffset = kBackPointerStorageOffset +
kPointerSize;
static const int kPrototypeTransitionsOffset = kElementsTransitionOffset +
kPointerSize;
- // Layout of map transition.
+ // Layout of map transition entries in full transition arrays.
static const int kTransitionKey = 0;
static const int kTransitionTarget = 1;
static const int kTransitionSize = 2;