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 "global-handles.h" 29 30 #include "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 ASSERT(!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 GlobalHandles* global_handles = CcTest::i_isolate()->global_handles(); 90 Heap* heap = CcTest::heap(); 91 v8::HandleScope handle_scope(CcTest::isolate()); 92 93 Handle<Object> g1s1 = 94 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 95 Handle<Object> g1s2 = 96 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 97 98 Handle<Object> g2s1 = 99 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 100 Handle<Object> g2s2 = 101 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 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 ASSERT(can_skip_called_objects.length() == 4); 125 ASSERT(can_skip_called_objects.Contains(*g1s1.location())); 126 ASSERT(can_skip_called_objects.Contains(*g1s2.location())); 127 ASSERT(can_skip_called_objects.Contains(*g2s1.location())); 128 ASSERT(can_skip_called_objects.Contains(*g2s2.location())); 129 130 // Nothing was visited. 131 ASSERT(visitor.visited.length() == 0); 132 ASSERT(!info1.has_been_disposed()); 133 ASSERT(!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 ASSERT(can_skip_called_objects.length() == 3 || 149 can_skip_called_objects.length() == 4); 150 ASSERT(can_skip_called_objects.Contains(*g1s2.location())); 151 ASSERT(can_skip_called_objects.Contains(*g2s1.location())); 152 ASSERT(can_skip_called_objects.Contains(*g2s2.location())); 153 154 // The first group was visited. 155 ASSERT(visitor.visited.length() == 2); 156 ASSERT(visitor.visited.Contains(*g1s1.location())); 157 ASSERT(visitor.visited.Contains(*g1s2.location())); 158 ASSERT(info1.has_been_disposed()); 159 ASSERT(!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 ASSERT(can_skip_called_objects.length() == 1); 170 ASSERT(can_skip_called_objects.Contains(*g2s1.location()) || 171 can_skip_called_objects.Contains(*g2s2.location())); 172 173 // The second group was visited. 174 ASSERT(visitor.visited.length() == 2); 175 ASSERT(visitor.visited.Contains(*g2s1.location())); 176 ASSERT(visitor.visited.Contains(*g2s2.location())); 177 ASSERT(info2.has_been_disposed()); 178 } 179 } 180 181 182 TEST(IterateObjectGroups) { 183 CcTest::InitializeVM(); 184 GlobalHandles* global_handles = CcTest::i_isolate()->global_handles(); 185 Heap* heap = CcTest::heap(); 186 187 v8::HandleScope handle_scope(CcTest::isolate()); 188 189 Handle<Object> g1s1 = 190 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 191 Handle<Object> g1s2 = 192 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 193 194 Handle<Object> g2s1 = 195 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 196 Handle<Object> g2s2 = 197 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 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 ASSERT(can_skip_called_objects.length() == 4); 220 ASSERT(can_skip_called_objects.Contains(*g1s1.location())); 221 ASSERT(can_skip_called_objects.Contains(*g1s2.location())); 222 ASSERT(can_skip_called_objects.Contains(*g2s1.location())); 223 ASSERT(can_skip_called_objects.Contains(*g2s2.location())); 224 225 // Nothing was visited. 226 ASSERT(visitor.visited.length() == 0); 227 ASSERT(!info1.has_been_disposed()); 228 ASSERT(!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 ASSERT(can_skip_called_objects.length() == 3 || 244 can_skip_called_objects.length() == 4); 245 ASSERT(can_skip_called_objects.Contains(*g1s2.location())); 246 ASSERT(can_skip_called_objects.Contains(*g2s1.location())); 247 ASSERT(can_skip_called_objects.Contains(*g2s2.location())); 248 249 // The first group was visited. 250 ASSERT(visitor.visited.length() == 2); 251 ASSERT(visitor.visited.Contains(*g1s1.location())); 252 ASSERT(visitor.visited.Contains(*g1s2.location())); 253 ASSERT(info1.has_been_disposed()); 254 ASSERT(!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 ASSERT(can_skip_called_objects.length() == 1); 265 ASSERT(can_skip_called_objects.Contains(*g2s1.location()) || 266 can_skip_called_objects.Contains(*g2s2.location())); 267 268 // The second group was visited. 269 ASSERT(visitor.visited.length() == 2); 270 ASSERT(visitor.visited.Contains(*g2s1.location())); 271 ASSERT(visitor.visited.Contains(*g2s2.location())); 272 ASSERT(info2.has_been_disposed()); 273 } 274 } 275 276 277 TEST(ImplicitReferences) { 278 CcTest::InitializeVM(); 279 GlobalHandles* global_handles = CcTest::i_isolate()->global_handles(); 280 Heap* heap = CcTest::heap(); 281 282 v8::HandleScope handle_scope(CcTest::isolate()); 283 284 Handle<Object> g1s1 = 285 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 286 Handle<Object> g1c1 = 287 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 288 Handle<Object> g1c2 = 289 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 290 291 292 Handle<Object> g2s1 = 293 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 294 Handle<Object> g2s2 = 295 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 296 Handle<Object> g2c1 = 297 global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked()); 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 ASSERT(implicit_refs->length() == 2); 310 ASSERT(implicit_refs->at(0)->parent == 311 reinterpret_cast<HeapObject**>(g1s1.location())); 312 ASSERT(implicit_refs->at(0)->length == 2); 313 ASSERT(implicit_refs->at(0)->children[0] == g1c1.location()); 314 ASSERT(implicit_refs->at(0)->children[1] == g1c2.location()); 315 ASSERT(implicit_refs->at(1)->parent == 316 reinterpret_cast<HeapObject**>(g2s1.location())); 317 ASSERT(implicit_refs->at(1)->length == 1); 318 ASSERT(implicit_refs->at(1)->children[0] == g2c1.location()); 319 } 320 321 322 TEST(EternalHandles) { 323 CcTest::InitializeVM(); 324 Isolate* isolate = CcTest::i_isolate(); 325 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); 326 EternalHandles* eternal_handles = isolate->eternal_handles(); 327 328 // Create a number of handles that will not be on a block boundary 329 const int kArrayLength = 2048-1; 330 int indices[kArrayLength]; 331 v8::Eternal<v8::Value> eternals[kArrayLength]; 332 333 CHECK_EQ(0, eternal_handles->NumberOfHandles()); 334 for (int i = 0; i < kArrayLength; i++) { 335 indices[i] = -1; 336 HandleScope scope(isolate); 337 v8::Local<v8::Object> object = v8::Object::New(); 338 object->Set(i, v8::Integer::New(i, v8_isolate)); 339 // Create with internal api 340 eternal_handles->Create( 341 isolate, *v8::Utils::OpenHandle(*object), &indices[i]); 342 // Create with external api 343 CHECK(eternals[i].IsEmpty()); 344 eternals[i].Set(v8_isolate, object); 345 CHECK(!eternals[i].IsEmpty()); 346 } 347 348 isolate->heap()->CollectAllAvailableGarbage(); 349 350 for (int i = 0; i < kArrayLength; i++) { 351 for (int j = 0; j < 2; j++) { 352 HandleScope scope(isolate); 353 v8::Local<v8::Value> local; 354 if (j == 0) { 355 // Test internal api 356 local = v8::Utils::ToLocal(eternal_handles->Get(indices[i])); 357 } else { 358 // Test external api 359 local = eternals[i].Get(v8_isolate); 360 } 361 v8::Local<v8::Object> object = v8::Handle<v8::Object>::Cast(local); 362 v8::Local<v8::Value> value = object->Get(i); 363 CHECK(value->IsInt32()); 364 CHECK_EQ(i, value->Int32Value()); 365 } 366 } 367 368 CHECK_EQ(2*kArrayLength, eternal_handles->NumberOfHandles()); 369 370 // Create an eternal via the constructor 371 { 372 HandleScope scope(isolate); 373 v8::Local<v8::Object> object = v8::Object::New(); 374 v8::Eternal<v8::Object> eternal(v8_isolate, object); 375 CHECK(!eternal.IsEmpty()); 376 CHECK(object == eternal.Get(v8_isolate)); 377 } 378 379 CHECK_EQ(2*kArrayLength + 1, eternal_handles->NumberOfHandles()); 380 } 381