Index: test/cctest/test-heap.cc |
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc |
index 69998891bec2a7c0f4de94d637ca4dcdab78198f..bfadd208b6b8bde14e096359fce5914a9ea56f13 100644 |
--- a/test/cctest/test-heap.cc |
+++ b/test/cctest/test-heap.cc |
@@ -868,3 +868,99 @@ TEST(EmptyHandleEscapeFrom) { |
CHECK(runaway.is_null()); |
} |
+ |
+ |
+static int LenFromSize(int size) { |
+ return (size - FixedArray::kHeaderSize) / kPointerSize; |
+} |
+ |
+ |
+TEST(Regression39128) { |
+ // Test case for crbug.com/39128. |
+ InitializeVM(); |
+ |
+ // Increase the chance of 'bump-the-pointer' allocation in old space. |
+ bool force_compaction = true; |
+ Heap::CollectAllGarbage(force_compaction); |
+ |
+ v8::HandleScope scope; |
+ |
+ // The plan: create JSObject which references objects in new space. |
+ // Then clone this object (forcing it to go into old space) and check |
+ // that only bits pertaining to the object are updated in remembered set. |
+ |
+ // Step 1: prepare a map for the object. We add 1 inobject property to it. |
+ Handle<JSFunction> object_ctor(Top::global_context()->object_function()); |
+ CHECK(object_ctor->has_initial_map()); |
+ Handle<Map> object_map(object_ctor->initial_map()); |
+ // Create a map with single inobject property. |
+ Handle<Map> my_map = Factory::CopyMap(object_map, 1); |
+ int n_properties = my_map->inobject_properties(); |
+ CHECK_GT(n_properties, 0); |
+ |
+ int object_size = my_map->instance_size(); |
+ |
+ // Step 2: allocate a lot of objects so to almost fill new space: we need |
+ // just enough room to allocate JSObject and thus fill the newspace. |
+ |
+ int allocation_amount = Min(FixedArray::kMaxSize, |
+ Heap::MaxObjectSizeInNewSpace()); |
+ int allocation_len = LenFromSize(allocation_amount); |
+ NewSpace* new_space = Heap::new_space(); |
+ Address* top_addr = new_space->allocation_top_address(); |
+ Address* limit_addr = new_space->allocation_limit_address(); |
+ while ((*limit_addr - *top_addr) > allocation_amount) { |
+ CHECK(!Heap::always_allocate()); |
+ Object* array = Heap::AllocateFixedArray(allocation_len); |
+ CHECK(!array->IsFailure()); |
+ CHECK(new_space->Contains(array)); |
+ } |
+ |
+ // Step 3: now allocate fixed array and JSObject to fill the whole new space. |
+ int to_fill = *limit_addr - *top_addr - object_size; |
+ int fixed_array_len = LenFromSize(to_fill); |
+ CHECK(fixed_array_len < FixedArray::kMaxLength); |
+ |
+ CHECK(!Heap::always_allocate()); |
+ Object* array = Heap::AllocateFixedArray(fixed_array_len); |
+ CHECK(!array->IsFailure()); |
+ CHECK(new_space->Contains(array)); |
+ |
+ Object* object = Heap::AllocateJSObjectFromMap(*my_map); |
+ CHECK(!object->IsFailure()); |
+ CHECK(new_space->Contains(object)); |
+ JSObject* jsobject = JSObject::cast(object); |
+ CHECK_EQ(0, jsobject->elements()->length()); |
+ CHECK_EQ(0, jsobject->properties()->length()); |
+ // Create a reference to object in new space in jsobject. |
+ jsobject->FastPropertyAtPut(-1, array); |
+ |
+ CHECK_EQ(0L, (*limit_addr - *top_addr)); |
+ |
+ // Step 4: clone jsobject, but force always allocate first to create a clone |
+ // in old pointer space. |
+ Address old_pointer_space_top = Heap::old_pointer_space()->top(); |
+ AlwaysAllocateScope aa_scope; |
+ Object* clone_obj = Heap::CopyJSObject(jsobject); |
+ CHECK(!object->IsFailure()); |
+ JSObject* clone = JSObject::cast(clone_obj); |
+ if (clone->address() != old_pointer_space_top) { |
+ // Alas, got allocated from free list, we cannot do checks. |
+ return; |
+ } |
+ CHECK(Heap::old_pointer_space()->Contains(clone->address())); |
+ |
+ // Step 5: verify validity of remembered set. |
+ Address clone_addr = clone->address(); |
+ Page* page = Page::FromAddress(clone_addr); |
+ // Check that remembered set tracks a reference from inobject property 1. |
+ CHECK(page->IsRSetSet(clone_addr, object_size - kPointerSize)); |
+ // Probe several addresses after the object. |
+ for (int i = 0; i < 7; i++) { |
+ int offset = object_size + i * kPointerSize; |
+ if (clone_addr + offset >= page->ObjectAreaEnd()) { |
+ break; |
+ } |
+ CHECK(!page->IsRSetSet(clone_addr, offset)); |
+ } |
+} |