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 "top.h"
     35 #include "cctest.h"
     36 
     37 using namespace v8::internal;
     38 
     39 static v8::Persistent<v8::Context> env;
     40 
     41 static void InitializeVM() {
     42   if (env.IsEmpty()) env = v8::Context::New();
     43   v8::HandleScope scope;
     44   env->Enter();
     45 }
     46 
     47 
     48 TEST(MarkingStack) {
     49   int mem_size = 20 * kPointerSize;
     50   byte* mem = NewArray<byte>(20*kPointerSize);
     51   Address low = reinterpret_cast<Address>(mem);
     52   Address high = low + mem_size;
     53   MarkingStack s;
     54   s.Initialize(low, high);
     55 
     56   Address address = NULL;
     57   while (!s.is_full()) {
     58     s.Push(HeapObject::FromAddress(address));
     59     address += kPointerSize;
     60   }
     61 
     62   while (!s.is_empty()) {
     63     Address value = s.Pop()->address();
     64     address -= kPointerSize;
     65     CHECK_EQ(address, value);
     66   }
     67 
     68   CHECK_EQ(NULL, address);
     69   DeleteArray(mem);
     70 }
     71 
     72 
     73 TEST(Promotion) {
     74   // Ensure that we get a compacting collection so that objects are promoted
     75   // from new space.
     76   FLAG_gc_global = true;
     77   FLAG_always_compact = true;
     78   Heap::ConfigureHeap(2*256*KB, 4*MB);
     79 
     80   InitializeVM();
     81 
     82   v8::HandleScope sc;
     83 
     84   // Allocate a fixed array in the new space.
     85   int array_size =
     86       (Heap::MaxObjectSizeInPagedSpace() - FixedArray::kHeaderSize) /
     87       (kPointerSize * 4);
     88   Object* obj = Heap::AllocateFixedArray(array_size);
     89   CHECK(!obj->IsFailure());
     90 
     91   Handle<FixedArray> array(FixedArray::cast(obj));
     92 
     93   // Array should be in the new space.
     94   CHECK(Heap::InSpace(*array, NEW_SPACE));
     95 
     96   // Call the m-c collector, so array becomes an old object.
     97   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
     98 
     99   // Array now sits in the old space
    100   CHECK(Heap::InSpace(*array, OLD_POINTER_SPACE));
    101 }
    102 
    103 
    104 TEST(NoPromotion) {
    105   Heap::ConfigureHeap(2*256*KB, 4*MB);
    106 
    107   // Test the situation that some objects in new space are promoted to
    108   // the old space
    109   InitializeVM();
    110 
    111   v8::HandleScope sc;
    112 
    113   // Do a mark compact GC to shrink the heap.
    114   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
    115 
    116   // Allocate a big Fixed array in the new space.
    117   int size = (Heap::MaxObjectSizeInPagedSpace() - FixedArray::kHeaderSize) /
    118       kPointerSize;
    119   Object* obj = Heap::AllocateFixedArray(size);
    120 
    121   Handle<FixedArray> array(FixedArray::cast(obj));
    122 
    123   // Array still stays in the new space.
    124   CHECK(Heap::InSpace(*array, NEW_SPACE));
    125 
    126   // Allocate objects in the old space until out of memory.
    127   FixedArray* host = *array;
    128   while (true) {
    129     Object* obj = Heap::AllocateFixedArray(100, TENURED);
    130     if (obj->IsFailure()) break;
    131 
    132     host->set(0, obj);
    133     host = FixedArray::cast(obj);
    134   }
    135 
    136   // Call mark compact GC, and it should pass.
    137   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
    138 
    139   // array should not be promoted because the old space is full.
    140   CHECK(Heap::InSpace(*array, NEW_SPACE));
    141 }
    142 
    143 
    144 TEST(MarkCompactCollector) {
    145   InitializeVM();
    146 
    147   v8::HandleScope sc;
    148   // call mark-compact when heap is empty
    149   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
    150 
    151   // keep allocating garbage in new space until it fails
    152   const int ARRAY_SIZE = 100;
    153   Object* array;
    154   do {
    155     array = Heap::AllocateFixedArray(ARRAY_SIZE);
    156   } while (!array->IsFailure());
    157   CHECK(Heap::CollectGarbage(0, NEW_SPACE));
    158 
    159   array = Heap::AllocateFixedArray(ARRAY_SIZE);
    160   CHECK(!array->IsFailure());
    161 
    162   // keep allocating maps until it fails
    163   Object* mapp;
    164   do {
    165     mapp = Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    166   } while (!mapp->IsFailure());
    167   CHECK(Heap::CollectGarbage(0, MAP_SPACE));
    168   mapp = Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    169   CHECK(!mapp->IsFailure());
    170 
    171   // allocate a garbage
    172   String* func_name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
    173   SharedFunctionInfo* function_share =
    174     SharedFunctionInfo::cast(Heap::AllocateSharedFunctionInfo(func_name));
    175   JSFunction* function =
    176     JSFunction::cast(Heap::AllocateFunction(*Top::function_map(),
    177                                             function_share,
    178                                             Heap::undefined_value()));
    179   Map* initial_map =
    180       Map::cast(Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize));
    181   function->set_initial_map(initial_map);
    182   Top::context()->global()->SetProperty(func_name, function, NONE);
    183 
    184   JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function));
    185   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
    186 
    187   func_name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
    188   CHECK(Top::context()->global()->HasLocalProperty(func_name));
    189   Object* func_value = Top::context()->global()->GetProperty(func_name);
    190   CHECK(func_value->IsJSFunction());
    191   function = JSFunction::cast(func_value);
    192 
    193   obj = JSObject::cast(Heap::AllocateJSObject(function));
    194   String* obj_name = String::cast(Heap::LookupAsciiSymbol("theObject"));
    195   Top::context()->global()->SetProperty(obj_name, obj, NONE);
    196   String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
    197   obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
    198 
    199   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
    200 
    201   obj_name = String::cast(Heap::LookupAsciiSymbol("theObject"));
    202   CHECK(Top::context()->global()->HasLocalProperty(obj_name));
    203   CHECK(Top::context()->global()->GetProperty(obj_name)->IsJSObject());
    204   obj = JSObject::cast(Top::context()->global()->GetProperty(obj_name));
    205   prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
    206   CHECK(obj->GetProperty(prop_name) == Smi::FromInt(23));
    207 }
    208 
    209 
    210 static Handle<Map> CreateMap() {
    211   return Factory::NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    212 }
    213 
    214 
    215 TEST(MapCompact) {
    216   FLAG_max_map_space_pages = 16;
    217   InitializeVM();
    218 
    219   {
    220     v8::HandleScope sc;
    221     // keep allocating maps while pointers are still encodable and thus
    222     // mark compact is permitted.
    223     Handle<JSObject> root = Factory::NewJSObjectFromMap(CreateMap());
    224     do {
    225       Handle<Map> map = CreateMap();
    226       map->set_prototype(*root);
    227       root = Factory::NewJSObjectFromMap(map);
    228     } while (Heap::map_space()->MapPointersEncodable());
    229   }
    230   // Now, as we don't have any handles to just allocated maps, we should
    231   // be able to trigger map compaction.
    232   // To give an additional chance to fail, try to force compaction which
    233   // should be impossible right now.
    234   Heap::CollectAllGarbage(true);
    235   // And now map pointers should be encodable again.
    236   CHECK(Heap::map_space()->MapPointersEncodable());
    237 }
    238 
    239 
    240 static int gc_starts = 0;
    241 static int gc_ends = 0;
    242 
    243 static void GCPrologueCallbackFunc() {
    244   CHECK(gc_starts == gc_ends);
    245   gc_starts++;
    246 }
    247 
    248 
    249 static void GCEpilogueCallbackFunc() {
    250   CHECK(gc_starts == gc_ends + 1);
    251   gc_ends++;
    252 }
    253 
    254 
    255 TEST(GCCallback) {
    256   InitializeVM();
    257 
    258   Heap::SetGlobalGCPrologueCallback(&GCPrologueCallbackFunc);
    259   Heap::SetGlobalGCEpilogueCallback(&GCEpilogueCallbackFunc);
    260 
    261   // Scavenge does not call GC callback functions.
    262   Heap::PerformScavenge();
    263 
    264   CHECK_EQ(0, gc_starts);
    265   CHECK_EQ(gc_ends, gc_starts);
    266 
    267   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
    268   CHECK_EQ(1, gc_starts);
    269   CHECK_EQ(gc_ends, gc_starts);
    270 }
    271 
    272 
    273 static int NumberOfWeakCalls = 0;
    274 static void WeakPointerCallback(v8::Persistent<v8::Value> handle, void* id) {
    275   NumberOfWeakCalls++;
    276 }
    277 
    278 TEST(ObjectGroups) {
    279   InitializeVM();
    280 
    281   NumberOfWeakCalls = 0;
    282   v8::HandleScope handle_scope;
    283 
    284   Handle<Object> g1s1 =
    285     GlobalHandles::Create(Heap::AllocateFixedArray(1));
    286   Handle<Object> g1s2 =
    287     GlobalHandles::Create(Heap::AllocateFixedArray(1));
    288   GlobalHandles::MakeWeak(g1s1.location(),
    289                           reinterpret_cast<void*>(1234),
    290                           &WeakPointerCallback);
    291   GlobalHandles::MakeWeak(g1s2.location(),
    292                           reinterpret_cast<void*>(1234),
    293                           &WeakPointerCallback);
    294 
    295   Handle<Object> g2s1 =
    296     GlobalHandles::Create(Heap::AllocateFixedArray(1));
    297   Handle<Object> g2s2 =
    298     GlobalHandles::Create(Heap::AllocateFixedArray(1));
    299   GlobalHandles::MakeWeak(g2s1.location(),
    300                           reinterpret_cast<void*>(1234),
    301                           &WeakPointerCallback);
    302   GlobalHandles::MakeWeak(g2s2.location(),
    303                           reinterpret_cast<void*>(1234),
    304                           &WeakPointerCallback);
    305 
    306   Handle<Object> root = GlobalHandles::Create(*g1s1);  // make a root.
    307 
    308   // Connect group 1 and 2, make a cycle.
    309   Handle<FixedArray>::cast(g1s2)->set(0, *g2s2);
    310   Handle<FixedArray>::cast(g2s1)->set(0, *g1s1);
    311 
    312   {
    313     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
    314     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
    315     GlobalHandles::AddGroup(g1_objects, 2);
    316     GlobalHandles::AddGroup(g2_objects, 2);
    317   }
    318   // Do a full GC
    319   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
    320 
    321   // All object should be alive.
    322   CHECK_EQ(0, NumberOfWeakCalls);
    323 
    324   // Weaken the root.
    325   GlobalHandles::MakeWeak(root.location(),
    326                           reinterpret_cast<void*>(1234),
    327                           &WeakPointerCallback);
    328 
    329   // Groups are deleted, rebuild groups.
    330   {
    331     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
    332     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
    333     GlobalHandles::AddGroup(g1_objects, 2);
    334     GlobalHandles::AddGroup(g2_objects, 2);
    335   }
    336 
    337   CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
    338 
    339   // All objects should be gone. 5 global handles in total.
    340   CHECK_EQ(5, NumberOfWeakCalls);
    341 }
    342