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