1 // Copyright 2013 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 "src/global-handles.h" 29 30 #include "test/cctest/cctest.h" 31 32 using namespace v8::internal; 33 using v8::UniqueId; 34 35 36 static List<Object*> skippable_objects; 37 static List<Object*> can_skip_called_objects; 38 39 40 static bool CanSkipCallback(Heap* heap, Object** pointer) { 41 can_skip_called_objects.Add(*pointer); 42 return skippable_objects.Contains(*pointer); 43 } 44 45 46 static void ResetCanSkipData() { 47 skippable_objects.Clear(); 48 can_skip_called_objects.Clear(); 49 } 50 51 52 class TestRetainedObjectInfo : public v8::RetainedObjectInfo { 53 public: 54 TestRetainedObjectInfo() : has_been_disposed_(false) {} 55 56 bool has_been_disposed() { return has_been_disposed_; } 57 58 virtual void Dispose() { 59 DCHECK(!has_been_disposed_); 60 has_been_disposed_ = true; 61 } 62 63 virtual bool IsEquivalent(v8::RetainedObjectInfo* other) { 64 return other == this; 65 } 66 67 virtual intptr_t GetHash() { return 0; } 68 69 virtual const char* GetLabel() { return "whatever"; } 70 71 private: 72 bool has_been_disposed_; 73 }; 74 75 76 class TestObjectVisitor : public ObjectVisitor { 77 public: 78 virtual void VisitPointers(Object** start, Object** end) { 79 for (Object** o = start; o != end; ++o) 80 visited.Add(*o); 81 } 82 83 List<Object*> visited; 84 }; 85 86 87 TEST(IterateObjectGroupsOldApi) { 88 CcTest::InitializeVM(); 89 Isolate* isolate = CcTest::i_isolate(); 90 GlobalHandles* global_handles = isolate->global_handles(); 91 v8::HandleScope handle_scope(CcTest::isolate()); 92 93 Handle<Object> g1s1 = 94 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 95 Handle<Object> g1s2 = 96 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 97 98 Handle<Object> g2s1 = 99 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 100 Handle<Object> g2s2 = 101 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 102 103 TestRetainedObjectInfo info1; 104 TestRetainedObjectInfo info2; 105 { 106 Object** g1_objects[] = { g1s1.location(), g1s2.location() }; 107 Object** g2_objects[] = { g2s1.location(), g2s2.location() }; 108 109 global_handles->AddObjectGroup(g1_objects, 2, &info1); 110 global_handles->AddObjectGroup(g2_objects, 2, &info2); 111 } 112 113 // Iterate the object groups. First skip all. 114 { 115 ResetCanSkipData(); 116 skippable_objects.Add(*g1s1.location()); 117 skippable_objects.Add(*g1s2.location()); 118 skippable_objects.Add(*g2s1.location()); 119 skippable_objects.Add(*g2s2.location()); 120 TestObjectVisitor visitor; 121 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 122 123 // CanSkipCallback was called for all objects. 124 DCHECK(can_skip_called_objects.length() == 4); 125 DCHECK(can_skip_called_objects.Contains(*g1s1.location())); 126 DCHECK(can_skip_called_objects.Contains(*g1s2.location())); 127 DCHECK(can_skip_called_objects.Contains(*g2s1.location())); 128 DCHECK(can_skip_called_objects.Contains(*g2s2.location())); 129 130 // Nothing was visited. 131 DCHECK(visitor.visited.length() == 0); 132 DCHECK(!info1.has_been_disposed()); 133 DCHECK(!info2.has_been_disposed()); 134 } 135 136 // Iterate again, now only skip the second object group. 137 { 138 ResetCanSkipData(); 139 // The first grough should still be visited, since only one object is 140 // skipped. 141 skippable_objects.Add(*g1s1.location()); 142 skippable_objects.Add(*g2s1.location()); 143 skippable_objects.Add(*g2s2.location()); 144 TestObjectVisitor visitor; 145 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 146 147 // CanSkipCallback was called for all objects. 148 DCHECK(can_skip_called_objects.length() == 3 || 149 can_skip_called_objects.length() == 4); 150 DCHECK(can_skip_called_objects.Contains(*g1s2.location())); 151 DCHECK(can_skip_called_objects.Contains(*g2s1.location())); 152 DCHECK(can_skip_called_objects.Contains(*g2s2.location())); 153 154 // The first group was visited. 155 DCHECK(visitor.visited.length() == 2); 156 DCHECK(visitor.visited.Contains(*g1s1.location())); 157 DCHECK(visitor.visited.Contains(*g1s2.location())); 158 DCHECK(info1.has_been_disposed()); 159 DCHECK(!info2.has_been_disposed()); 160 } 161 162 // Iterate again, don't skip anything. 163 { 164 ResetCanSkipData(); 165 TestObjectVisitor visitor; 166 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 167 168 // CanSkipCallback was called for all objects. 169 DCHECK(can_skip_called_objects.length() == 1); 170 DCHECK(can_skip_called_objects.Contains(*g2s1.location()) || 171 can_skip_called_objects.Contains(*g2s2.location())); 172 173 // The second group was visited. 174 DCHECK(visitor.visited.length() == 2); 175 DCHECK(visitor.visited.Contains(*g2s1.location())); 176 DCHECK(visitor.visited.Contains(*g2s2.location())); 177 DCHECK(info2.has_been_disposed()); 178 } 179 } 180 181 182 TEST(IterateObjectGroups) { 183 CcTest::InitializeVM(); 184 Isolate* isolate = CcTest::i_isolate(); 185 GlobalHandles* global_handles = isolate->global_handles(); 186 187 v8::HandleScope handle_scope(CcTest::isolate()); 188 189 Handle<Object> g1s1 = 190 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 191 Handle<Object> g1s2 = 192 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 193 194 Handle<Object> g2s1 = 195 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 196 Handle<Object> g2s2 = 197 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 198 199 TestRetainedObjectInfo info1; 200 TestRetainedObjectInfo info2; 201 global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2)); 202 global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2)); 203 global_handles->SetRetainedObjectInfo(UniqueId(2), &info2); 204 global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1)); 205 global_handles->SetObjectGroupId(g1s2.location(), UniqueId(1)); 206 global_handles->SetRetainedObjectInfo(UniqueId(1), &info1); 207 208 // Iterate the object groups. First skip all. 209 { 210 ResetCanSkipData(); 211 skippable_objects.Add(*g1s1.location()); 212 skippable_objects.Add(*g1s2.location()); 213 skippable_objects.Add(*g2s1.location()); 214 skippable_objects.Add(*g2s2.location()); 215 TestObjectVisitor visitor; 216 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 217 218 // CanSkipCallback was called for all objects. 219 DCHECK(can_skip_called_objects.length() == 4); 220 DCHECK(can_skip_called_objects.Contains(*g1s1.location())); 221 DCHECK(can_skip_called_objects.Contains(*g1s2.location())); 222 DCHECK(can_skip_called_objects.Contains(*g2s1.location())); 223 DCHECK(can_skip_called_objects.Contains(*g2s2.location())); 224 225 // Nothing was visited. 226 DCHECK(visitor.visited.length() == 0); 227 DCHECK(!info1.has_been_disposed()); 228 DCHECK(!info2.has_been_disposed()); 229 } 230 231 // Iterate again, now only skip the second object group. 232 { 233 ResetCanSkipData(); 234 // The first grough should still be visited, since only one object is 235 // skipped. 236 skippable_objects.Add(*g1s1.location()); 237 skippable_objects.Add(*g2s1.location()); 238 skippable_objects.Add(*g2s2.location()); 239 TestObjectVisitor visitor; 240 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 241 242 // CanSkipCallback was called for all objects. 243 DCHECK(can_skip_called_objects.length() == 3 || 244 can_skip_called_objects.length() == 4); 245 DCHECK(can_skip_called_objects.Contains(*g1s2.location())); 246 DCHECK(can_skip_called_objects.Contains(*g2s1.location())); 247 DCHECK(can_skip_called_objects.Contains(*g2s2.location())); 248 249 // The first group was visited. 250 DCHECK(visitor.visited.length() == 2); 251 DCHECK(visitor.visited.Contains(*g1s1.location())); 252 DCHECK(visitor.visited.Contains(*g1s2.location())); 253 DCHECK(info1.has_been_disposed()); 254 DCHECK(!info2.has_been_disposed()); 255 } 256 257 // Iterate again, don't skip anything. 258 { 259 ResetCanSkipData(); 260 TestObjectVisitor visitor; 261 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 262 263 // CanSkipCallback was called for all objects. 264 DCHECK(can_skip_called_objects.length() == 1); 265 DCHECK(can_skip_called_objects.Contains(*g2s1.location()) || 266 can_skip_called_objects.Contains(*g2s2.location())); 267 268 // The second group was visited. 269 DCHECK(visitor.visited.length() == 2); 270 DCHECK(visitor.visited.Contains(*g2s1.location())); 271 DCHECK(visitor.visited.Contains(*g2s2.location())); 272 DCHECK(info2.has_been_disposed()); 273 } 274 } 275 276 277 TEST(ImplicitReferences) { 278 CcTest::InitializeVM(); 279 Isolate* isolate = CcTest::i_isolate(); 280 GlobalHandles* global_handles = isolate->global_handles(); 281 282 v8::HandleScope handle_scope(CcTest::isolate()); 283 284 Handle<Object> g1s1 = 285 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 286 Handle<Object> g1c1 = 287 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 288 Handle<Object> g1c2 = 289 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 290 291 292 Handle<Object> g2s1 = 293 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 294 Handle<Object> g2s2 = 295 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 296 Handle<Object> g2c1 = 297 global_handles->Create(*isolate->factory()->NewFixedArray(1)); 298 299 global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1)); 300 global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2)); 301 global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2)); 302 global_handles->SetReferenceFromGroup(UniqueId(1), g1c1.location()); 303 global_handles->SetReferenceFromGroup(UniqueId(1), g1c2.location()); 304 global_handles->SetReferenceFromGroup(UniqueId(2), g2c1.location()); 305 306 List<ImplicitRefGroup*>* implicit_refs = 307 global_handles->implicit_ref_groups(); 308 USE(implicit_refs); 309 DCHECK(implicit_refs->length() == 2); 310 DCHECK(implicit_refs->at(0)->parent == 311 reinterpret_cast<HeapObject**>(g1s1.location())); 312 DCHECK(implicit_refs->at(0)->length == 2); 313 DCHECK(implicit_refs->at(0)->children[0] == g1c1.location()); 314 DCHECK(implicit_refs->at(0)->children[1] == g1c2.location()); 315 DCHECK(implicit_refs->at(1)->parent == 316 reinterpret_cast<HeapObject**>(g2s1.location())); 317 DCHECK(implicit_refs->at(1)->length == 1); 318 DCHECK(implicit_refs->at(1)->children[0] == g2c1.location()); 319 global_handles->RemoveObjectGroups(); 320 global_handles->RemoveImplicitRefGroups(); 321 } 322 323 324 TEST(EternalHandles) { 325 CcTest::InitializeVM(); 326 Isolate* isolate = CcTest::i_isolate(); 327 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); 328 EternalHandles* eternal_handles = isolate->eternal_handles(); 329 330 // Create a number of handles that will not be on a block boundary 331 const int kArrayLength = 2048-1; 332 int indices[kArrayLength]; 333 v8::Eternal<v8::Value> eternals[kArrayLength]; 334 335 CHECK_EQ(0, eternal_handles->NumberOfHandles()); 336 for (int i = 0; i < kArrayLength; i++) { 337 indices[i] = -1; 338 HandleScope scope(isolate); 339 v8::Local<v8::Object> object = v8::Object::New(v8_isolate); 340 object->Set(i, v8::Integer::New(v8_isolate, i)); 341 // Create with internal api 342 eternal_handles->Create( 343 isolate, *v8::Utils::OpenHandle(*object), &indices[i]); 344 // Create with external api 345 CHECK(eternals[i].IsEmpty()); 346 eternals[i].Set(v8_isolate, object); 347 CHECK(!eternals[i].IsEmpty()); 348 } 349 350 isolate->heap()->CollectAllAvailableGarbage(); 351 352 for (int i = 0; i < kArrayLength; i++) { 353 for (int j = 0; j < 2; j++) { 354 HandleScope scope(isolate); 355 v8::Local<v8::Value> local; 356 if (j == 0) { 357 // Test internal api 358 local = v8::Utils::ToLocal(eternal_handles->Get(indices[i])); 359 } else { 360 // Test external api 361 local = eternals[i].Get(v8_isolate); 362 } 363 v8::Local<v8::Object> object = v8::Handle<v8::Object>::Cast(local); 364 v8::Local<v8::Value> value = object->Get(i); 365 CHECK(value->IsInt32()); 366 CHECK_EQ(i, value->Int32Value()); 367 } 368 } 369 370 CHECK_EQ(2*kArrayLength, eternal_handles->NumberOfHandles()); 371 372 // Create an eternal via the constructor 373 { 374 HandleScope scope(isolate); 375 v8::Local<v8::Object> object = v8::Object::New(v8_isolate); 376 v8::Eternal<v8::Object> eternal(v8_isolate, object); 377 CHECK(!eternal.IsEmpty()); 378 CHECK(object == eternal.Get(v8_isolate)); 379 } 380 381 CHECK_EQ(2*kArrayLength + 1, eternal_handles->NumberOfHandles()); 382 } 383