| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index b7ecee776430f70f1f2fd5072d0bb4328b625ab6..a1e37b11e0b3b92226d36e47b921c38f89ea8614 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -7222,7 +7222,9 @@ void String::PrintOn(FILE* file) {
|
| }
|
|
|
|
|
| -void Map::CreateOneBackPointer(Map* target) {
|
| +void Map::CreateOneBackPointer(Object* transition_target) {
|
| + if (!transition_target->IsMap()) return;
|
| + Map* target = Map::cast(transition_target);
|
| #ifdef DEBUG
|
| // Verify target.
|
| Object* source_prototype = prototype();
|
| @@ -7244,86 +7246,131 @@ void Map::CreateOneBackPointer(Map* target) {
|
| void Map::CreateBackPointers() {
|
| DescriptorArray* descriptors = instance_descriptors();
|
| for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
|
| - if (descriptors->IsTransition(i)) {
|
| - Object* object = reinterpret_cast<Object*>(descriptors->GetValue(i));
|
| - if (object->IsMap()) {
|
| - CreateOneBackPointer(reinterpret_cast<Map*>(object));
|
| - } else {
|
| - ASSERT(object->IsFixedArray());
|
| - ASSERT(descriptors->GetType(i) == ELEMENTS_TRANSITION);
|
| - FixedArray* array = reinterpret_cast<FixedArray*>(object);
|
| - for (int i = 0; i < array->length(); ++i) {
|
| - Map* target = reinterpret_cast<Map*>(array->get(i));
|
| - if (!target->IsUndefined()) {
|
| - CreateOneBackPointer(target);
|
| + switch (descriptors->GetType(i)) {
|
| + case MAP_TRANSITION:
|
| + case CONSTANT_TRANSITION:
|
| + CreateOneBackPointer(descriptors->GetValue(i));
|
| + break;
|
| + case ELEMENTS_TRANSITION: {
|
| + Object* object = descriptors->GetValue(i);
|
| + if (object->IsMap()) {
|
| + CreateOneBackPointer(object);
|
| + } else {
|
| + FixedArray* array = FixedArray::cast(object);
|
| + for (int i = 0; i < array->length(); ++i) {
|
| + CreateOneBackPointer(array->get(i));
|
| }
|
| }
|
| + break;
|
| + }
|
| + case CALLBACKS: {
|
| + Object* object = descriptors->GetValue(i);
|
| + if (object->IsAccessorPair()) {
|
| + AccessorPair* accessors = AccessorPair::cast(object);
|
| + CreateOneBackPointer(accessors->getter());
|
| + CreateOneBackPointer(accessors->setter());
|
| + }
|
| + break;
|
| }
|
| + case NORMAL:
|
| + case FIELD:
|
| + case CONSTANT_FUNCTION:
|
| + case HANDLER:
|
| + case INTERCEPTOR:
|
| + case NULL_DESCRIPTOR:
|
| + break;
|
| }
|
| }
|
| }
|
|
|
|
|
| +bool Map::RestoreOneBackPointer(Object* object,
|
| + Object* real_prototype,
|
| + bool* keep_entry) {
|
| + if (!object->IsMap()) return false;
|
| + Map* map = Map::cast(object);
|
| + if (Marking::MarkBitFrom(map).Get()) {
|
| + *keep_entry = true;
|
| + return false;
|
| + }
|
| + ASSERT(map->prototype() == this || map->prototype() == real_prototype);
|
| + // Getter prototype() is read-only, set_prototype() has side effects.
|
| + *RawField(map, Map::kPrototypeOffset) = real_prototype;
|
| + return true;
|
| +}
|
| +
|
| +
|
| void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) {
|
| - // Live DescriptorArray objects will be marked, so we must use
|
| - // low-level accessors to get and modify their data.
|
| - DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
|
| + DescriptorArray* d = DescriptorArray::cast(
|
| *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset));
|
| if (d->IsEmpty()) return;
|
| Smi* NullDescriptorDetails =
|
| PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
|
| - FixedArray* contents = reinterpret_cast<FixedArray*>(
|
| + FixedArray* contents = FixedArray::cast(
|
| d->get(DescriptorArray::kContentArrayIndex));
|
| ASSERT(contents->length() >= 2);
|
| for (int i = 0; i < contents->length(); i += 2) {
|
| - // 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 a
|
| - // non-live object.
|
| + // 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 a non-live object.
|
| + bool keep_entry = false;
|
| PropertyDetails details(Smi::cast(contents->get(i + 1)));
|
| - if (IsTransitionType(details.type())) {
|
| - Object* object = reinterpret_cast<Object*>(contents->get(i));
|
| - if (object->IsMap()) {
|
| - Map* target = reinterpret_cast<Map*>(object);
|
| - ASSERT(target->IsHeapObject());
|
| - MarkBit map_mark = Marking::MarkBitFrom(target);
|
| - if (!map_mark.Get()) {
|
| - ASSERT(target->IsMap());
|
| - contents->set_unchecked(i + 1, NullDescriptorDetails);
|
| - contents->set_null_unchecked(heap, i);
|
| - ASSERT(target->prototype() == this ||
|
| - target->prototype() == real_prototype);
|
| - // Getter prototype() is read-only, set_prototype() has side effects.
|
| - *RawField(target, Map::kPrototypeOffset) = real_prototype;
|
| - }
|
| - } else {
|
| - ASSERT(object->IsFixedArray());
|
| - ASSERT(details.type() == ELEMENTS_TRANSITION);
|
| - FixedArray* array = reinterpret_cast<FixedArray*>(object);
|
| - bool reachable_map_found = false;
|
| - for (int j = 0; j < array->length(); ++j) {
|
| - Map* target = reinterpret_cast<Map*>(array->get(j));
|
| - ASSERT(target->IsHeapObject());
|
| - MarkBit map_mark = Marking::MarkBitFrom(target);
|
| - if (!map_mark.Get()) {
|
| - ASSERT(target->IsMap());
|
| - array->set_undefined(j);
|
| - ASSERT(target->prototype() == this ||
|
| - target->prototype() == real_prototype);
|
| - // Getter prototype() is read-only, set_prototype() has side
|
| - // effects.
|
| - *RawField(target, Map::kPrototypeOffset) = real_prototype;
|
| - } else if (target->IsMap()) {
|
| - reachable_map_found = true;
|
| + switch (details.type()) {
|
| + case MAP_TRANSITION:
|
| + case CONSTANT_TRANSITION:
|
| + RestoreOneBackPointer(contents->get(i), real_prototype, &keep_entry);
|
| + break;
|
| + case ELEMENTS_TRANSITION: {
|
| + Object* object = contents->get(i);
|
| + if (object->IsMap()) {
|
| + RestoreOneBackPointer(object, real_prototype, &keep_entry);
|
| + } else {
|
| + FixedArray* array = FixedArray::cast(object);
|
| + for (int j = 0; j < array->length(); ++j) {
|
| + if (RestoreOneBackPointer(array->get(j),
|
| + real_prototype,
|
| + &keep_entry)) {
|
| + array->set_undefined(j);
|
| + }
|
| }
|
| }
|
| - // If no map was found, make sure the FixedArray also gets collected.
|
| - if (!reachable_map_found) {
|
| - contents->set_unchecked(i + 1, NullDescriptorDetails);
|
| - contents->set_null_unchecked(heap, i);
|
| + break;
|
| + }
|
| + case CALLBACKS: {
|
| + Object* object = contents->get(i);
|
| + if (object->IsAccessorPair()) {
|
| + AccessorPair* accessors = AccessorPair::cast(object);
|
| + if (RestoreOneBackPointer(accessors->getter(),
|
| + real_prototype,
|
| + &keep_entry)) {
|
| + accessors->set_getter(heap->the_hole_value());
|
| + }
|
| + if (RestoreOneBackPointer(accessors->setter(),
|
| + real_prototype,
|
| + &keep_entry)) {
|
| + accessors->set_setter(heap->the_hole_value());
|
| + }
|
| + } else {
|
| + keep_entry = true;
|
| }
|
| + break;
|
| }
|
| + case NORMAL:
|
| + case FIELD:
|
| + case CONSTANT_FUNCTION:
|
| + case HANDLER:
|
| + case INTERCEPTOR:
|
| + case NULL_DESCRIPTOR:
|
| + keep_entry = true;
|
| + break;
|
| + }
|
| + // 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) {
|
| + contents->set_unchecked(i + 1, NullDescriptorDetails);
|
| + contents->set_null_unchecked(heap, i);
|
| }
|
| }
|
| }
|
|
|