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 "v8.h"
     29 
     30 #include "global-handles.h"
     31 #include "snapshot.h"
     32 #include "cctest.h"
     33 
     34 using namespace v8::internal;
     35 
     36 
     37 static Isolate* GetIsolateFrom(LocalContext* context) {
     38   return reinterpret_cast<Isolate*>((*context)->GetIsolate());
     39 }
     40 
     41 
     42 static Handle<JSWeakSet> AllocateJSWeakSet(Isolate* isolate) {
     43   Factory* factory = isolate->factory();
     44   Heap* heap = isolate->heap();
     45   Handle<Map> map = factory->NewMap(JS_WEAK_SET_TYPE, JSWeakSet::kSize);
     46   Handle<JSObject> weakset_obj = factory->NewJSObjectFromMap(map);
     47   Handle<JSWeakSet> weakset(JSWeakSet::cast(*weakset_obj));
     48   // Do not use handles for the hash table, it would make entries strong.
     49   Object* table_obj = ObjectHashTable::Allocate(heap, 1)->ToObjectChecked();
     50   ObjectHashTable* table = ObjectHashTable::cast(table_obj);
     51   weakset->set_table(table);
     52   weakset->set_next(Smi::FromInt(0));
     53   return weakset;
     54 }
     55 
     56 static void PutIntoWeakSet(Handle<JSWeakSet> weakset,
     57                            Handle<JSObject> key,
     58                            Handle<Object> value) {
     59   Handle<ObjectHashTable> table = PutIntoObjectHashTable(
     60       Handle<ObjectHashTable>(ObjectHashTable::cast(weakset->table())),
     61       Handle<JSObject>(JSObject::cast(*key)),
     62       value);
     63   weakset->set_table(*table);
     64 }
     65 
     66 static int NumberOfWeakCalls = 0;
     67 static void WeakPointerCallback(v8::Isolate* isolate,
     68                                 v8::Persistent<v8::Value>* handle,
     69                                 void* id) {
     70   ASSERT(id == reinterpret_cast<void*>(1234));
     71   NumberOfWeakCalls++;
     72   handle->Dispose(isolate);
     73 }
     74 
     75 
     76 TEST(WeakSet_Weakness) {
     77   FLAG_incremental_marking = false;
     78   LocalContext context;
     79   Isolate* isolate = GetIsolateFrom(&context);
     80   Factory* factory = isolate->factory();
     81   Heap* heap = isolate->heap();
     82   HandleScope scope(isolate);
     83   Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
     84   GlobalHandles* global_handles = isolate->global_handles();
     85 
     86   // Keep global reference to the key.
     87   Handle<Object> key;
     88   {
     89     HandleScope scope(isolate);
     90     Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
     91     Handle<JSObject> object = factory->NewJSObjectFromMap(map);
     92     key = global_handles->Create(*object);
     93   }
     94   CHECK(!global_handles->IsWeak(key.location()));
     95 
     96   // Put entry into weak set.
     97   {
     98     HandleScope scope(isolate);
     99     PutIntoWeakSet(weakset,
    100                    Handle<JSObject>(JSObject::cast(*key)),
    101                    Handle<Smi>(Smi::FromInt(23), isolate));
    102   }
    103   CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements());
    104 
    105   // Force a full GC.
    106   heap->CollectAllGarbage(false);
    107   CHECK_EQ(0, NumberOfWeakCalls);
    108   CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements());
    109   CHECK_EQ(
    110       0, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
    111 
    112   // Make the global reference to the key weak.
    113   {
    114     HandleScope scope(isolate);
    115     global_handles->MakeWeak(key.location(),
    116                              reinterpret_cast<void*>(1234),
    117                              &WeakPointerCallback);
    118   }
    119   CHECK(global_handles->IsWeak(key.location()));
    120 
    121   // Force a full GC.
    122   // Perform two consecutive GCs because the first one will only clear
    123   // weak references whereas the second one will also clear weak sets.
    124   heap->CollectAllGarbage(false);
    125   CHECK_EQ(1, NumberOfWeakCalls);
    126   CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements());
    127   CHECK_EQ(
    128       0, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
    129   heap->CollectAllGarbage(false);
    130   CHECK_EQ(1, NumberOfWeakCalls);
    131   CHECK_EQ(0, ObjectHashTable::cast(weakset->table())->NumberOfElements());
    132   CHECK_EQ(
    133       1, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
    134 }
    135 
    136 
    137 TEST(WeakSet_Shrinking) {
    138   LocalContext context;
    139   Isolate* isolate = GetIsolateFrom(&context);
    140   Factory* factory = isolate->factory();
    141   Heap* heap = isolate->heap();
    142   HandleScope scope(isolate);
    143   Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
    144 
    145   // Check initial capacity.
    146   CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->Capacity());
    147 
    148   // Fill up weak set to trigger capacity change.
    149   {
    150     HandleScope scope(isolate);
    151     Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    152     for (int i = 0; i < 32; i++) {
    153       Handle<JSObject> object = factory->NewJSObjectFromMap(map);
    154       PutIntoWeakSet(weakset, object, Handle<Smi>(Smi::FromInt(i), isolate));
    155     }
    156   }
    157 
    158   // Check increased capacity.
    159   CHECK_EQ(128, ObjectHashTable::cast(weakset->table())->Capacity());
    160 
    161   // Force a full GC.
    162   CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->NumberOfElements());
    163   CHECK_EQ(
    164       0, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
    165   heap->CollectAllGarbage(false);
    166   CHECK_EQ(0, ObjectHashTable::cast(weakset->table())->NumberOfElements());
    167   CHECK_EQ(
    168       32, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
    169 
    170   // Check shrunk capacity.
    171   CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->Capacity());
    172 }
    173 
    174 
    175 // Test that weak set values on an evacuation candidate which are not reachable
    176 // by other paths are correctly recorded in the slots buffer.
    177 TEST(WeakSet_Regress2060a) {
    178   FLAG_always_compact = true;
    179   LocalContext context;
    180   Isolate* isolate = GetIsolateFrom(&context);
    181   Factory* factory = isolate->factory();
    182   Heap* heap = isolate->heap();
    183   HandleScope scope(isolate);
    184   Handle<JSFunction> function =
    185       factory->NewFunction(factory->function_string(), factory->null_value());
    186   Handle<JSObject> key = factory->NewJSObject(function);
    187   Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
    188 
    189   // Start second old-space page so that values land on evacuation candidate.
    190   Page* first_page = heap->old_pointer_space()->anchor()->next_page();
    191   factory->NewFixedArray(900 * KB / kPointerSize, TENURED);
    192 
    193   // Fill up weak set with values on an evacuation candidate.
    194   {
    195     HandleScope scope(isolate);
    196     for (int i = 0; i < 32; i++) {
    197       Handle<JSObject> object = factory->NewJSObject(function, TENURED);
    198       CHECK(!heap->InNewSpace(object->address()));
    199       CHECK(!first_page->Contains(object->address()));
    200       PutIntoWeakSet(weakset, key, object);
    201     }
    202   }
    203 
    204   // Force compacting garbage collection.
    205   CHECK(FLAG_always_compact);
    206   heap->CollectAllGarbage(Heap::kNoGCFlags);
    207 }
    208 
    209 
    210 // Test that weak set keys on an evacuation candidate which are reachable by
    211 // other strong paths are correctly recorded in the slots buffer.
    212 TEST(WeakSet_Regress2060b) {
    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 =
    224       factory->NewFunction(factory->function_string(), factory->null_value());
    225 
    226   // Start second old-space page so that keys land on evacuation candidate.
    227   Page* first_page = heap->old_pointer_space()->anchor()->next_page();
    228   factory->NewFixedArray(900 * KB / kPointerSize, TENURED);
    229 
    230   // Fill up weak set 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<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
    238   for (int i = 0; i < 32; i++) {
    239     PutIntoWeakSet(weakset,
    240                    keys[i],
    241                    Handle<Smi>(Smi::FromInt(i), isolate));
    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(Heap::kNoGCFlags);
    248   heap->CollectAllGarbage(Heap::kNoGCFlags);
    249   heap->CollectAllGarbage(Heap::kNoGCFlags);
    250 }
    251