Add a test to verify correctness of remembered set update for Heap::CopyJSObject.
authorantonm@chromium.org <antonm@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 25 Mar 2010 15:32:58 +0000 (15:32 +0000)
committerantonm@chromium.org <antonm@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 25 Mar 2010 15:32:58 +0000 (15:32 +0000)
Review URL: http://codereview.chromium.org/1256002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4286 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/heap.cc
src/heap.h
test/cctest/test-heap.cc

index a9754ce057837968f482b1920e1e8fc416f972e0..1f1599a79bf6c8f0f0a8482a082e1c32807e6fb2 100644 (file)
@@ -2663,7 +2663,7 @@ Object* Heap::CopyJSObject(JSObject* source) {
   FixedArray* elements = FixedArray::cast(source->elements());
   FixedArray* properties = FixedArray::cast(source->properties());
   // Update elements if necessary.
-  if (elements->length()> 0) {
+  if (elements->length() > 0) {
     Object* elem = CopyFixedArray(elements);
     if (elem->IsFailure()) return elem;
     JSObject::cast(clone)->set_elements(FixedArray::cast(elem));
index d37641ccf3e72d3142838ebf31f018afa09c1946..2a0de236ee7105d63ea4286b46832b7d024e1d4d 100644 (file)
@@ -938,6 +938,8 @@ class Heap : public AllStatic {
 
   static void RecordStats(HeapStats* stats);
 
+  static int MaxObjectSizeInNewSpace() { return kMaxObjectSizeInNewSpace; }
+
  private:
   static int reserved_semispace_size_;
   static int max_semispace_size_;
index 69998891bec2a7c0f4de94d637ca4dcdab78198f..bfadd208b6b8bde14e096359fce5914a9ea56f13 100644 (file)
@@ -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));
+  }
+}