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 = Isolate::Current()->global_handles(); 90 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 = Isolate::Current()->global_handles(); 185 186 v8::HandleScope handle_scope(CcTest::isolate()); 187 188 Handle<Object> g1s1 = 189 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 190 Handle<Object> g1s2 = 191 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 192 193 Handle<Object> g2s1 = 194 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 195 Handle<Object> g2s2 = 196 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 197 198 TestRetainedObjectInfo info1; 199 TestRetainedObjectInfo info2; 200 global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2)); 201 global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2)); 202 global_handles->SetRetainedObjectInfo(UniqueId(2), &info2); 203 global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1)); 204 global_handles->SetObjectGroupId(g1s2.location(), UniqueId(1)); 205 global_handles->SetRetainedObjectInfo(UniqueId(1), &info1); 206 207 // Iterate the object groups. First skip all. 208 { 209 ResetCanSkipData(); 210 skippable_objects.Add(*g1s1.location()); 211 skippable_objects.Add(*g1s2.location()); 212 skippable_objects.Add(*g2s1.location()); 213 skippable_objects.Add(*g2s2.location()); 214 TestObjectVisitor visitor; 215 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 216 217 // CanSkipCallback was called for all objects. 218 ASSERT(can_skip_called_objects.length() == 4); 219 ASSERT(can_skip_called_objects.Contains(*g1s1.location())); 220 ASSERT(can_skip_called_objects.Contains(*g1s2.location())); 221 ASSERT(can_skip_called_objects.Contains(*g2s1.location())); 222 ASSERT(can_skip_called_objects.Contains(*g2s2.location())); 223 224 // Nothing was visited. 225 ASSERT(visitor.visited.length() == 0); 226 ASSERT(!info1.has_been_disposed()); 227 ASSERT(!info2.has_been_disposed()); 228 } 229 230 // Iterate again, now only skip the second object group. 231 { 232 ResetCanSkipData(); 233 // The first grough should still be visited, since only one object is 234 // skipped. 235 skippable_objects.Add(*g1s1.location()); 236 skippable_objects.Add(*g2s1.location()); 237 skippable_objects.Add(*g2s2.location()); 238 TestObjectVisitor visitor; 239 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 240 241 // CanSkipCallback was called for all objects. 242 ASSERT(can_skip_called_objects.length() == 3 || 243 can_skip_called_objects.length() == 4); 244 ASSERT(can_skip_called_objects.Contains(*g1s2.location())); 245 ASSERT(can_skip_called_objects.Contains(*g2s1.location())); 246 ASSERT(can_skip_called_objects.Contains(*g2s2.location())); 247 248 // The first group was visited. 249 ASSERT(visitor.visited.length() == 2); 250 ASSERT(visitor.visited.Contains(*g1s1.location())); 251 ASSERT(visitor.visited.Contains(*g1s2.location())); 252 ASSERT(info1.has_been_disposed()); 253 ASSERT(!info2.has_been_disposed()); 254 } 255 256 // Iterate again, don't skip anything. 257 { 258 ResetCanSkipData(); 259 TestObjectVisitor visitor; 260 global_handles->IterateObjectGroups(&visitor, &CanSkipCallback); 261 262 // CanSkipCallback was called for all objects. 263 ASSERT(can_skip_called_objects.length() == 1); 264 ASSERT(can_skip_called_objects.Contains(*g2s1.location()) || 265 can_skip_called_objects.Contains(*g2s2.location())); 266 267 // The second group was visited. 268 ASSERT(visitor.visited.length() == 2); 269 ASSERT(visitor.visited.Contains(*g2s1.location())); 270 ASSERT(visitor.visited.Contains(*g2s2.location())); 271 ASSERT(info2.has_been_disposed()); 272 } 273 } 274 275 276 TEST(ImplicitReferences) { 277 CcTest::InitializeVM(); 278 GlobalHandles* global_handles = Isolate::Current()->global_handles(); 279 280 v8::HandleScope handle_scope(CcTest::isolate()); 281 282 Handle<Object> g1s1 = 283 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 284 Handle<Object> g1c1 = 285 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 286 Handle<Object> g1c2 = 287 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 288 289 290 Handle<Object> g2s1 = 291 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 292 Handle<Object> g2s2 = 293 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 294 Handle<Object> g2c1 = 295 global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked()); 296 297 global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1)); 298 global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2)); 299 global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2)); 300 global_handles->SetReferenceFromGroup(UniqueId(1), g1c1.location()); 301 global_handles->SetReferenceFromGroup(UniqueId(1), g1c2.location()); 302 global_handles->SetReferenceFromGroup(UniqueId(2), g2c1.location()); 303 304 List<ImplicitRefGroup*>* implicit_refs = 305 global_handles->implicit_ref_groups(); 306 USE(implicit_refs); 307 ASSERT(implicit_refs->length() == 2); 308 ASSERT(implicit_refs->at(0)->parent == 309 reinterpret_cast<HeapObject**>(g1s1.location())); 310 ASSERT(implicit_refs->at(0)->length == 2); 311 ASSERT(implicit_refs->at(0)->children[0] == g1c1.location()); 312 ASSERT(implicit_refs->at(0)->children[1] == g1c2.location()); 313 ASSERT(implicit_refs->at(1)->parent == 314 reinterpret_cast<HeapObject**>(g2s1.location())); 315 ASSERT(implicit_refs->at(1)->length == 1); 316 ASSERT(implicit_refs->at(1)->children[0] == g2c1.location()); 317 } 318 319 320 TEST(EternalHandles) { 321 CcTest::InitializeVM(); 322 Isolate* isolate = Isolate::Current(); 323 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); 324 EternalHandles* eternals = isolate->eternal_handles(); 325 326 // Create a number of handles that will not be on a block boundary 327 const int kArrayLength = 2048-1; 328 int indices[kArrayLength]; 329 330 CHECK_EQ(0, eternals->NumberOfHandles()); 331 for (int i = 0; i < kArrayLength; i++) { 332 HandleScope scope(isolate); 333 v8::Local<v8::Object> object = v8::Object::New(); 334 object->Set(i, v8::Integer::New(i, v8_isolate)); 335 if (i % 2 == 0) { 336 // Create with internal api 337 indices[i] = eternals->Create(isolate, *v8::Utils::OpenHandle(*object)); 338 } else { 339 // Create with external api 340 indices[i] = object.Eternalize(v8_isolate); 341 } 342 } 343 344 isolate->heap()->CollectAllAvailableGarbage(); 345 346 for (int i = 0; i < kArrayLength; i++) { 347 for (int j = 0; j < 2; j++) { 348 HandleScope scope(isolate); 349 v8::Local<v8::Object> object; 350 if (j == 0) { 351 // Test internal api 352 v8::Local<v8::Value> local = 353 v8::Utils::ToLocal(eternals->Get(indices[i])); 354 object = v8::Handle<v8::Object>::Cast(local); 355 } else { 356 // Test external api 357 object = v8::Local<v8::Object>::GetEternal(v8_isolate, indices[i]); 358 } 359 v8::Local<v8::Value> value = object->Get(i); 360 CHECK(value->IsInt32()); 361 CHECK_EQ(i, value->Int32Value()); 362 } 363 } 364 365 CHECK_EQ(kArrayLength, eternals->NumberOfHandles()); 366 } 367