Home | History | Annotate | Download | only in cctest
      1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include <stdlib.h>
     29 
     30 #include "v8.h"
     31 
     32 #include "global-handles.h"
     33 #include "snapshot.h"
     34 #include "cctest.h"
     35 
     36 using namespace v8::internal;
     37 
     38 static v8::Persistent<v8::Context> env;
     39 
     40 static void InitializeVM() {
     41   if (env.IsEmpty()) env = v8::Context::New();
     42   v8::HandleScope scope;
     43   env->Enter();
     44 }
     45 
     46 
     47 TEST(MarkingStack) {
     48   int mem_size = 20 * kPointerSize;
     49   byte* mem = NewArray<byte>(20*kPointerSize);
     50   Address low = reinterpret_cast<Address>(mem);
     51   Address high = low + mem_size;
     52   MarkingStack s;
     53   s.Initialize(low, high);
     54 
     55   Address address = NULL;
     56   while (!s.is_full()) {
     57     s.Push(HeapObject::FromAddress(address));
     58     address += kPointerSize;
     59   }
     60 
     61   while (!s.is_empty()) {
     62     Address value = s.Pop()->address();
     63     address -= kPointerSize;
     64     CHECK_EQ(address, value);
     65   }
     66 
     67   CHECK_EQ(NULL, address);
     68   DeleteArray(mem);
     69 }
     70 
     71 
     72 TEST(Promotion) {
     73   // This test requires compaction. If compaction is turned off, we
     74   // skip the entire test.
     75   if (FLAG_never_compact) return;
     76 
     77   // Ensure that we get a compacting collection so that objects are promoted
     78   // from new space.
     79   FLAG_gc_global = true;
     80   FLAG_always_compact = true;
     81   HEAP->ConfigureHeap(2*256*KB, 4*MB, 4*MB);
     82 
     83   InitializeVM();
     84 
     85   v8::HandleScope sc;
     86 
     87   // Allocate a fixed array in the new space.
     88   int array_size =
     89       (HEAP->MaxObjectSizeInPagedSpace() - FixedArray::kHeaderSize) /
     90       (kPointerSize * 4);
     91   Object* obj = HEAP->AllocateFixedArray(array_size)->ToObjectChecked();
     92 
     93   Handle<FixedArray> array(FixedArray::cast(obj));
     94 
     95   // Array should be in the new space.
     96   CHECK(HEAP->InSpace(*array, NEW_SPACE));
     97 
     98   // Call the m-c collector, so array becomes an old object.
     99   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    100 
    101   // Array now sits in the old space
    102   CHECK(HEAP->InSpace(*array, OLD_POINTER_SPACE));
    103 }
    104 
    105 
    106 TEST(NoPromotion) {
    107   HEAP->ConfigureHeap(2*256*KB, 4*MB, 4*MB);
    108 
    109   // Test the situation that some objects in new space are promoted to
    110   // the old space
    111   InitializeVM();
    112 
    113   v8::HandleScope sc;
    114 
    115   // Do a mark compact GC to shrink the heap.
    116   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    117 
    118   // Allocate a big Fixed array in the new space.
    119   int size = (HEAP->MaxObjectSizeInPagedSpace() - FixedArray::kHeaderSize) /
    120       kPointerSize;
    121   Object* obj = HEAP->AllocateFixedArray(size)->ToObjectChecked();
    122 
    123   Handle<FixedArray> array(FixedArray::cast(obj));
    124 
    125   // Array still stays in the new space.
    126   CHECK(HEAP->InSpace(*array, NEW_SPACE));
    127 
    128   // Allocate objects in the old space until out of memory.
    129   FixedArray* host = *array;
    130   while (true) {
    131     Object* obj;
    132     { MaybeObject* maybe_obj = HEAP->AllocateFixedArray(100, TENURED);
    133       if (!maybe_obj->ToObject(&obj)) break;
    134     }
    135 
    136     host->set(0, obj);
    137     host = FixedArray::cast(obj);
    138   }
    139 
    140   // Call mark compact GC, and it should pass.
    141   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    142 
    143   // array should not be promoted because the old space is full.
    144   CHECK(HEAP->InSpace(*array, NEW_SPACE));
    145 }
    146 
    147 
    148 TEST(MarkCompactCollector) {
    149   InitializeVM();
    150 
    151   v8::HandleScope sc;
    152   // call mark-compact when heap is empty
    153   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    154 
    155   // keep allocating garbage in new space until it fails
    156   const int ARRAY_SIZE = 100;
    157   Object* array;
    158   MaybeObject* maybe_array;
    159   do {
    160     maybe_array = HEAP->AllocateFixedArray(ARRAY_SIZE);
    161   } while (maybe_array->ToObject(&array));
    162   HEAP->CollectGarbage(NEW_SPACE);
    163 
    164   array = HEAP->AllocateFixedArray(ARRAY_SIZE)->ToObjectChecked();
    165 
    166   // keep allocating maps until it fails
    167   Object* mapp;
    168   MaybeObject* maybe_mapp;
    169   do {
    170     maybe_mapp = HEAP->AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    171   } while (maybe_mapp->ToObject(&mapp));
    172   HEAP->CollectGarbage(MAP_SPACE);
    173   mapp = HEAP->AllocateMap(JS_OBJECT_TYPE,
    174                            JSObject::kHeaderSize)->ToObjectChecked();
    175 
    176   // allocate a garbage
    177   String* func_name =
    178       String::cast(HEAP->LookupAsciiSymbol("theFunction")->ToObjectChecked());
    179   SharedFunctionInfo* function_share = SharedFunctionInfo::cast(
    180       HEAP->AllocateSharedFunctionInfo(func_name)->ToObjectChecked());
    181   JSFunction* function = JSFunction::cast(
    182       HEAP->AllocateFunction(*Isolate::Current()->function_map(),
    183                              function_share,
    184                              HEAP->undefined_value())->ToObjectChecked());
    185   Map* initial_map =
    186       Map::cast(HEAP->AllocateMap(JS_OBJECT_TYPE,
    187                                   JSObject::kHeaderSize)->ToObjectChecked());
    188   function->set_initial_map(initial_map);
    189   Isolate::Current()->context()->global()->SetProperty(
    190       func_name, function, NONE, kNonStrictMode)->ToObjectChecked();
    191 
    192   JSObject* obj = JSObject::cast(
    193       HEAP->AllocateJSObject(function)->ToObjectChecked());
    194   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    195 
    196   func_name =
    197       String::cast(HEAP->LookupAsciiSymbol("theFunction")->ToObjectChecked());
    198   CHECK(Isolate::Current()->context()->global()->HasLocalProperty(func_name));
    199   Object* func_value = Isolate::Current()->context()->global()->
    200       GetProperty(func_name)->ToObjectChecked();
    201   CHECK(func_value->IsJSFunction());
    202   function = JSFunction::cast(func_value);
    203 
    204   obj = JSObject::cast(HEAP->AllocateJSObject(function)->ToObjectChecked());
    205   String* obj_name =
    206       String::cast(HEAP->LookupAsciiSymbol("theObject")->ToObjectChecked());
    207   Isolate::Current()->context()->global()->SetProperty(
    208       obj_name, obj, NONE, kNonStrictMode)->ToObjectChecked();
    209   String* prop_name =
    210       String::cast(HEAP->LookupAsciiSymbol("theSlot")->ToObjectChecked());
    211   obj->SetProperty(prop_name,
    212                    Smi::FromInt(23),
    213                    NONE,
    214                    kNonStrictMode)->ToObjectChecked();
    215 
    216   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    217 
    218   obj_name =
    219       String::cast(HEAP->LookupAsciiSymbol("theObject")->ToObjectChecked());
    220   CHECK(Isolate::Current()->context()->global()->HasLocalProperty(obj_name));
    221   CHECK(Isolate::Current()->context()->global()->
    222         GetProperty(obj_name)->ToObjectChecked()->IsJSObject());
    223   obj = JSObject::cast(Isolate::Current()->context()->global()->
    224                        GetProperty(obj_name)->ToObjectChecked());
    225   prop_name =
    226       String::cast(HEAP->LookupAsciiSymbol("theSlot")->ToObjectChecked());
    227   CHECK(obj->GetProperty(prop_name) == Smi::FromInt(23));
    228 }
    229 
    230 
    231 static Handle<Map> CreateMap() {
    232   return FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    233 }
    234 
    235 
    236 TEST(MapCompact) {
    237   FLAG_max_map_space_pages = 16;
    238   InitializeVM();
    239 
    240   {
    241     v8::HandleScope sc;
    242     // keep allocating maps while pointers are still encodable and thus
    243     // mark compact is permitted.
    244     Handle<JSObject> root = FACTORY->NewJSObjectFromMap(CreateMap());
    245     do {
    246       Handle<Map> map = CreateMap();
    247       map->set_prototype(*root);
    248       root = FACTORY->NewJSObjectFromMap(map);
    249     } while (HEAP->map_space()->MapPointersEncodable());
    250   }
    251   // Now, as we don't have any handles to just allocated maps, we should
    252   // be able to trigger map compaction.
    253   // To give an additional chance to fail, try to force compaction which
    254   // should be impossible right now.
    255   HEAP->CollectAllGarbage(true);
    256   // And now map pointers should be encodable again.
    257   CHECK(HEAP->map_space()->MapPointersEncodable());
    258 }
    259 
    260 
    261 static int gc_starts = 0;
    262 static int gc_ends = 0;
    263 
    264 static void GCPrologueCallbackFunc() {
    265   CHECK(gc_starts == gc_ends);
    266   gc_starts++;
    267 }
    268 
    269 
    270 static void GCEpilogueCallbackFunc() {
    271   CHECK(gc_starts == gc_ends + 1);
    272   gc_ends++;
    273 }
    274 
    275 
    276 TEST(GCCallback) {
    277   InitializeVM();
    278 
    279   HEAP->SetGlobalGCPrologueCallback(&GCPrologueCallbackFunc);
    280   HEAP->SetGlobalGCEpilogueCallback(&GCEpilogueCallbackFunc);
    281 
    282   // Scavenge does not call GC callback functions.
    283   HEAP->PerformScavenge();
    284 
    285   CHECK_EQ(0, gc_starts);
    286   CHECK_EQ(gc_ends, gc_starts);
    287 
    288   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    289   CHECK_EQ(1, gc_starts);
    290   CHECK_EQ(gc_ends, gc_starts);
    291 }
    292 
    293 
    294 static int NumberOfWeakCalls = 0;
    295 static void WeakPointerCallback(v8::Persistent<v8::Value> handle, void* id) {
    296   ASSERT(id == reinterpret_cast<void*>(1234));
    297   NumberOfWeakCalls++;
    298   handle.Dispose();
    299 }
    300 
    301 TEST(ObjectGroups) {
    302   InitializeVM();
    303   GlobalHandles* global_handles = Isolate::Current()->global_handles();
    304 
    305   NumberOfWeakCalls = 0;
    306   v8::HandleScope handle_scope;
    307 
    308   Handle<Object> g1s1 =
    309       global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
    310   Handle<Object> g1s2 =
    311       global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
    312   Handle<Object> g1c1 =
    313       global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
    314   global_handles->MakeWeak(g1s1.location(),
    315                            reinterpret_cast<void*>(1234),
    316                            &WeakPointerCallback);
    317   global_handles->MakeWeak(g1s2.location(),
    318                            reinterpret_cast<void*>(1234),
    319                            &WeakPointerCallback);
    320   global_handles->MakeWeak(g1c1.location(),
    321                            reinterpret_cast<void*>(1234),
    322                            &WeakPointerCallback);
    323 
    324   Handle<Object> g2s1 =
    325       global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
    326   Handle<Object> g2s2 =
    327     global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
    328   Handle<Object> g2c1 =
    329     global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
    330   global_handles->MakeWeak(g2s1.location(),
    331                            reinterpret_cast<void*>(1234),
    332                            &WeakPointerCallback);
    333   global_handles->MakeWeak(g2s2.location(),
    334                            reinterpret_cast<void*>(1234),
    335                            &WeakPointerCallback);
    336   global_handles->MakeWeak(g2c1.location(),
    337                            reinterpret_cast<void*>(1234),
    338                            &WeakPointerCallback);
    339 
    340   Handle<Object> root = global_handles->Create(*g1s1);  // make a root.
    341 
    342   // Connect group 1 and 2, make a cycle.
    343   Handle<FixedArray>::cast(g1s2)->set(0, *g2s2);
    344   Handle<FixedArray>::cast(g2s1)->set(0, *g1s1);
    345 
    346   {
    347     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
    348     Object** g1_children[] = { g1c1.location() };
    349     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
    350     Object** g2_children[] = { g2c1.location() };
    351     global_handles->AddObjectGroup(g1_objects, 2, NULL);
    352     global_handles->AddImplicitReferences(
    353         Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
    354     global_handles->AddObjectGroup(g2_objects, 2, NULL);
    355     global_handles->AddImplicitReferences(
    356         Handle<HeapObject>::cast(g2s2).location(), g2_children, 1);
    357   }
    358   // Do a full GC
    359   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    360 
    361   // All object should be alive.
    362   CHECK_EQ(0, NumberOfWeakCalls);
    363 
    364   // Weaken the root.
    365   global_handles->MakeWeak(root.location(),
    366                            reinterpret_cast<void*>(1234),
    367                            &WeakPointerCallback);
    368   // But make children strong roots---all the objects (except for children)
    369   // should be collectable now.
    370   global_handles->ClearWeakness(g1c1.location());
    371   global_handles->ClearWeakness(g2c1.location());
    372 
    373   // Groups are deleted, rebuild groups.
    374   {
    375     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
    376     Object** g1_children[] = { g1c1.location() };
    377     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
    378     Object** g2_children[] = { g2c1.location() };
    379     global_handles->AddObjectGroup(g1_objects, 2, NULL);
    380     global_handles->AddImplicitReferences(
    381         Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
    382     global_handles->AddObjectGroup(g2_objects, 2, NULL);
    383     global_handles->AddImplicitReferences(
    384         Handle<HeapObject>::cast(g2s2).location(), g2_children, 1);
    385   }
    386 
    387   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    388 
    389   // All objects should be gone. 5 global handles in total.
    390   CHECK_EQ(5, NumberOfWeakCalls);
    391 
    392   // And now make children weak again and collect them.
    393   global_handles->MakeWeak(g1c1.location(),
    394                            reinterpret_cast<void*>(1234),
    395                            &WeakPointerCallback);
    396   global_handles->MakeWeak(g2c1.location(),
    397                            reinterpret_cast<void*>(1234),
    398                            &WeakPointerCallback);
    399 
    400   HEAP->CollectGarbage(OLD_POINTER_SPACE);
    401   CHECK_EQ(7, NumberOfWeakCalls);
    402 }
    403 
    404 
    405 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
    406  public:
    407   TestRetainedObjectInfo() : has_been_disposed_(false) {}
    408 
    409   bool has_been_disposed() { return has_been_disposed_; }
    410 
    411   virtual void Dispose() {
    412     ASSERT(!has_been_disposed_);
    413     has_been_disposed_ = true;
    414   }
    415 
    416   virtual bool IsEquivalent(v8::RetainedObjectInfo* other) {
    417     return other == this;
    418   }
    419 
    420   virtual intptr_t GetHash() { return 0; }
    421 
    422   virtual const char* GetLabel() { return "whatever"; }
    423 
    424  private:
    425   bool has_been_disposed_;
    426 };
    427 
    428 
    429 TEST(EmptyObjectGroups) {
    430   InitializeVM();
    431   GlobalHandles* global_handles = Isolate::Current()->global_handles();
    432 
    433   v8::HandleScope handle_scope;
    434 
    435   Handle<Object> object =
    436       global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
    437 
    438   TestRetainedObjectInfo info;
    439   global_handles->AddObjectGroup(NULL, 0, &info);
    440   ASSERT(info.has_been_disposed());
    441 
    442   global_handles->AddImplicitReferences(
    443         Handle<HeapObject>::cast(object).location(), NULL, 0);
    444 }
    445