test-global-handles.cc 16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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 37 38


static List<Object*> skippable_objects;
static List<Object*> can_skip_called_objects;

39

40 41 42 43 44
static bool CanSkipCallback(Heap* heap, Object** pointer) {
  can_skip_called_objects.Add(*pointer);
  return skippable_objects.Contains(*pointer);
}

45

46 47 48 49 50
static void ResetCanSkipData() {
  skippable_objects.Clear();
  can_skip_called_objects.Clear();
}

51

52 53 54 55 56 57 58
class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
 public:
  TestRetainedObjectInfo() : has_been_disposed_(false) {}

  bool has_been_disposed() { return has_been_disposed_; }

  virtual void Dispose() {
59
    CHECK(!has_been_disposed_);
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    has_been_disposed_ = true;
  }

  virtual bool IsEquivalent(v8::RetainedObjectInfo* other) {
    return other == this;
  }

  virtual intptr_t GetHash() { return 0; }

  virtual const char* GetLabel() { return "whatever"; }

 private:
  bool has_been_disposed_;
};

75

76 77
class TestObjectVisitor : public ObjectVisitor {
 public:
78
  void VisitPointers(Object** start, Object** end) override {
79 80 81 82 83 84 85
    for (Object** o = start; o != end; ++o)
      visited.Add(*o);
  }

  List<Object*> visited;
};

86

87 88
TEST(IterateObjectGroupsOldApi) {
  CcTest::InitializeVM();
89 90
  Isolate* isolate = CcTest::i_isolate();
  GlobalHandles* global_handles = isolate->global_handles();
91 92 93
  v8::HandleScope handle_scope(CcTest::isolate());

  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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

  TestRetainedObjectInfo info1;
  TestRetainedObjectInfo info2;
  {
    Object** g1_objects[] = { g1s1.location(), g1s2.location() };
    Object** g2_objects[] = { g2s1.location(), g2s2.location() };

    global_handles->AddObjectGroup(g1_objects, 2, &info1);
    global_handles->AddObjectGroup(g2_objects, 2, &info2);
  }

  // Iterate the object groups. First skip all.
  {
    ResetCanSkipData();
    skippable_objects.Add(*g1s1.location());
    skippable_objects.Add(*g1s2.location());
    skippable_objects.Add(*g2s1.location());
    skippable_objects.Add(*g2s2.location());
    TestObjectVisitor visitor;
    global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);

    // CanSkipCallback was called for all objects.
124 125 126 127 128
    CHECK(can_skip_called_objects.length() == 4);
    CHECK(can_skip_called_objects.Contains(*g1s1.location()));
    CHECK(can_skip_called_objects.Contains(*g1s2.location()));
    CHECK(can_skip_called_objects.Contains(*g2s1.location()));
    CHECK(can_skip_called_objects.Contains(*g2s2.location()));
129 130

    // Nothing was visited.
131 132 133
    CHECK(visitor.visited.length() == 0);
    CHECK(!info1.has_been_disposed());
    CHECK(!info2.has_been_disposed());
134 135 136 137 138 139 140 141 142 143 144 145 146 147
  }

  // Iterate again, now only skip the second object group.
  {
    ResetCanSkipData();
    // The first grough should still be visited, since only one object is
    // skipped.
    skippable_objects.Add(*g1s1.location());
    skippable_objects.Add(*g2s1.location());
    skippable_objects.Add(*g2s2.location());
    TestObjectVisitor visitor;
    global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);

    // CanSkipCallback was called for all objects.
148 149 150 151 152
    CHECK(can_skip_called_objects.length() == 3 ||
          can_skip_called_objects.length() == 4);
    CHECK(can_skip_called_objects.Contains(*g1s2.location()));
    CHECK(can_skip_called_objects.Contains(*g2s1.location()));
    CHECK(can_skip_called_objects.Contains(*g2s2.location()));
153 154

    // The first group was visited.
155 156 157 158 159
    CHECK(visitor.visited.length() == 2);
    CHECK(visitor.visited.Contains(*g1s1.location()));
    CHECK(visitor.visited.Contains(*g1s2.location()));
    CHECK(info1.has_been_disposed());
    CHECK(!info2.has_been_disposed());
160 161 162 163 164 165 166 167 168
  }

  // Iterate again, don't skip anything.
  {
    ResetCanSkipData();
    TestObjectVisitor visitor;
    global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);

    // CanSkipCallback was called for all objects.
169 170 171
    CHECK(can_skip_called_objects.length() == 1);
    CHECK(can_skip_called_objects.Contains(*g2s1.location()) ||
          can_skip_called_objects.Contains(*g2s2.location()));
172 173

    // The second group was visited.
174 175 176 177
    CHECK(visitor.visited.length() == 2);
    CHECK(visitor.visited.Contains(*g2s1.location()));
    CHECK(visitor.visited.Contains(*g2s2.location()));
    CHECK(info2.has_been_disposed());
178 179
  }
}
180 181 182 183


TEST(IterateObjectGroups) {
  CcTest::InitializeVM();
184 185
  Isolate* isolate = CcTest::i_isolate();
  GlobalHandles* global_handles = isolate->global_handles();
186 187 188 189

  v8::HandleScope handle_scope(CcTest::isolate());

  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 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

  TestRetainedObjectInfo info1;
  TestRetainedObjectInfo info2;
  global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2));
  global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2));
  global_handles->SetRetainedObjectInfo(UniqueId(2), &info2);
  global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1));
  global_handles->SetObjectGroupId(g1s2.location(), UniqueId(1));
  global_handles->SetRetainedObjectInfo(UniqueId(1), &info1);

  // Iterate the object groups. First skip all.
  {
    ResetCanSkipData();
    skippable_objects.Add(*g1s1.location());
    skippable_objects.Add(*g1s2.location());
    skippable_objects.Add(*g2s1.location());
    skippable_objects.Add(*g2s2.location());
    TestObjectVisitor visitor;
    global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);

    // CanSkipCallback was called for all objects.
219 220 221 222 223
    CHECK(can_skip_called_objects.length() == 4);
    CHECK(can_skip_called_objects.Contains(*g1s1.location()));
    CHECK(can_skip_called_objects.Contains(*g1s2.location()));
    CHECK(can_skip_called_objects.Contains(*g2s1.location()));
    CHECK(can_skip_called_objects.Contains(*g2s2.location()));
224 225

    // Nothing was visited.
226 227 228
    CHECK(visitor.visited.length() == 0);
    CHECK(!info1.has_been_disposed());
    CHECK(!info2.has_been_disposed());
229 230 231 232 233 234 235 236 237 238 239 240 241 242
  }

  // Iterate again, now only skip the second object group.
  {
    ResetCanSkipData();
    // The first grough should still be visited, since only one object is
    // skipped.
    skippable_objects.Add(*g1s1.location());
    skippable_objects.Add(*g2s1.location());
    skippable_objects.Add(*g2s2.location());
    TestObjectVisitor visitor;
    global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);

    // CanSkipCallback was called for all objects.
243 244 245 246 247
    CHECK(can_skip_called_objects.length() == 3 ||
          can_skip_called_objects.length() == 4);
    CHECK(can_skip_called_objects.Contains(*g1s2.location()));
    CHECK(can_skip_called_objects.Contains(*g2s1.location()));
    CHECK(can_skip_called_objects.Contains(*g2s2.location()));
248 249

    // The first group was visited.
250 251 252 253 254
    CHECK(visitor.visited.length() == 2);
    CHECK(visitor.visited.Contains(*g1s1.location()));
    CHECK(visitor.visited.Contains(*g1s2.location()));
    CHECK(info1.has_been_disposed());
    CHECK(!info2.has_been_disposed());
255 256 257 258 259 260 261 262 263
  }

  // Iterate again, don't skip anything.
  {
    ResetCanSkipData();
    TestObjectVisitor visitor;
    global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);

    // CanSkipCallback was called for all objects.
264 265 266
    CHECK(can_skip_called_objects.length() == 1);
    CHECK(can_skip_called_objects.Contains(*g2s1.location()) ||
          can_skip_called_objects.Contains(*g2s2.location()));
267 268

    // The second group was visited.
269 270 271 272
    CHECK(visitor.visited.length() == 2);
    CHECK(visitor.visited.Contains(*g2s1.location()));
    CHECK(visitor.visited.Contains(*g2s2.location()));
    CHECK(info2.has_been_disposed());
273 274 275 276 277 278
  }
}


TEST(ImplicitReferences) {
  CcTest::InitializeVM();
279 280
  Isolate* isolate = CcTest::i_isolate();
  GlobalHandles* global_handles = isolate->global_handles();
281 282 283 284

  v8::HandleScope handle_scope(CcTest::isolate());

  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 300 301 302 303 304 305 306 307 308

  global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1));
  global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2));
  global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2));
  global_handles->SetReferenceFromGroup(UniqueId(1), g1c1.location());
  global_handles->SetReferenceFromGroup(UniqueId(1), g1c2.location());
  global_handles->SetReferenceFromGroup(UniqueId(2), g2c1.location());

  List<ImplicitRefGroup*>* implicit_refs =
      global_handles->implicit_ref_groups();
  USE(implicit_refs);
309 310 311 312 313 314 315 316 317 318
  CHECK(implicit_refs->length() == 2);
  CHECK(implicit_refs->at(0)->parent ==
        reinterpret_cast<HeapObject**>(g1s1.location()));
  CHECK(implicit_refs->at(0)->length == 2);
  CHECK(implicit_refs->at(0)->children[0] == g1c1.location());
  CHECK(implicit_refs->at(0)->children[1] == g1c2.location());
  CHECK(implicit_refs->at(1)->parent ==
        reinterpret_cast<HeapObject**>(g2s1.location()));
  CHECK(implicit_refs->at(1)->length == 1);
  CHECK(implicit_refs->at(1)->children[0] == g2c1.location());
319 320
  global_handles->RemoveObjectGroups();
  global_handles->RemoveImplicitRefGroups();
321
}
322 323


324 325
TEST(EternalHandles) {
  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 331 332

  // Create a number of handles that will not be on a block boundary
  const int kArrayLength = 2048-1;
  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 341 342
    object->Set(v8_isolate->GetCurrentContext(), i,
                v8::Integer::New(v8_isolate, i))
        .FromJust();
343 344 345 346 347
    // Create with internal api
    eternal_handles->Create(
        isolate, *v8::Utils::OpenHandle(*object), &indices[i]);
    // Create with external api
    CHECK(eternals[i].IsEmpty());
348 349
    eternals[i].Set(v8_isolate, object);
    CHECK(!eternals[i].IsEmpty());
350 351 352 353 354
  }

  isolate->heap()->CollectAllAvailableGarbage();

  for (int i = 0; i < kArrayLength; i++) {
355 356
    for (int j = 0; j < 2; j++) {
      HandleScope scope(isolate);
357
      v8::Local<v8::Value> local;
358 359
      if (j == 0) {
        // Test internal api
360
        local = v8::Utils::ToLocal(eternal_handles->Get(indices[i]));
361 362
      } else {
        // Test external api
363
        local = eternals[i].Get(v8_isolate);
364
      }
365 366 367
      v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(local);
      v8::Local<v8::Value> value =
          object->Get(v8_isolate->GetCurrentContext(), i).ToLocalChecked();
368
      CHECK(value->IsInt32());
369 370
      CHECK_EQ(i,
               value->Int32Value(v8_isolate->GetCurrentContext()).FromJust());
371
    }
372 373
  }

374 375 376 377 378
  CHECK_EQ(2*kArrayLength, eternal_handles->NumberOfHandles());

  // Create an eternal via the constructor
  {
    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 383 384 385
    CHECK(object == eternal.Get(v8_isolate));
  }

  CHECK_EQ(2*kArrayLength + 1, eternal_handles->NumberOfHandles());
386
}
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403


TEST(PersistentBaseGetLocal) {
  CcTest::InitializeVM();
  v8::Isolate* isolate = CcTest::isolate();

  v8::HandleScope scope(isolate);
  v8::Local<v8::Object> o = v8::Object::New(isolate);
  CHECK(!o.IsEmpty());
  v8::Persistent<v8::Object> p(isolate, o);
  CHECK(o == p.Get(isolate));
  CHECK(v8::Local<v8::Object>::New(isolate, p) == p.Get(isolate));

  v8::Global<v8::Object> g(isolate, o);
  CHECK(o == g.Get(isolate));
  CHECK(v8::Local<v8::Object>::New(isolate, g) == g.Get(isolate));
}
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419


void WeakCallback(const v8::WeakCallbackInfo<void>& data) {}


TEST(WeakPersistentSmi) {
  CcTest::InitializeVM();
  v8::Isolate* isolate = CcTest::isolate();

  v8::HandleScope scope(isolate);
  v8::Local<v8::Number> n = v8::Number::New(isolate, 0);
  v8::Global<v8::Number> g(isolate, n);

  // Should not crash.
  g.SetWeak<void>(nullptr, &WeakCallback, v8::WeakCallbackType::kParameter);
}
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452

void finalizer(const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
  data.GetParameter()->ClearWeak();
  v8::Local<v8::Object> o =
      v8::Local<v8::Object>::New(data.GetIsolate(), *data.GetParameter());
  o->Set(data.GetIsolate()->GetCurrentContext(), v8_str("finalizer"),
         v8_str("was here"))
      .FromJust();
}

TEST(FinalizerWeakness) {
  CcTest::InitializeVM();
  v8::Isolate* isolate = CcTest::isolate();

  v8::Global<v8::Object> g;
  int identity;

  {
    v8::HandleScope scope(isolate);
    v8::Local<v8::Object> o = v8::Object::New(isolate);
    identity = o->GetIdentityHash();
    g.Reset(isolate, o);
    g.SetWeak(&g, finalizer, v8::WeakCallbackType::kFinalizer);
  }

  CcTest::i_isolate()->heap()->CollectAllAvailableGarbage();

  CHECK(!g.IsEmpty());
  v8::HandleScope scope(isolate);
  v8::Local<v8::Object> o = v8::Local<v8::Object>::New(isolate, g);
  CHECK_EQ(identity, o->GetIdentityHash());
  CHECK(o->Has(isolate->GetCurrentContext(), v8_str("finalizer")).FromJust());
}
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471

TEST(PhatomHandlesWithoutCallbacks) {
  CcTest::InitializeVM();
  v8::Isolate* isolate = CcTest::isolate();

  v8::Global<v8::Object> g1, g2;
  {
    v8::HandleScope scope(isolate);
    g1.Reset(isolate, v8::Object::New(isolate));
    g1.SetWeak();
    g2.Reset(isolate, v8::Object::New(isolate));
    g2.SetWeak();
  }

  CHECK_EQ(0, isolate->NumberOfPhantomHandleResetsSinceLastCall());
  CcTest::i_isolate()->heap()->CollectAllAvailableGarbage();
  CHECK_EQ(2, isolate->NumberOfPhantomHandleResetsSinceLastCall());
  CHECK_EQ(0, isolate->NumberOfPhantomHandleResetsSinceLastCall());
}