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