Home | History | Annotate | Download | only in cctest
      1 // Copyright 2011 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 <utility>
     29 
     30 #include "src/v8.h"
     31 
     32 #include "src/global-handles.h"
     33 #include "test/cctest/cctest.h"
     34 #include "test/cctest/heap/utils-inl.h"
     35 
     36 using namespace v8::internal;
     37 
     38 static Isolate* GetIsolateFrom(LocalContext* context) {
     39   return reinterpret_cast<Isolate*>((*context)->GetIsolate());
     40 }
     41 
     42 
     43 static Handle<JSWeakMap> AllocateJSWeakMap(Isolate* isolate) {
     44   Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
     45   // Do not leak handles for the hash table, it would make entries strong.
     46   {
     47     HandleScope scope(isolate);
     48     Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 1);
     49     weakmap->set_table(*table);
     50   }
     51   return weakmap;
     52 }
     53 
     54 static int NumberOfWeakCalls = 0;
     55 static void WeakPointerCallback(
     56     const v8::WeakCallbackData<v8::Value, void>& data) {
     57   std::pair<v8::Persistent<v8::Value>*, int>* p =
     58       reinterpret_cast<std::pair<v8::Persistent<v8::Value>*, int>*>(
     59           data.GetParameter());
     60   CHECK_EQ(1234, p->second);
     61   NumberOfWeakCalls++;
     62   p->first->Reset();
     63 }
     64 
     65 
     66 TEST(Weakness) {
     67   FLAG_incremental_marking = false;
     68   LocalContext context;
     69   Isolate* isolate = GetIsolateFrom(&context);
     70   Factory* factory = isolate->factory();
     71   Heap* heap = isolate->heap();
     72   HandleScope scope(isolate);
     73   Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
     74   GlobalHandles* global_handles = isolate->global_handles();
     75 
     76   // Keep global reference to the key.
     77   Handle<Object> key;
     78   {
     79     HandleScope scope(isolate);
     80     Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
     81     Handle<JSObject> object = factory->NewJSObjectFromMap(map);
     82     key = global_handles->Create(*object);
     83   }
     84   CHECK(!global_handles->IsWeak(key.location()));
     85 
     86   // Put two chained entries into weak map.
     87   {
     88     HandleScope scope(isolate);
     89     Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
     90     Handle<JSObject> object = factory->NewJSObjectFromMap(map);
     91     Handle<Smi> smi(Smi::FromInt(23), isolate);
     92     int32_t hash = Object::GetOrCreateHash(isolate, key)->value();
     93     JSWeakCollection::Set(weakmap, key, object, hash);
     94     int32_t object_hash = Object::GetOrCreateHash(isolate, object)->value();
     95     JSWeakCollection::Set(weakmap, object, smi, object_hash);
     96   }
     97   CHECK_EQ(2, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
     98 
     99   // Force a full GC.
    100   heap->CollectAllGarbage(false);
    101   CHECK_EQ(0, NumberOfWeakCalls);
    102   CHECK_EQ(2, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
    103   CHECK_EQ(
    104       0, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
    105 
    106   // Make the global reference to the key weak.
    107   {
    108     HandleScope scope(isolate);
    109     std::pair<Handle<Object>*, int> handle_and_id(&key, 1234);
    110     GlobalHandles::MakeWeak(key.location(),
    111                             reinterpret_cast<void*>(&handle_and_id),
    112                             &WeakPointerCallback);
    113   }
    114   CHECK(global_handles->IsWeak(key.location()));
    115 
    116   // Force a full GC.
    117   // Perform two consecutive GCs because the first one will only clear
    118   // weak references whereas the second one will also clear weak maps.
    119   heap->CollectAllGarbage(false);
    120   CHECK_EQ(1, NumberOfWeakCalls);
    121   CHECK_EQ(2, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
    122   CHECK_EQ(
    123       0, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
    124   heap->CollectAllGarbage(false);
    125   CHECK_EQ(1, NumberOfWeakCalls);
    126   CHECK_EQ(0, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
    127   CHECK_EQ(2,
    128            ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
    129 }
    130 
    131 
    132 TEST(Shrinking) {
    133   LocalContext context;
    134   Isolate* isolate = GetIsolateFrom(&context);
    135   Factory* factory = isolate->factory();
    136   Heap* heap = isolate->heap();
    137   HandleScope scope(isolate);
    138   Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
    139 
    140   // Check initial capacity.
    141   CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->Capacity());
    142 
    143   // Fill up weak map to trigger capacity change.
    144   {
    145     HandleScope scope(isolate);
    146     Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    147     for (int i = 0; i < 32; i++) {
    148       Handle<JSObject> object = factory->NewJSObjectFromMap(map);
    149       Handle<Smi> smi(Smi::FromInt(i), isolate);
    150       int32_t object_hash = Object::GetOrCreateHash(isolate, object)->value();
    151       JSWeakCollection::Set(weakmap, object, smi, object_hash);
    152     }
    153   }
    154 
    155   // Check increased capacity.
    156   CHECK_EQ(128, ObjectHashTable::cast(weakmap->table())->Capacity());
    157 
    158   // Force a full GC.
    159   CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
    160   CHECK_EQ(
    161       0, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
    162   heap->CollectAllGarbage(false);
    163   CHECK_EQ(0, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
    164   CHECK_EQ(
    165       32, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
    166 
    167   // Check shrunk capacity.
    168   CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->Capacity());
    169 }
    170 
    171 
    172 // Test that weak map values on an evacuation candidate which are not reachable
    173 // by other paths are correctly recorded in the slots buffer.
    174 TEST(Regress2060a) {
    175   if (i::FLAG_never_compact) return;
    176   FLAG_always_compact = true;
    177   LocalContext context;
    178   Isolate* isolate = GetIsolateFrom(&context);
    179   Factory* factory = isolate->factory();
    180   Heap* heap = isolate->heap();
    181   HandleScope scope(isolate);
    182   Handle<JSFunction> function = factory->NewFunction(
    183       factory->function_string());
    184   Handle<JSObject> key = factory->NewJSObject(function);
    185   Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
    186 
    187   // Start second old-space page so that values land on evacuation candidate.
    188   Page* first_page = heap->old_space()->anchor()->next_page();
    189   SimulateFullSpace(heap->old_space());
    190 
    191   // Fill up weak map with values on an evacuation candidate.
    192   {
    193     HandleScope scope(isolate);
    194     for (int i = 0; i < 32; i++) {
    195       Handle<JSObject> object = factory->NewJSObject(function, TENURED);
    196       CHECK(!heap->InNewSpace(object->address()));
    197       CHECK(!first_page->Contains(object->address()));
    198       int32_t hash = Object::GetOrCreateHash(isolate, key)->value();
    199       JSWeakCollection::Set(weakmap, key, object, hash);
    200     }
    201   }
    202 
    203   // Force compacting garbage collection.
    204   CHECK(FLAG_always_compact);
    205   heap->CollectAllGarbage();
    206 }
    207 
    208 
    209 // Test that weak map keys on an evacuation candidate which are reachable by
    210 // other strong paths are correctly recorded in the slots buffer.
    211 TEST(Regress2060b) {
    212   if (i::FLAG_never_compact) return;
    213   FLAG_always_compact = true;
    214 #ifdef VERIFY_HEAP
    215   FLAG_verify_heap = true;
    216 #endif
    217 
    218   LocalContext context;
    219   Isolate* isolate = GetIsolateFrom(&context);
    220   Factory* factory = isolate->factory();
    221   Heap* heap = isolate->heap();
    222   HandleScope scope(isolate);
    223   Handle<JSFunction> function = factory->NewFunction(
    224       factory->function_string());
    225 
    226   // Start second old-space page so that keys land on evacuation candidate.
    227   Page* first_page = heap->old_space()->anchor()->next_page();
    228   SimulateFullSpace(heap->old_space());
    229 
    230   // Fill up weak map with keys on an evacuation candidate.
    231   Handle<JSObject> keys[32];
    232   for (int i = 0; i < 32; i++) {
    233     keys[i] = factory->NewJSObject(function, TENURED);
    234     CHECK(!heap->InNewSpace(keys[i]->address()));
    235     CHECK(!first_page->Contains(keys[i]->address()));
    236   }
    237   Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
    238   for (int i = 0; i < 32; i++) {
    239     Handle<Smi> smi(Smi::FromInt(i), isolate);
    240     int32_t hash = Object::GetOrCreateHash(isolate, keys[i])->value();
    241     JSWeakCollection::Set(weakmap, keys[i], smi, hash);
    242   }
    243 
    244   // Force compacting garbage collection. The subsequent collections are used
    245   // to verify that key references were actually updated.
    246   CHECK(FLAG_always_compact);
    247   heap->CollectAllGarbage();
    248   heap->CollectAllGarbage();
    249   heap->CollectAllGarbage();
    250 }
    251 
    252 
    253 TEST(Regress399527) {
    254   CcTest::InitializeVM();
    255   v8::HandleScope scope(CcTest::isolate());
    256   Isolate* isolate = CcTest::i_isolate();
    257   Heap* heap = isolate->heap();
    258   {
    259     HandleScope scope(isolate);
    260     AllocateJSWeakMap(isolate);
    261     SimulateIncrementalMarking(heap);
    262   }
    263   // The weak map is marked black here but leaving the handle scope will make
    264   // the object unreachable. Aborting incremental marking will clear all the
    265   // marking bits which makes the weak map garbage.
    266   heap->CollectAllGarbage();
    267 }
    268