Handle<Object> descriptors) {
v8::NeanderArray callbacks(descriptors);
int nof_callbacks = callbacks.length();
+ int descriptor_count = array->number_of_descriptors();
Handle<DescriptorArray> result =
- NewDescriptorArray(array->number_of_descriptors() + nof_callbacks);
-
- // Number of descriptors added to the result so far.
- int descriptor_count = 0;
+ NewDescriptorArray(descriptor_count + nof_callbacks);
// Ensure that marking will not progress and change color of objects.
DescriptorArray::WhitenessWitness witness(*result);
// Copy the descriptors from the array.
- for (int i = 0; i < array->number_of_descriptors(); i++) {
- if (!array->IsNullDescriptor(i)) {
- DescriptorArray::CopyFrom(result, descriptor_count++, array, i, witness);
- }
+ for (int i = 0; i < descriptor_count; i++) {
+ DescriptorArray::CopyFrom(result, i, array, i, witness);
}
- // Number of duplicates detected.
- int duplicates = 0;
-
// Fill in new callback descriptors. Process the callbacks from
// back to front so that the last callback with a given name takes
// precedence over previously added callbacks with that name.
CallbacksDescriptor desc(*key, *entry, entry->property_attributes());
result->Set(descriptor_count, &desc, witness);
descriptor_count++;
- } else {
- duplicates++;
}
}
// If duplicates were detected, allocate a result of the right size
// and transfer the elements.
- if (duplicates > 0) {
- int number_of_descriptors = result->number_of_descriptors() - duplicates;
- Handle<DescriptorArray> new_result =
- NewDescriptorArray(number_of_descriptors);
- for (int i = 0; i < number_of_descriptors; i++) {
+ if (descriptor_count < result->length()) {
+ Handle<DescriptorArray> new_result = NewDescriptorArray(descriptor_count);
+ for (int i = 0; i < descriptor_count; i++) {
DescriptorArray::CopyFrom(new_result, i, result, i, witness);
}
result = new_result;
void FixedArray::set_null_unchecked(Heap* heap, int index) {
ASSERT(index >= 0 && index < this->length());
- ASSERT(!HEAP->InNewSpace(heap->null_value()));
+ ASSERT(!heap->InNewSpace(heap->null_value()));
WRITE_FIELD(this, kHeaderSize + index * kPointerSize, heap->null_value());
}
}
+void DescriptorArray::SetKeyUnchecked(Heap* heap,
+ int descriptor_number,
+ String* key) {
+ ASSERT(descriptor_number < number_of_descriptors());
+ set_unchecked(heap,
+ ToKeyIndex(descriptor_number),
+ key,
+ UPDATE_WRITE_BARRIER);
+}
+
+
Object** DescriptorArray::GetValueSlot(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return HeapObject::RawField(
}
-void DescriptorArray::SetNullValueUnchecked(int descriptor_number, Heap* heap) {
+void DescriptorArray::SetNullValueUnchecked(Heap* heap, int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
set_null_unchecked(heap, ToValueIndex(descriptor_number));
}
+
+void DescriptorArray::SetValueUnchecked(Heap* heap,
+ int descriptor_number,
+ Object* value) {
+ ASSERT(descriptor_number < number_of_descriptors());
+ set_unchecked(heap,
+ ToValueIndex(descriptor_number),
+ value,
+ UPDATE_WRITE_BARRIER);
+}
+
+
PropertyDetails DescriptorArray::GetDetails(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
Object* details = get(ToDetailsIndex(descriptor_number));
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
- case NULL_DESCRIPTOR:
return false;
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
}
UNREACHABLE(); // Keep the compiler happy.
return false;
}
-bool DescriptorArray::IsNullDescriptor(int descriptor_number) {
- return GetType(descriptor_number) == NULL_DESCRIPTOR;
-}
-
-
void DescriptorArray::Get(int descriptor_number, Descriptor* desc) {
desc->Init(GetKey(descriptor_number),
GetValue(descriptor_number),
}
+void Map::ClearDescriptorArray() {
+ int bitfield3 = bit_field3();
+#ifdef DEBUG
+ Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
+ if (!object->IsSmi()) {
+ ZapInstanceDescriptors();
+ }
+#endif
+ WRITE_FIELD(this,
+ kInstanceDescriptorsOrBitField3Offset,
+ Smi::FromInt(bitfield3));
+}
+
+
void Map::set_bit_field3(int value) {
ASSERT(Smi::IsValid(value));
- Object* object = READ_FIELD(this,
- kInstanceDescriptorsOrBitField3Offset);
+ Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (object->IsSmi()) {
WRITE_FIELD(this,
kInstanceDescriptorsOrBitField3Offset,
case HANDLER:
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
+ case NONEXISTENT:
UNREACHABLE();
}
}
}
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
+ break;
+ case NONEXISTENT:
+ UNREACHABLE();
break;
}
UNREACHABLE();
}
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
+ break;
+ case NONEXISTENT:
+ UNREACHABLE();
break;
}
}
// FIELD, even if the value is a constant function.
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
}
- case NULL_DESCRIPTOR:
- return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case HANDLER:
+ case NONEXISTENT:
UNREACHABLE();
return value;
}
case CONSTANT_TRANSITION:
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
// if the value is a function.
- case NULL_DESCRIPTOR:
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case HANDLER:
+ case NONEXISTENT:
UNREACHABLE();
}
UNREACHABLE(); // keep the compiler happy
}
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
case INTERCEPTOR:
break;
case HANDLER:
case NORMAL:
+ case NONEXISTENT:
UNREACHABLE();
break;
}
this->FastPropertyAt(descriptors->GetFieldIndex(0));
return StringDictionary::cast(hidden_store);
} else {
- ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR ||
- descriptors->GetType(0) == MAP_TRANSITION);
+ ASSERT(descriptors->GetType(0) == MAP_TRANSITION);
}
}
} else {
this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary);
return this;
} else {
- ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR ||
- descriptors->GetType(0) == MAP_TRANSITION);
+ ASSERT(descriptors->GetType(0) == MAP_TRANSITION);
}
}
}
int Map::PropertyIndexFor(String* name) {
DescriptorArray* descs = instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
- return descs->GetFieldIndex(i);
- }
+ if (name->Equals(descs->GetKey(i))) return descs->GetFieldIndex(i);
}
return -1;
}
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
- case NULL_DESCRIPTOR:
// We definitely have no map transition.
raw_index += 2;
++index;
break;
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
}
}
if (index == descriptor_array_->number_of_descriptors()) {
// removing all other transitions is not supported.
bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
ASSERT(remove_transitions == !descriptor->ContainsTransition());
- ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
// Ensure the key is a symbol.
{ MaybeObject* maybe_result = descriptor->KeyToSymbol();
int new_size = 0;
for (int i = 0; i < number_of_descriptors(); i++) {
- if (IsNullDescriptor(i)) continue;
if (remove_transitions && IsTransitionOnly(i)) continue;
new_size++;
}
insertion_index = to_index++;
if (replacing) from_index++;
} else {
- if (!(IsNullDescriptor(from_index) ||
- (remove_transitions && IsTransitionOnly(from_index)))) {
+ if (!(remove_transitions && IsTransitionOnly(from_index))) {
MaybeObject* copy_result =
new_descriptors->CopyFrom(to_index++, this, from_index, witness);
if (copy_result->IsFailure()) return copy_result;
}
for (; low <= limit && GetKey(low)->Hash() == hash; ++low) {
- if (GetKey(low)->Equals(name) && !IsNullDescriptor(low))
- return low;
+ if (GetKey(low)->Equals(name)) return low;
}
return kNotFound;
for (int number = 0; number < len; number++) {
String* entry = GetKey(number);
if (mode == EXPECT_SORTED && entry->Hash() > hash) break;
- if (name->Equals(entry) && !IsNullDescriptor(number)) {
- return number;
- }
+ if (name->Equals(entry)) return number;
}
return kNotFound;
}
}
-void Map::ClearNonLiveTransitions(Heap* heap) {
- DescriptorArray* d = DescriptorArray::cast(
- *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset));
- if (d->IsEmpty()) return;
- Map* elements_transition = d->elements_transition_map();
- if (elements_transition != NULL &&
- ClearBackPointer(heap, elements_transition)) {
- d->ClearElementsTransition();
+// This function should only be called from within the GC, since it uses
+// IncrementLiveBytesFromGC. If called from anywhere else, this results in an
+// inconsistent live-bytes count.
+static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
+ ASSERT(elms->map() != HEAP->fixed_cow_array_map());
+ // For now this trick is only applied to fixed arrays in new and paged space.
+ // In large object space the object's start must coincide with chunk
+ // and thus the trick is just not applicable.
+ ASSERT(!HEAP->lo_space()->Contains(elms));
+
+ const int len = elms->length();
+
+ ASSERT(to_trim < len);
+
+ Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim);
+
+#ifdef DEBUG
+ // If we are doing a big trim in old space then we zap the space.
+ Object** zap = reinterpret_cast<Object**>(new_end);
+ for (int i = 1; i < to_trim; i++) {
+ *zap++ = Smi::FromInt(0);
}
- Smi* NullDescriptorDetails =
- PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
- for (int i = 0; i < d->number_of_descriptors(); ++i) {
- // If the pair (value, details) is a map transition, check if the target is
- // live. If not, null the descriptor. Also drop the back pointer for that
- // map transition, so that this map is not reached again by following a back
- // pointer from that non-live map.
- bool keep_entry = false;
- PropertyDetails details(d->GetDetails(i));
- switch (details.type()) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- keep_entry = !ClearBackPointer(heap, d->GetValue(i));
- break;
- case CALLBACKS: {
- Object* object = d->GetValue(i);
- if (object->IsAccessorPair()) {
- AccessorPair* accessors = AccessorPair::cast(object);
- Object* getter = accessors->getter();
- if (getter->IsMap()) {
- if (ClearBackPointer(heap, getter)) {
- accessors->set_getter(heap->the_hole_value());
- } else {
- keep_entry = true;
- }
- } else if (!getter->IsTheHole()) {
- keep_entry = true;
+#endif
+
+ int size_delta = to_trim * kPointerSize;
+
+ // Technically in new space this write might be omitted (except for
+ // debug mode which iterates through the heap), but to play safer
+ // we still do it.
+ heap->CreateFillerObjectAt(new_end, size_delta);
+
+ elms->set_length(len - to_trim);
+
+ // Maintain marking consistency for IncrementalMarking.
+ if (Marking::IsBlack(Marking::MarkBitFrom(elms))) {
+ MemoryChunk::IncrementLiveBytesFromGC(elms->address(), -size_delta);
+ }
+}
+
+
+// If the descriptor describes a transition to a dead map, the back pointer
+// of this map is cleared and we return true. Otherwise we return false.
+static bool ClearNonLiveTransitionsFromDescriptor(Heap* heap,
+ DescriptorArray* d,
+ int descriptor_index) {
+ // If the pair (value, details) is a map transition, check if the target is
+ // live. If not, null the descriptor. Also drop the back pointer for that
+ // map transition, so that this map is not reached again by following a back
+ // pointer from that non-live map.
+ PropertyDetails details(d->GetDetails(descriptor_index));
+ switch (details.type()) {
+ case MAP_TRANSITION:
+ case CONSTANT_TRANSITION:
+ return ClearBackPointer(heap, d->GetValue(descriptor_index));
+ case CALLBACKS: {
+ Object* object = d->GetValue(descriptor_index);
+ if (object->IsAccessorPair()) {
+ bool cleared = true;
+ AccessorPair* accessors = AccessorPair::cast(object);
+ Object* getter = accessors->getter();
+ if (getter->IsMap()) {
+ if (ClearBackPointer(heap, getter)) {
+ accessors->set_getter(heap->the_hole_value());
+ } else {
+ cleared = false;
}
- Object* setter = accessors->setter();
- if (setter->IsMap()) {
- if (ClearBackPointer(heap, setter)) {
- accessors->set_setter(heap->the_hole_value());
- } else {
- keep_entry = true;
- }
- } else if (!setter->IsTheHole()) {
- keep_entry = true;
+ } else if (!getter->IsTheHole()) {
+ cleared = false;
+ }
+ Object* setter = accessors->setter();
+ if (setter->IsMap()) {
+ if (ClearBackPointer(heap, setter)) {
+ accessors->set_setter(heap->the_hole_value());
+ } else {
+ cleared = false;
}
- } else {
- keep_entry = true;
+ } else if (!setter->IsTheHole()) {
+ cleared = false;
}
- break;
+ return cleared;
}
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- case NULL_DESCRIPTOR:
- keep_entry = true;
- break;
+ return false;
}
- // Make sure that an entry containing only dead transitions gets collected.
- // What we *really* want to do here is removing this entry completely, but
- // for technical reasons we can't do this, so we zero it out instead.
- if (!keep_entry) {
- d->SetDetailsUnchecked(i, NullDescriptorDetails);
- d->SetNullValueUnchecked(i, heap);
+ case NORMAL:
+ case FIELD:
+ case CONSTANT_FUNCTION:
+ case HANDLER:
+ case INTERCEPTOR:
+ return false;
+ case NONEXISTENT:
+ break;
+ }
+ UNREACHABLE();
+ return true;
+}
+
+
+void Map::ClearNonLiveTransitions(Heap* heap) {
+ Object* array = *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset);
+ // If there are no descriptors to be cleared, return.
+ // TODO(verwaest) Should be an assert, otherwise back pointers are not
+ // properly cleared.
+ if (array->IsSmi()) return;
+ DescriptorArray* d = DescriptorArray::cast(array);
+
+ int descriptor_index = 0;
+ // Compact all live descriptors to the left.
+ for (int i = 0; i < d->number_of_descriptors(); ++i) {
+ if (!ClearNonLiveTransitionsFromDescriptor(heap, d, i)) {
+ if (i != descriptor_index) {
+ d->SetKeyUnchecked(heap, descriptor_index, d->GetKey(i));
+ d->SetDetailsUnchecked(descriptor_index, d->GetDetails(i).AsSmi());
+ d->SetValueUnchecked(heap, descriptor_index, d->GetValue(i));
+ }
+ descriptor_index++;
}
}
+
+ Map* elements_transition = d->elements_transition_map();
+ if (elements_transition != NULL &&
+ ClearBackPointer(heap, elements_transition)) {
+ elements_transition = NULL;
+ d->ClearElementsTransition();
+ } else {
+ // If there are no descriptors to be cleared, return.
+ // TODO(verwaest) Should be an assert, otherwise back pointers are not
+ // properly cleared.
+ if (descriptor_index == d->number_of_descriptors()) return;
+ }
+
+ // If the final descriptor array does not contain any live descriptors, remove
+ // the descriptor array from the map.
+ if (descriptor_index == 0 && elements_transition == NULL) {
+ ClearDescriptorArray();
+ return;
+ }
+
+ int trim = d->number_of_descriptors() - descriptor_index;
+ if (trim > 0) {
+ RightTrimFixedArray(heap, d, trim * DescriptorArray::kDescriptorSize);
+ }
}
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
- case NULL_DESCRIPTOR:
return false;
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
}
UNREACHABLE(); // Keep the compiler happy.
return false;