+TEST(PromotionQueue) {
+ i::FLAG_expose_gc = true;
+ i::FLAG_max_semi_space_size = 2;
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+ NewSpace* new_space = heap->new_space();
+
+ // In this test we will try to overwrite the promotion queue which is at the
+ // end of to-space. To actually make that possible, we need at least two
+ // semi-space pages and take advantage of fragementation.
+ // (1) Grow semi-space to two pages.
+ // (2) Create a few small long living objects and call the scavenger to
+ // move them to the other semi-space.
+ // (3) Create a huge object, i.e., remainder of first semi-space page and
+ // create another huge object which should be of maximum allocatable memory
+ // size of the second semi-space page.
+ // (4) Call the scavenger again.
+ // What will happen is: the scavenger will promote the objects created in (2)
+ // and will create promotion queue entries at the end of the second
+ // semi-space page during the next scavenge when it promotes the objects to
+ // the old generation. The first allocation of (3) will fill up the first
+ // semi-space page. The second allocation in (3) will not fit into the first
+ // semi-space page, but it will overwrite the promotion queue which are in
+ // the second semi-space page. If the right guards are in place, the promotion
+ // queue will be evacuated in that case.
+
+ // Grow the semi-space to two pages to make semi-space copy overwrite the
+ // promotion queue, which will be at the end of the second page.
+ intptr_t old_capacity = new_space->Capacity();
+ new_space->Grow();
+ CHECK(new_space->IsAtMaximumCapacity());
+ CHECK(2 * old_capacity == new_space->Capacity());
+
+ // Call the scavenger two times to get an empty new space
+ heap->CollectGarbage(NEW_SPACE);
+ heap->CollectGarbage(NEW_SPACE);
+
+ // First create a few objects which will survive a scavenge, and will get
+ // promoted to the old generation later on. These objects will create
+ // promotion queue entries at the end of the second semi-space page.
+ const int number_handles = 12;
+ Handle<FixedArray> handles[number_handles];
+ for (int i = 0; i < number_handles; i++) {
+ handles[i] = isolate->factory()->NewFixedArray(1, NOT_TENURED);
+ }
+ heap->CollectGarbage(NEW_SPACE);
+
+ // Create the first huge object which will exactly fit the first semi-space
+ // page.
+ int new_linear_size = static_cast<int>(
+ *heap->new_space()->allocation_limit_address() -
+ *heap->new_space()->allocation_top_address());
+ int length = new_linear_size / kPointerSize - FixedArray::kHeaderSize;
+ Handle<FixedArray> first =
+ isolate->factory()->NewFixedArray(length, NOT_TENURED);
+ CHECK(heap->InNewSpace(*first));
+
+ // Create the second huge object of maximum allocatable second semi-space
+ // page size.
+ new_linear_size = static_cast<int>(
+ *heap->new_space()->allocation_limit_address() -
+ *heap->new_space()->allocation_top_address());
+ length = Page::kMaxRegularHeapObjectSize / kPointerSize -
+ FixedArray::kHeaderSize;
+ Handle<FixedArray> second =
+ isolate->factory()->NewFixedArray(length, NOT_TENURED);
+ CHECK(heap->InNewSpace(*second));
+
+ // This scavenge will corrupt memory if the promotion queue is not evacuated.
+ heap->CollectGarbage(NEW_SPACE);
+}
+
+
+TEST(Regress388880) {
+ i::FLAG_expose_gc = true;
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+ Heap* heap = isolate->heap();
+
+ Handle<Map> map1 = Map::Create(isolate->object_function(), 1);
+ Handle<Map> map2 =
+ Map::CopyWithField(map1, factory->NewStringFromStaticAscii("foo"),
+ HeapType::Any(isolate), NONE, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+
+ int desired_offset = Page::kPageSize - map1->instance_size();
+
+ // Allocate fixed array in old pointer space so, that object allocated
+ // afterwards would end at the end of the page.
+ {
+ SimulateFullSpace(heap->old_pointer_space());
+ int padding_size = desired_offset - Page::kObjectStartOffset;
+ int padding_array_length =
+ (padding_size - FixedArray::kHeaderSize) / kPointerSize;
+
+ Handle<FixedArray> temp2 =
+ factory->NewFixedArray(padding_array_length, TENURED);
+ Page* page = Page::FromAddress(temp2->address());
+ CHECK_EQ(Page::kObjectStartOffset, page->Offset(temp2->address()));
+ }
+
+ Handle<JSObject> o = factory->NewJSObjectFromMap(map1, TENURED, false);
+ o->set_properties(*factory->empty_fixed_array());
+
+ // Ensure that the object allocated where we need it.
+ Page* page = Page::FromAddress(o->address());
+ CHECK_EQ(desired_offset, page->Offset(o->address()));
+
+ // Now we have an object right at the end of the page.
+
+ // Enable incremental marking to trigger actions in Heap::AdjustLiveBytes()
+ // that would cause crash.
+ IncrementalMarking* marking = CcTest::heap()->incremental_marking();
+ marking->Abort();
+ marking->Start();
+ CHECK(marking->IsMarking());
+
+ // Now everything is set up for crashing in JSObject::MigrateFastToFast()
+ // when it calls heap->AdjustLiveBytes(...).
+ JSObject::MigrateToMap(o, map2);
+}
+
+
+TEST(RegressStoreBufferMapUpdate) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+ Heap* heap = isolate->heap();
+
+ // This test checks that we do not treat instance size field of the map
+ // as a heap pointer when processing the store buffer.
+
+ Handle<Map> map1 = Map::Create(isolate->object_function(), 1);
+
+ // Allocate a throw-away object.
+ factory->NewFixedArray(1, NOT_TENURED);
+
+ // Allocate a new-space object that will be moved by the GC (because
+ // the throw-away object will die).
+ Handle<FixedArray> object_to_move = factory->NewFixedArray(1, NOT_TENURED);
+
+ // Record the address before the GC.
+ Object* object_to_move_address = *object_to_move;
+
+ // Smash the new space pointer to the moving object into the instance size
+ // field of the map. The idea is to trick the GC into updating this pointer
+ // when the object moves. This would be wrong because instance size should
+ // not be treated as a heap pointer.
+ *(reinterpret_cast<Object**>(map1->address() + Map::kInstanceSizeOffset)) =
+ object_to_move_address;
+
+ // Make sure we scan the map's page on scavenge.
+ Page* page = Page::FromAddress(map1->address());
+ page->set_scan_on_scavenge(true);
+
+ heap->CollectGarbage(NEW_SPACE);
+
+ // Check the object has really moved.
+ CHECK(*object_to_move != object_to_move_address);
+
+ // Now check that we have not updated the instance size field of the map.
+ CHECK_EQ(object_to_move_address,
+ *(reinterpret_cast<Object**>(map1->address() +
+ Map::kInstanceSizeOffset)));
+}
+
+