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 32using namespace v8::internal; 33using v8::UniqueId; 34 35 36static List<Object*> skippable_objects; 37static List<Object*> can_skip_called_objects; 38 39 40static bool CanSkipCallback(Heap* heap, Object** pointer) { 41 can_skip_called_objects.Add(*pointer); 42 return skippable_objects.Contains(*pointer); 43} 44 45 46static void ResetCanSkipData() { 47 skippable_objects.Clear(); 48 can_skip_called_objects.Clear(); 49} 50 51 52class 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 76class 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 87TEST(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 182TEST(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 277TEST(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 324TEST(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 389TEST(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 406void WeakCallback(const v8::WeakCallbackInfo<void>& data) {} 407 408 409TEST(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