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 CHECK(!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 void VisitPointers(Object** start, Object** end) override { 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 CHECK(can_skip_called_objects.length() == 4); 125 CHECK(can_skip_called_objects.Contains(*g1s1.location())); 126 CHECK(can_skip_called_objects.Contains(*g1s2.location())); 127 CHECK(can_skip_called_objects.Contains(*g2s1.location())); 128 CHECK(can_skip_called_objects.Contains(*g2s2.location())); 129 130 // Nothing was visited. 131 CHECK(visitor.visited.length() == 0); 132 CHECK(!info1.has_been_disposed()); 133 CHECK(!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 CHECK(can_skip_called_objects.length() == 3 || 149 can_skip_called_objects.length() == 4); 150 CHECK(can_skip_called_objects.Contains(*g1s2.location())); 151 CHECK(can_skip_called_objects.Contains(*g2s1.location())); 152 CHECK(can_skip_called_objects.Contains(*g2s2.location())); 153 154 // The first group was visited. 155 CHECK(visitor.visited.length() == 2); 156 CHECK(visitor.visited.Contains(*g1s1.location())); 157 CHECK(visitor.visited.Contains(*g1s2.location())); 158 CHECK(info1.has_been_disposed()); 159 CHECK(!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 CHECK(can_skip_called_objects.length() == 1); 170 CHECK(can_skip_called_objects.Contains(*g2s1.location()) || 171 can_skip_called_objects.Contains(*g2s2.location())); 172 173 // The second group was visited. 174 CHECK(visitor.visited.length() == 2); 175 CHECK(visitor.visited.Contains(*g2s1.location())); 176 CHECK(visitor.visited.Contains(*g2s2.location())); 177 CHECK(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 CHECK(can_skip_called_objects.length() == 4); 220 CHECK(can_skip_called_objects.Contains(*g1s1.location())); 221 CHECK(can_skip_called_objects.Contains(*g1s2.location())); 222 CHECK(can_skip_called_objects.Contains(*g2s1.location())); 223 CHECK(can_skip_called_objects.Contains(*g2s2.location())); 224 225 // Nothing was visited. 226 CHECK(visitor.visited.length() == 0); 227 CHECK(!info1.has_been_disposed()); 228 CHECK(!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 CHECK(can_skip_called_objects.length() == 3 || 244 can_skip_called_objects.length() == 4); 245 CHECK(can_skip_called_objects.Contains(*g1s2.location())); 246 CHECK(can_skip_called_objects.Contains(*g2s1.location())); 247 CHECK(can_skip_called_objects.Contains(*g2s2.location())); 248 249 // The first group was visited. 250 CHECK(visitor.visited.length() == 2); 251 CHECK(visitor.visited.Contains(*g1s1.location())); 252 CHECK(visitor.visited.Contains(*g1s2.location())); 253 CHECK(info1.has_been_disposed()); 254 CHECK(!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 CHECK(can_skip_called_objects.length() == 1); 265 CHECK(can_skip_called_objects.Contains(*g2s1.location()) || 266 can_skip_called_objects.Contains(*g2s2.location())); 267 268 // The second group was visited. 269 CHECK(visitor.visited.length() == 2); 270 CHECK(visitor.visited.Contains(*g2s1.location())); 271 CHECK(visitor.visited.Contains(*g2s2.location())); 272 CHECK(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 CHECK(implicit_refs->length() == 2); 310 CHECK(implicit_refs->at(0)->parent == 311 reinterpret_cast<HeapObject**>(g1s1.location())); 312 CHECK(implicit_refs->at(0)->length == 2); 313 CHECK(implicit_refs->at(0)->children[0] == g1c1.location()); 314 CHECK(implicit_refs->at(0)->children[1] == g1c2.location()); 315 CHECK(implicit_refs->at(1)->parent == 316 reinterpret_cast<HeapObject**>(g2s1.location())); 317 CHECK(implicit_refs->at(1)->length == 1); 318 CHECK(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(v8_isolate->GetCurrentContext(), i, 341 v8::Integer::New(v8_isolate, i)) 342 .FromJust(); 343 // Create with internal api 344 eternal_handles->Create( 345 isolate, *v8::Utils::OpenHandle(*object), &indices[i]); 346 // Create with external api 347 CHECK(eternals[i].IsEmpty()); 348 eternals[i].Set(v8_isolate, object); 349 CHECK(!eternals[i].IsEmpty()); 350 } 351 352 isolate->heap()->CollectAllAvailableGarbage(); 353 354 for (int i = 0; i < kArrayLength; i++) { 355 for (int j = 0; j < 2; j++) { 356 HandleScope scope(isolate); 357 v8::Local<v8::Value> local; 358 if (j == 0) { 359 // Test internal api 360 local = v8::Utils::ToLocal(eternal_handles->Get(indices[i])); 361 } else { 362 // Test external api 363 local = eternals[i].Get(v8_isolate); 364 } 365 v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(local); 366 v8::Local<v8::Value> value = 367 object->Get(v8_isolate->GetCurrentContext(), i).ToLocalChecked(); 368 CHECK(value->IsInt32()); 369 CHECK_EQ(i, 370 value->Int32Value(v8_isolate->GetCurrentContext()).FromJust()); 371 } 372 } 373 374 CHECK_EQ(2*kArrayLength, eternal_handles->NumberOfHandles()); 375 376 // Create an eternal via the constructor 377 { 378 HandleScope scope(isolate); 379 v8::Local<v8::Object> object = v8::Object::New(v8_isolate); 380 v8::Eternal<v8::Object> eternal(v8_isolate, object); 381 CHECK(!eternal.IsEmpty()); 382 CHECK(object == eternal.Get(v8_isolate)); 383 } 384 385 CHECK_EQ(2*kArrayLength + 1, eternal_handles->NumberOfHandles()); 386 } 387 388 389 TEST(PersistentBaseGetLocal) { 390 CcTest::InitializeVM(); 391 v8::Isolate* isolate = CcTest::isolate(); 392 393 v8::HandleScope scope(isolate); 394 v8::Local<v8::Object> o = v8::Object::New(isolate); 395 CHECK(!o.IsEmpty()); 396 v8::Persistent<v8::Object> p(isolate, o); 397 CHECK(o == p.Get(isolate)); 398 CHECK(v8::Local<v8::Object>::New(isolate, p) == p.Get(isolate)); 399 400 v8::Global<v8::Object> g(isolate, o); 401 CHECK(o == g.Get(isolate)); 402 CHECK(v8::Local<v8::Object>::New(isolate, g) == g.Get(isolate)); 403 } 404 405 406 void WeakCallback(const v8::WeakCallbackInfo<void>& data) {} 407 408 409 TEST(WeakPersistentSmi) { 410 CcTest::InitializeVM(); 411 v8::Isolate* isolate = CcTest::isolate(); 412 413 v8::HandleScope scope(isolate); 414 v8::Local<v8::Number> n = v8::Number::New(isolate, 0); 415 v8::Global<v8::Number> g(isolate, n); 416 417 // Should not crash. 418 g.SetWeak<void>(nullptr, &WeakCallback, v8::WeakCallbackType::kParameter); 419 } 420 421 void finalizer(const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) { 422 data.GetParameter()->ClearWeak(); 423 v8::Local<v8::Object> o = 424 v8::Local<v8::Object>::New(data.GetIsolate(), *data.GetParameter()); 425 o->Set(data.GetIsolate()->GetCurrentContext(), v8_str("finalizer"), 426 v8_str("was here")) 427 .FromJust(); 428 } 429 430 TEST(FinalizerWeakness) { 431 CcTest::InitializeVM(); 432 v8::Isolate* isolate = CcTest::isolate(); 433 434 v8::Global<v8::Object> g; 435 int identity; 436 437 { 438 v8::HandleScope scope(isolate); 439 v8::Local<v8::Object> o = v8::Object::New(isolate); 440 identity = o->GetIdentityHash(); 441 g.Reset(isolate, o); 442 g.SetWeak(&g, finalizer, v8::WeakCallbackType::kFinalizer); 443 } 444 445 CcTest::i_isolate()->heap()->CollectAllAvailableGarbage(); 446 447 CHECK(!g.IsEmpty()); 448 v8::HandleScope scope(isolate); 449 v8::Local<v8::Object> o = v8::Local<v8::Object>::New(isolate, g); 450 CHECK_EQ(identity, o->GetIdentityHash()); 451 CHECK(o->Has(isolate->GetCurrentContext(), v8_str("finalizer")).FromJust()); 452 } 453 454 TEST(PhatomHandlesWithoutCallbacks) { 455 CcTest::InitializeVM(); 456 v8::Isolate* isolate = CcTest::isolate(); 457 458 v8::Global<v8::Object> g1, g2; 459 { 460 v8::HandleScope scope(isolate); 461 g1.Reset(isolate, v8::Object::New(isolate)); 462 g1.SetWeak(); 463 g2.Reset(isolate, v8::Object::New(isolate)); 464 g2.SetWeak(); 465 } 466 467 CHECK_EQ(0, isolate->NumberOfPhantomHandleResetsSinceLastCall()); 468 CcTest::i_isolate()->heap()->CollectAllAvailableGarbage(); 469 CHECK_EQ(2, isolate->NumberOfPhantomHandleResetsSinceLastCall()); 470 CHECK_EQ(0, isolate->NumberOfPhantomHandleResetsSinceLastCall()); 471 } 472