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