test-js-weak-refs.cc 38.1 KB
Newer Older
1 2 3 4
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#include "src/execution/isolate.h"
#include "src/execution/microtask-queue.h"
7
#include "src/handles/handles-inl.h"
8
#include "src/heap/factory-inl.h"
9
#include "src/heap/heap-inl.h"
10
#include "src/objects/js-objects.h"
11
#include "src/objects/js-weak-refs-inl.h"
12
#include "test/cctest/cctest.h"
13
#include "test/cctest/heap/heap-utils.h"
14 15 16 17

namespace v8 {
namespace internal {

18 19
namespace {

20 21
Handle<JSFinalizationRegistry> ConstructJSFinalizationRegistry(
    Isolate* isolate) {
22
  Factory* factory = isolate->factory();
23
  Handle<String> finalization_registry_name =
24
      factory->NewStringFromStaticChars("FinalizationRegistry");
25 26
  Handle<Object> global =
      handle(isolate->native_context()->global_object(), isolate);
27 28
  Handle<JSFunction> finalization_registry_fun = Handle<JSFunction>::cast(
      Object::GetProperty(isolate, global, finalization_registry_name)
29
          .ToHandleChecked());
30 31
  auto finalization_registry = Handle<JSFinalizationRegistry>::cast(
      JSObject::New(finalization_registry_fun, finalization_registry_fun,
32 33 34
                    Handle<AllocationSite>::null())
          .ToHandleChecked());
#ifdef VERIFY_HEAP
35
  finalization_registry->JSFinalizationRegistryVerify(isolate);
36
#endif  // VERIFY_HEAP
37
  return finalization_registry;
38 39
}

40 41
Handle<JSWeakRef> ConstructJSWeakRef(Handle<JSReceiver> target,
                                     Isolate* isolate) {
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
  Factory* factory = isolate->factory();
  Handle<String> weak_ref_name = factory->WeakRef_string();
  Handle<Object> global =
      handle(isolate->native_context()->global_object(), isolate);
  Handle<JSFunction> weak_ref_fun = Handle<JSFunction>::cast(
      Object::GetProperty(isolate, global, weak_ref_name).ToHandleChecked());
  auto weak_ref = Handle<JSWeakRef>::cast(
      JSObject::New(weak_ref_fun, weak_ref_fun, Handle<AllocationSite>::null())
          .ToHandleChecked());
  weak_ref->set_target(*target);
#ifdef VERIFY_HEAP
  weak_ref->JSWeakRefVerify(isolate);
#endif  // VERIFY_HEAP
  return weak_ref;
}

58 59 60 61 62 63 64 65 66 67 68
Handle<JSObject> CreateKey(const char* key_prop_value, Isolate* isolate) {
  Factory* factory = isolate->factory();
  Handle<String> key_string = factory->NewStringFromStaticChars("key_string");
  Handle<JSObject> key =
      isolate->factory()->NewJSObject(isolate->object_function());
  JSObject::AddProperty(isolate, key, key_string,
                        factory->NewStringFromAsciiChecked(key_prop_value),
                        NONE);
  return key;
}

69 70
Handle<WeakCell> FinalizationRegistryRegister(
    Handle<JSFinalizationRegistry> finalization_registry,
71 72 73 74 75 76 77 78 79 80 81
    Handle<JSObject> target, Handle<Object> held_value,
    Handle<Object> unregister_token, Isolate* isolate) {
  Factory* factory = isolate->factory();
  Handle<JSFunction> regfunc = Handle<JSFunction>::cast(
      Object::GetProperty(isolate, finalization_registry,
                          factory->NewStringFromStaticChars("register"))
          .ToHandleChecked());
  Handle<Object> args[] = {target, held_value, unregister_token};
  Execution::Call(isolate, regfunc, finalization_registry, arraysize(args),
                  args)
      .ToHandleChecked();
82
  CHECK(finalization_registry->active_cells().IsWeakCell());
83
  Handle<WeakCell> weak_cell =
84
      handle(WeakCell::cast(finalization_registry->active_cells()), isolate);
85
#ifdef VERIFY_HEAP
86
  weak_cell->WeakCellVerify(isolate);
87 88 89 90
#endif  // VERIFY_HEAP
  return weak_cell;
}

91 92 93
Handle<WeakCell> FinalizationRegistryRegister(
    Handle<JSFinalizationRegistry> finalization_registry,
    Handle<JSObject> target, Isolate* isolate) {
94 95
  Handle<Object> undefined =
      handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
96 97
  return FinalizationRegistryRegister(finalization_registry, target, undefined,
                                      undefined, isolate);
98 99 100
}

void NullifyWeakCell(Handle<WeakCell> weak_cell, Isolate* isolate) {
101
  auto empty_func = [](HeapObject object, ObjectSlot slot, Object target) {};
102 103
  weak_cell->Nullify(isolate, empty_func);
#ifdef VERIFY_HEAP
104
  weak_cell->WeakCellVerify(isolate);
105 106 107
#endif  // VERIFY_HEAP
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
Object PopClearedCellHoldings(
    Handle<JSFinalizationRegistry> finalization_registry, Isolate* isolate) {
  // PopClearedCell is implemented in Torque. Reproduce that implementation here
  // for testing.
  Handle<WeakCell> weak_cell =
      handle(WeakCell::cast(finalization_registry->cleared_cells()), isolate);
  DCHECK(weak_cell->prev().IsUndefined(isolate));
  finalization_registry->set_cleared_cells(weak_cell->next());
  weak_cell->set_next(ReadOnlyRoots(isolate).undefined_value());

  if (finalization_registry->cleared_cells().IsWeakCell()) {
    WeakCell cleared_cells_head =
        WeakCell::cast(finalization_registry->cleared_cells());
    DCHECK_EQ(cleared_cells_head.prev(), *weak_cell);
    cleared_cells_head.set_prev(ReadOnlyRoots(isolate).undefined_value());
  } else {
    DCHECK(finalization_registry->cleared_cells().IsUndefined(isolate));
  }

  if (!weak_cell->unregister_token().IsUndefined(isolate)) {
    JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap(
        isolate, finalization_registry->ptr(), weak_cell->ptr());
  }

  return weak_cell->holdings();
}

135 136 137 138 139 140 141 142 143 144
// Usage: VerifyWeakCellChain(isolate, list_head, n, cell1, cell2, ..., celln);
// verifies that list_head == cell1 and cell1, cell2, ..., celln. form a list.
void VerifyWeakCellChain(Isolate* isolate, Object list_head, int n_args, ...) {
  CHECK_GE(n_args, 0);

  va_list args;
  va_start(args, n_args);

  if (n_args == 0) {
    // Verify empty list
145
    CHECK(list_head.IsUndefined(isolate));
146 147 148
  } else {
    WeakCell current = WeakCell::cast(Object(va_arg(args, Address)));
    CHECK_EQ(current, list_head);
149
    CHECK(current.prev().IsUndefined(isolate));
150 151 152

    for (int i = 1; i < n_args; i++) {
      WeakCell next = WeakCell::cast(Object(va_arg(args, Address)));
153 154
      CHECK_EQ(current.next(), next);
      CHECK_EQ(next.prev(), current);
155 156
      current = next;
    }
157
    CHECK(current.next().IsUndefined(isolate));
158 159 160 161 162 163
  }
  va_end(args);
}

// Like VerifyWeakCellChain but verifies the chain created with key_list_prev
// and key_list_next instead of prev and next.
164 165
void VerifyWeakCellKeyChain(Isolate* isolate, SimpleNumberDictionary key_map,
                            Object unregister_token, int n_args, ...) {
166 167 168 169 170
  CHECK_GE(n_args, 0);

  va_list args;
  va_start(args, n_args);

171 172 173 174 175 176
  Object hash = unregister_token.GetHash();
  InternalIndex entry = InternalIndex::NotFound();
  if (!hash.IsUndefined(isolate)) {
    uint32_t key = Smi::ToInt(hash);
    entry = key_map.FindEntry(isolate, key);
  }
177 178
  if (n_args == 0) {
    // Verify empty list
179
    CHECK(entry.is_not_found());
180
  } else {
181
    CHECK(entry.is_found());
182
    WeakCell current = WeakCell::cast(Object(va_arg(args, Address)));
183
    Object list_head = key_map.ValueAt(entry);
184
    CHECK_EQ(current, list_head);
185
    CHECK(current.key_list_prev().IsUndefined(isolate));
186 187 188

    for (int i = 1; i < n_args; i++) {
      WeakCell next = WeakCell::cast(Object(va_arg(args, Address)));
189 190
      CHECK_EQ(current.key_list_next(), next);
      CHECK_EQ(next.key_list_prev(), current);
191 192
      current = next;
    }
193
    CHECK(current.key_list_next().IsUndefined(isolate));
194 195
  }
  va_end(args);
196 197
}

198 199 200 201 202 203 204 205 206 207 208
Handle<JSWeakRef> MakeWeakRefAndKeepDuringJob(Isolate* isolate) {
  HandleScope inner_scope(isolate);

  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());
  Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
  isolate->heap()->KeepDuringJob(js_object);

  return inner_scope.CloseAndEscape(inner_weak_ref);
}

209 210
}  // namespace

211
TEST(TestRegister) {
212 213 214 215 216
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
217 218
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
219 220 221
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());

222 223
  // Register a weak reference and verify internal data structures.
  Handle<WeakCell> weak_cell1 =
224
      FinalizationRegistryRegister(finalization_registry, js_object, isolate);
225

226
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 1,
227
                      *weak_cell1);
228 229
  CHECK(weak_cell1->key_list_prev().IsUndefined(isolate));
  CHECK(weak_cell1->key_list_next().IsUndefined(isolate));
230

231
  CHECK(finalization_registry->cleared_cells().IsUndefined(isolate));
232 233

  // No key was used during registration, key-based map stays uninitialized.
234
  CHECK(finalization_registry->key_map().IsUndefined(isolate));
235 236 237

  // Register another weak reference and verify internal data structures.
  Handle<WeakCell> weak_cell2 =
238
      FinalizationRegistryRegister(finalization_registry, js_object, isolate);
239

240
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 2,
241
                      *weak_cell2, *weak_cell1);
242 243
  CHECK(weak_cell2->key_list_prev().IsUndefined(isolate));
  CHECK(weak_cell2->key_list_next().IsUndefined(isolate));
244

245 246
  CHECK(finalization_registry->cleared_cells().IsUndefined(isolate));
  CHECK(finalization_registry->key_map().IsUndefined(isolate));
247 248
}

249
TEST(TestRegisterWithKey) {
250 251 252 253 254
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
255 256
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
257 258 259
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());

260 261
  Handle<JSObject> token1 = CreateKey("token1", isolate);
  Handle<JSObject> token2 = CreateKey("token2", isolate);
262 263 264 265
  Handle<Object> undefined =
      handle(ReadOnlyRoots(isolate).undefined_value(), isolate);

  // Register a weak reference with a key and verify internal data structures.
266 267
  Handle<WeakCell> weak_cell1 = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
268 269

  {
270
    SimpleNumberDictionary key_map =
271
        SimpleNumberDictionary::cast(finalization_registry->key_map());
272 273
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 0);
274 275 276 277
  }

  // Register another weak reference with a different key and verify internal
  // data structures.
278 279
  Handle<WeakCell> weak_cell2 = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token2, isolate);
280 281

  {
282
    SimpleNumberDictionary key_map =
283
        SimpleNumberDictionary::cast(finalization_registry->key_map());
284 285
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 1, *weak_cell2);
286 287
  }

288
  // Register another weak reference with token1 and verify internal data
289
  // structures.
290 291
  Handle<WeakCell> weak_cell3 = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
292

293
  {
294
    SimpleNumberDictionary key_map =
295
        SimpleNumberDictionary::cast(finalization_registry->key_map());
296
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell3,
297
                           *weak_cell1);
298
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 1, *weak_cell2);
299 300 301 302 303 304 305 306 307
  }
}

TEST(TestWeakCellNullify1) {
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
308 309
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
310 311 312 313
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());

  Handle<WeakCell> weak_cell1 =
314
      FinalizationRegistryRegister(finalization_registry, js_object, isolate);
315
  Handle<WeakCell> weak_cell2 =
316
      FinalizationRegistryRegister(finalization_registry, js_object, isolate);
317 318

  // Nullify the first WeakCell and verify internal data structures.
319
  NullifyWeakCell(weak_cell1, isolate);
320
  CHECK_EQ(finalization_registry->active_cells(), *weak_cell2);
321 322
  CHECK(weak_cell2->prev().IsUndefined(isolate));
  CHECK(weak_cell2->next().IsUndefined(isolate));
323
  CHECK_EQ(finalization_registry->cleared_cells(), *weak_cell1);
324 325
  CHECK(weak_cell1->prev().IsUndefined(isolate));
  CHECK(weak_cell1->next().IsUndefined(isolate));
326

327
  // Nullify the second WeakCell and verify internal data structures.
328
  NullifyWeakCell(weak_cell2, isolate);
329 330
  CHECK(finalization_registry->active_cells().IsUndefined(isolate));
  CHECK_EQ(finalization_registry->cleared_cells(), *weak_cell2);
331
  CHECK_EQ(weak_cell2->next(), *weak_cell1);
332
  CHECK(weak_cell2->prev().IsUndefined(isolate));
333
  CHECK_EQ(weak_cell1->prev(), *weak_cell2);
334
  CHECK(weak_cell1->next().IsUndefined(isolate));
335 336
}

337
TEST(TestWeakCellNullify2) {
338 339 340 341 342
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
343 344
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
345 346 347
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());

348
  Handle<WeakCell> weak_cell1 =
349
      FinalizationRegistryRegister(finalization_registry, js_object, isolate);
350
  Handle<WeakCell> weak_cell2 =
351
      FinalizationRegistryRegister(finalization_registry, js_object, isolate);
352

353
  // Like TestWeakCellNullify1 but nullify the WeakCells in opposite order.
354
  NullifyWeakCell(weak_cell2, isolate);
355
  CHECK_EQ(finalization_registry->active_cells(), *weak_cell1);
356 357
  CHECK(weak_cell1->prev().IsUndefined(isolate));
  CHECK(weak_cell1->next().IsUndefined(isolate));
358
  CHECK_EQ(finalization_registry->cleared_cells(), *weak_cell2);
359 360
  CHECK(weak_cell2->prev().IsUndefined(isolate));
  CHECK(weak_cell2->next().IsUndefined(isolate));
361 362

  NullifyWeakCell(weak_cell1, isolate);
363 364
  CHECK(finalization_registry->active_cells().IsUndefined(isolate));
  CHECK_EQ(finalization_registry->cleared_cells(), *weak_cell1);
365
  CHECK_EQ(weak_cell1->next(), *weak_cell2);
366
  CHECK(weak_cell1->prev().IsUndefined(isolate));
367
  CHECK_EQ(weak_cell2->prev(), *weak_cell1);
368
  CHECK(weak_cell2->next().IsUndefined(isolate));
369 370
}

371
TEST(TestJSFinalizationRegistryPopClearedCellHoldings1) {
372 373 374 375
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
376
  Factory* factory = isolate->factory();
377
  HandleScope outer_scope(isolate);
378 379
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
380 381
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());
382 383 384 385
  Handle<Object> undefined =
      handle(ReadOnlyRoots(isolate).undefined_value(), isolate);

  Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
386 387
  Handle<WeakCell> weak_cell1 = FinalizationRegistryRegister(
      finalization_registry, js_object, holdings1, undefined, isolate);
388
  Handle<Object> holdings2 = factory->NewStringFromAsciiChecked("holdings2");
389 390
  Handle<WeakCell> weak_cell2 = FinalizationRegistryRegister(
      finalization_registry, js_object, holdings2, undefined, isolate);
391
  Handle<Object> holdings3 = factory->NewStringFromAsciiChecked("holdings3");
392 393
  Handle<WeakCell> weak_cell3 = FinalizationRegistryRegister(
      finalization_registry, js_object, holdings3, undefined, isolate);
394 395 396 397

  NullifyWeakCell(weak_cell2, isolate);
  NullifyWeakCell(weak_cell3, isolate);

398
  CHECK(finalization_registry->NeedsCleanup());
399
  Object cleared1 = PopClearedCellHoldings(finalization_registry, isolate);
400
  CHECK_EQ(cleared1, *holdings3);
401 402
  CHECK(weak_cell3->prev().IsUndefined(isolate));
  CHECK(weak_cell3->next().IsUndefined(isolate));
403

404
  CHECK(finalization_registry->NeedsCleanup());
405
  Object cleared2 = PopClearedCellHoldings(finalization_registry, isolate);
406
  CHECK_EQ(cleared2, *holdings2);
407 408
  CHECK(weak_cell2->prev().IsUndefined(isolate));
  CHECK(weak_cell2->next().IsUndefined(isolate));
409

410
  CHECK(!finalization_registry->NeedsCleanup());
411 412 413

  NullifyWeakCell(weak_cell1, isolate);

414
  CHECK(finalization_registry->NeedsCleanup());
415
  Object cleared3 = PopClearedCellHoldings(finalization_registry, isolate);
416
  CHECK_EQ(cleared3, *holdings1);
417 418
  CHECK(weak_cell1->prev().IsUndefined(isolate));
  CHECK(weak_cell1->next().IsUndefined(isolate));
419

420 421 422
  CHECK(!finalization_registry->NeedsCleanup());
  CHECK(finalization_registry->active_cells().IsUndefined(isolate));
  CHECK(finalization_registry->cleared_cells().IsUndefined(isolate));
423 424
}

425
TEST(TestJSFinalizationRegistryPopClearedCellHoldings2) {
426 427
  // Test that when all WeakCells for a key are popped, the key is removed from
  // the key map.
428 429 430 431
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
432
  Factory* factory = isolate->factory();
433
  HandleScope outer_scope(isolate);
434 435
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
436 437
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());
438
  Handle<JSObject> token1 = CreateKey("token1", isolate);
439

440
  Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
441 442
  Handle<WeakCell> weak_cell1 = FinalizationRegistryRegister(
      finalization_registry, js_object, holdings1, token1, isolate);
443
  Handle<Object> holdings2 = factory->NewStringFromAsciiChecked("holdings2");
444 445
  Handle<WeakCell> weak_cell2 = FinalizationRegistryRegister(
      finalization_registry, js_object, holdings2, token1, isolate);
446

447 448
  NullifyWeakCell(weak_cell1, isolate);
  NullifyWeakCell(weak_cell2, isolate);
449

450 451 452
  // Nullifying doesn't affect the key chains (just moves WeakCells from
  // active_cells to cleared_cells).
  {
453
    SimpleNumberDictionary key_map =
454
        SimpleNumberDictionary::cast(finalization_registry->key_map());
455
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell2,
456 457
                           *weak_cell1);
  }
458

459
  Object cleared1 = PopClearedCellHoldings(finalization_registry, isolate);
460
  CHECK_EQ(cleared1, *holdings2);
461 462

  {
463
    SimpleNumberDictionary key_map =
464
        SimpleNumberDictionary::cast(finalization_registry->key_map());
465
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
466 467
  }

468
  Object cleared2 = PopClearedCellHoldings(finalization_registry, isolate);
469
  CHECK_EQ(cleared2, *holdings1);
470 471

  {
472
    SimpleNumberDictionary key_map =
473
        SimpleNumberDictionary::cast(finalization_registry->key_map());
474
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
475 476 477 478 479 480 481 482 483
  }
}

TEST(TestUnregisterActiveCells) {
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
484 485
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
486 487 488
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());

489 490
  Handle<JSObject> token1 = CreateKey("token1", isolate);
  Handle<JSObject> token2 = CreateKey("token2", isolate);
491 492 493
  Handle<Object> undefined =
      handle(ReadOnlyRoots(isolate).undefined_value(), isolate);

494 495 496 497
  Handle<WeakCell> weak_cell1a = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
  Handle<WeakCell> weak_cell1b = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
498

499 500 501 502
  Handle<WeakCell> weak_cell2a = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token2, isolate);
  Handle<WeakCell> weak_cell2b = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token2, isolate);
503

504
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 4,
505
                      *weak_cell2b, *weak_cell2a, *weak_cell1b, *weak_cell1a);
506
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 0);
507
  {
508
    SimpleNumberDictionary key_map =
509
        SimpleNumberDictionary::cast(finalization_registry->key_map());
510
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
511
                           *weak_cell1a);
512
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 2, *weak_cell2b,
513 514 515
                           *weak_cell2a);
  }

516
  JSFinalizationRegistry::Unregister(finalization_registry, token1, isolate);
517
  {
518
    SimpleNumberDictionary key_map =
519
        SimpleNumberDictionary::cast(finalization_registry->key_map());
520 521
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 2, *weak_cell2b,
522 523
                           *weak_cell2a);
  }
524

525
  // Both weak_cell1a and weak_cell1b removed from active_cells.
526
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 2,
527
                      *weak_cell2b, *weak_cell2a);
528
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 0);
529 530
}

531
TEST(TestUnregisterActiveAndClearedCells) {
532 533 534 535 536
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
537 538
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
539 540 541
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());

542 543
  Handle<JSObject> token1 = CreateKey("token1", isolate);
  Handle<JSObject> token2 = CreateKey("token2", isolate);
544 545
  Handle<Object> undefined =
      handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
546

547 548 549 550
  Handle<WeakCell> weak_cell1a = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
  Handle<WeakCell> weak_cell1b = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
551

552 553 554 555
  Handle<WeakCell> weak_cell2a = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token2, isolate);
  Handle<WeakCell> weak_cell2b = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token2, isolate);
556

557
  NullifyWeakCell(weak_cell2a, isolate);
558

559
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 3,
560
                      *weak_cell2b, *weak_cell1b, *weak_cell1a);
561
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 1,
562 563
                      *weak_cell2a);
  {
564
    SimpleNumberDictionary key_map =
565
        SimpleNumberDictionary::cast(finalization_registry->key_map());
566
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
567
                           *weak_cell1a);
568
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 2, *weak_cell2b,
569 570 571
                           *weak_cell2a);
  }

572
  JSFinalizationRegistry::Unregister(finalization_registry, token2, isolate);
573

574
  // Both weak_cell2a and weak_cell2b removed.
575
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 2,
576
                      *weak_cell1b, *weak_cell1a);
577
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 0);
578
  {
579
    SimpleNumberDictionary key_map =
580
        SimpleNumberDictionary::cast(finalization_registry->key_map());
581
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
582
                           *weak_cell1a);
583
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 0);
584
  }
585 586
}

587
TEST(TestWeakCellUnregisterTwice) {
588 589 590 591 592
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
593 594
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
595 596 597
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());

598
  Handle<JSObject> token1 = CreateKey("token1", isolate);
599 600 601
  Handle<Object> undefined =
      handle(ReadOnlyRoots(isolate).undefined_value(), isolate);

602 603
  Handle<WeakCell> weak_cell1 = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
604

605
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 1,
606
                      *weak_cell1);
607
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 0);
608
  {
609
    SimpleNumberDictionary key_map =
610
        SimpleNumberDictionary::cast(finalization_registry->key_map());
611
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
612 613
  }

614
  JSFinalizationRegistry::Unregister(finalization_registry, token1, isolate);
615

616 617
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 0);
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 0);
618
  {
619
    SimpleNumberDictionary key_map =
620
        SimpleNumberDictionary::cast(finalization_registry->key_map());
621
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
622 623
  }

624
  JSFinalizationRegistry::Unregister(finalization_registry, token1, isolate);
625

626 627
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 0);
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 0);
628
  {
629
    SimpleNumberDictionary key_map =
630
        SimpleNumberDictionary::cast(finalization_registry->key_map());
631
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
632
  }
633 634
}

635
TEST(TestWeakCellUnregisterPopped) {
636 637 638 639
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
640
  Factory* factory = isolate->factory();
641
  HandleScope outer_scope(isolate);
642 643
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
644 645
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());
646
  Handle<JSObject> token1 = CreateKey("token1", isolate);
647
  Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
648 649
  Handle<WeakCell> weak_cell1 = FinalizationRegistryRegister(
      finalization_registry, js_object, holdings1, token1, isolate);
650 651 652

  NullifyWeakCell(weak_cell1, isolate);

653
  CHECK(finalization_registry->NeedsCleanup());
654
  Object cleared1 = PopClearedCellHoldings(finalization_registry, isolate);
655 656
  CHECK_EQ(cleared1, *holdings1);

657 658
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 0);
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 0);
659
  {
660
    SimpleNumberDictionary key_map =
661
        SimpleNumberDictionary::cast(finalization_registry->key_map());
662
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
663 664
  }

665
  JSFinalizationRegistry::Unregister(finalization_registry, token1, isolate);
666

667 668
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 0);
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 0);
669
  {
670
    SimpleNumberDictionary key_map =
671
        SimpleNumberDictionary::cast(finalization_registry->key_map());
672
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
673 674 675 676 677 678 679 680 681
  }
}

TEST(TestWeakCellUnregisterNonexistentKey) {
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
682 683
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
684
  Handle<JSObject> token1 = CreateKey("token1", isolate);
685

686
  JSFinalizationRegistry::Unregister(finalization_registry, token1, isolate);
687 688
}

689 690 691 692 693 694 695 696 697 698 699 700 701 702
TEST(TestJSWeakRef) {
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;

  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
  Handle<JSWeakRef> weak_ref;
  {
    HandleScope inner_scope(isolate);

    Handle<JSObject> js_object =
        isolate->factory()->NewJSObject(isolate->object_function());
    // This doesn't add the target into the KeepDuringJob set.
703
    Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
704 705

    CcTest::CollectAllGarbage();
706
    CHECK(!inner_weak_ref->target().IsUndefined(isolate));
707 708 709 710

    weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
  }

711
  CHECK(!weak_ref->target().IsUndefined(isolate));
712 713 714

  CcTest::CollectAllGarbage();

715
  CHECK(weak_ref->target().IsUndefined(isolate));
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
}

TEST(TestJSWeakRefIncrementalMarking) {
  FLAG_harmony_weak_refs = true;
  if (!FLAG_incremental_marking) {
    return;
  }
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  LocalContext context;

  Isolate* isolate = CcTest::i_isolate();
  Heap* heap = isolate->heap();
  HandleScope outer_scope(isolate);
  Handle<JSWeakRef> weak_ref;
  {
    HandleScope inner_scope(isolate);

    Handle<JSObject> js_object =
        isolate->factory()->NewJSObject(isolate->object_function());
    // This doesn't add the target into the KeepDuringJob set.
737
    Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
738 739 740

    heap::SimulateIncrementalMarking(heap, true);
    CcTest::CollectAllGarbage();
741
    CHECK(!inner_weak_ref->target().IsUndefined(isolate));
742 743 744 745

    weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
  }

746
  CHECK(!weak_ref->target().IsUndefined(isolate));
747 748 749 750

  heap::SimulateIncrementalMarking(heap, true);
  CcTest::CollectAllGarbage();

751
  CHECK(weak_ref->target().IsUndefined(isolate));
752 753 754 755 756 757 758 759 760
}

TEST(TestJSWeakRefKeepDuringJob) {
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;

  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
761 762 763 764
  Handle<JSWeakRef> weak_ref = MakeWeakRefAndKeepDuringJob(isolate);
  CHECK(!weak_ref->target().IsUndefined(isolate));
  CcTest::CollectAllGarbage();
  CHECK(!weak_ref->target().IsUndefined(isolate));
765

766 767 768 769
  // Clears the KeepDuringJob set.
  context->GetIsolate()->ClearKeptObjects();
  CcTest::CollectAllGarbage();
  CHECK(weak_ref->target().IsUndefined(isolate));
770

771 772 773
  weak_ref = MakeWeakRefAndKeepDuringJob(isolate);
  CHECK(!weak_ref->target().IsUndefined(isolate));
  CcTest::CollectAllGarbage();
774
  CHECK(!weak_ref->target().IsUndefined(isolate));
775

776 777
  // ClearKeptObjects should be called by PerformMicrotasksCheckpoint.
  CcTest::isolate()->PerformMicrotaskCheckpoint();
778
  CcTest::CollectAllGarbage();
779
  CHECK(weak_ref->target().IsUndefined(isolate));
780

781
  weak_ref = MakeWeakRefAndKeepDuringJob(isolate);
782
  CHECK(!weak_ref->target().IsUndefined(isolate));
783
  CcTest::CollectAllGarbage();
784
  CHECK(!weak_ref->target().IsUndefined(isolate));
785

786 787 788
  // ClearKeptObjects should be called by MicrotasksScope::PerformCheckpoint.
  v8::MicrotasksScope::PerformCheckpoint(CcTest::isolate());
  CcTest::CollectAllGarbage();
789
  CHECK(weak_ref->target().IsUndefined(isolate));
790 791 792 793 794 795 796 797 798 799 800 801 802 803
}

TEST(TestJSWeakRefKeepDuringJobIncrementalMarking) {
  FLAG_harmony_weak_refs = true;
  if (!FLAG_incremental_marking) {
    return;
  }
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  LocalContext context;

  Isolate* isolate = CcTest::i_isolate();
  Heap* heap = isolate->heap();
  HandleScope outer_scope(isolate);
804
  Handle<JSWeakRef> weak_ref = MakeWeakRefAndKeepDuringJob(isolate);
805

806
  CHECK(!weak_ref->target().IsUndefined(isolate));
807 808 809 810

  heap::SimulateIncrementalMarking(heap, true);
  CcTest::CollectAllGarbage();

811
  CHECK(!weak_ref->target().IsUndefined(isolate));
812 813

  // Clears the KeepDuringJob set.
814
  context->GetIsolate()->ClearKeptObjects();
815 816 817
  heap::SimulateIncrementalMarking(heap, true);
  CcTest::CollectAllGarbage();

818
  CHECK(weak_ref->target().IsUndefined(isolate));
819 820
}

821 822 823 824 825 826
TEST(TestRemoveUnregisterToken) {
  FLAG_harmony_weak_refs = true;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope outer_scope(isolate);
827 828
  Handle<JSFinalizationRegistry> finalization_registry =
      ConstructJSFinalizationRegistry(isolate);
829 830 831 832 833 834 835 836
  Handle<JSObject> js_object =
      isolate->factory()->NewJSObject(isolate->object_function());

  Handle<JSObject> token1 = CreateKey("token1", isolate);
  Handle<JSObject> token2 = CreateKey("token2", isolate);
  Handle<Object> undefined =
      handle(ReadOnlyRoots(isolate).undefined_value(), isolate);

837 838 839 840
  Handle<WeakCell> weak_cell1a = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
  Handle<WeakCell> weak_cell1b = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token1, isolate);
841

842 843 844 845
  Handle<WeakCell> weak_cell2a = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token2, isolate);
  Handle<WeakCell> weak_cell2b = FinalizationRegistryRegister(
      finalization_registry, js_object, undefined, token2, isolate);
846 847 848

  NullifyWeakCell(weak_cell2a, isolate);

849
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 3,
850
                      *weak_cell2b, *weak_cell1b, *weak_cell1a);
851
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 1,
852 853 854
                      *weak_cell2a);
  {
    SimpleNumberDictionary key_map =
855
        SimpleNumberDictionary::cast(finalization_registry->key_map());
856 857 858 859 860 861
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
                           *weak_cell1a);
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 2, *weak_cell2b,
                           *weak_cell2a);
  }

862
  finalization_registry->RemoveUnregisterToken(
863 864 865 866 867 868 869
      JSReceiver::cast(*token2), isolate,
      [undefined](WeakCell matched_cell) {
        matched_cell.set_unregister_token(*undefined);
      },
      [](HeapObject, ObjectSlot, Object) {});

  // Both weak_cell2a and weak_cell2b remain on the weak cell chains.
870
  VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 3,
871
                      *weak_cell2b, *weak_cell1b, *weak_cell1a);
872
  VerifyWeakCellChain(isolate, finalization_registry->cleared_cells(), 1,
873 874 875 876 877
                      *weak_cell2a);

  // But both weak_cell2a and weak_cell2b are removed from the key chain.
  {
    SimpleNumberDictionary key_map =
878
        SimpleNumberDictionary::cast(finalization_registry->key_map());
879 880 881 882 883 884
    VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
                           *weak_cell1a);
    VerifyWeakCellKeyChain(isolate, key_map, *token2, 0);
  }
}

885 886
TEST(JSWeakRefScavengedInWorklist) {
  FLAG_harmony_weak_refs = true;
887
  if (!FLAG_incremental_marking || FLAG_single_generation) {
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
    return;
  }

  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Heap* heap = isolate->heap();

  {
    HandleScope outer_scope(isolate);
    Handle<JSWeakRef> weak_ref;

    // Make a WeakRef that points to a target, both of which become unreachable.
    {
      HandleScope inner_scope(isolate);
      Handle<JSObject> js_object =
          isolate->factory()->NewJSObject(isolate->object_function());
      Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
      CHECK(Heap::InYoungGeneration(*js_object));
      CHECK(Heap::InYoungGeneration(*inner_weak_ref));

      weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
    }

    // Do marking. This puts the WeakRef above into the js_weak_refs worklist
    // since its target isn't marked.
914 915
    CHECK(
        heap->mark_compact_collector()->weak_objects()->js_weak_refs.IsEmpty());
916
    heap::SimulateIncrementalMarking(heap, true);
917 918 919
    CHECK(!heap->mark_compact_collector()
               ->weak_objects()
               ->js_weak_refs.IsEmpty());
920 921 922 923
  }

  // Now collect both weak_ref and its target. The worklist should be empty.
  CcTest::CollectGarbage(NEW_SPACE);
924
  CHECK(heap->mark_compact_collector()->weak_objects()->js_weak_refs.IsEmpty());
925 926 927 928 929 930 931

  // The mark-compactor shouldn't see zapped WeakRefs in the worklist.
  CcTest::CollectAllGarbage();
}

TEST(JSWeakRefTenuredInWorklist) {
  FLAG_harmony_weak_refs = true;
932
  if (!FLAG_incremental_marking || FLAG_single_generation) {
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
    return;
  }

  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
  Handle<JSWeakRef> weak_ref;

  // Make a WeakRef that points to a target. The target becomes unreachable.
  {
    HandleScope inner_scope(isolate);
    Handle<JSObject> js_object =
        isolate->factory()->NewJSObject(isolate->object_function());
    Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
    CHECK(Heap::InYoungGeneration(*js_object));
    CHECK(Heap::InYoungGeneration(*inner_weak_ref));

    weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
  }
  JSWeakRef old_weak_ref_location = *weak_ref;

  // Do marking. This puts the WeakRef above into the js_weak_refs worklist
  // since its target isn't marked.
959
  CHECK(heap->mark_compact_collector()->weak_objects()->js_weak_refs.IsEmpty());
960
  heap::SimulateIncrementalMarking(heap, true);
961 962
  CHECK(
      !heap->mark_compact_collector()->weak_objects()->js_weak_refs.IsEmpty());
963 964 965 966 967 968

  // Now collect weak_ref's target. We still have a Handle to weak_ref, so it is
  // moved and remains on the worklist.
  CcTest::CollectGarbage(NEW_SPACE);
  JSWeakRef new_weak_ref_location = *weak_ref;
  CHECK_NE(old_weak_ref_location, new_weak_ref_location);
969 970
  CHECK(
      !heap->mark_compact_collector()->weak_objects()->js_weak_refs.IsEmpty());
971 972 973

  // The mark-compactor should see the moved WeakRef in the worklist.
  CcTest::CollectAllGarbage();
974
  CHECK(heap->mark_compact_collector()->weak_objects()->js_weak_refs.IsEmpty());
975 976 977
  CHECK(weak_ref->target().IsUndefined(isolate));
}

978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
TEST(UnregisterTokenHeapVerifier) {
  FLAG_harmony_weak_refs = true;
  if (!FLAG_incremental_marking) return;
  ManualGCScope manual_gc_scope;
#ifdef VERIFY_HEAP
  FLAG_verify_heap = true;
#endif

  CcTest::InitializeVM();
  v8::Isolate* isolate = CcTest::isolate();
  Heap* heap = CcTest::heap();
  v8::HandleScope outer_scope(isolate);

  {
    // Make a new FinalizationRegistry and register an object with an unregister
    // token that's unreachable after the IIFE returns.
    v8::HandleScope scope(isolate);
    CompileRun(
        "var token = {}; "
        "var registry = new FinalizationRegistry(function ()  {}); "
        "(function () { "
        "  let o = {}; "
        "  registry.register(o, {}, token); "
        "})();");
  }

  // GC so the WeakCell corresponding to o is moved from the active_cells to
  // cleared_cells.
  CcTest::CollectAllGarbage();
  CcTest::CollectAllGarbage();

  {
    // Override the unregister token to make the original object collectible.
    v8::HandleScope scope(isolate);
    CompileRun("token = 0;");
  }

  heap::SimulateIncrementalMarking(heap, true);

  // Pump message loop to run the finalizer task, then the incremental marking
  // task. The finalizer task will pop the WeakCell from the cleared list. This
  // should make the unregister_token slot undefined. That slot is iterated as a
  // custom weak pointer, so if it is not made undefined, the verifier as part
  // of the incremental marking task will crash.
  EmptyMessageQueues(isolate);
}

1025 1026
}  // namespace internal
}  // namespace v8