tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gc-has-one-chance-to-call-cleanupCallback-queueMicrotask.optional.any.js (3834B)


      1 // META: script=/common/gc.js
      2 // META: script=resources/maybe-garbage-collect.js
      3 // ├──> maybeGarbageCollectAndCleanupAsync
      4 // ├──> maybeGarbageCollectAsync
      5 // └──> resolveGarbageCollection
      6 /*---
      7 esid: sec-finalization-registry-target
      8 info: |
      9  FinalizationRegistry ( cleanupCallback )
     10 
     11  FinalizationRegistry.prototype.cleanupSome ( [ callback ] )
     12 
     13  ...
     14  4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception.
     15  5. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback).
     16  6. Return undefined.
     17 
     18  Execution
     19 
     20  At any time, if an object obj is not live, an ECMAScript implementation may perform the following steps atomically:
     21 
     22  1. For each WeakRef ref such that ref.[[Target]] is obj,
     23    a. Set ref.[[Target]] to empty.
     24  2. For each FinalizationRegistry finalizationRegistry such that finalizationRegistry.[[Cells]] contains cell, such that cell.[[Target]] is obj,
     25    a. Set cell.[[Target]] to empty.
     26    b. Optionally, perform ! HostCleanupFinalizationRegistry(finalizationRegistry).
     27 ---*/
     28 
     29 
     30 let cleanupCallback = 0;
     31 let holdings = [];
     32 function cb(holding) {
     33  holdings.push(holding);
     34 }
     35 
     36 let finalizationRegistry = new FinalizationRegistry(function() {
     37  cleanupCallback += 1;
     38 });
     39 
     40 function emptyCells() {
     41  let target = {};
     42  finalizationRegistry.register(target, 'a');
     43 
     44  let prom = maybeGarbageCollectAndCleanupAsync(target);
     45  target = null;
     46 
     47  return prom;
     48 }
     49 
     50 promise_test(() => {
     51  return (async () => {
     52    assert_implements(
     53      typeof FinalizationRegistry.prototype.cleanupSome === 'function',
     54      'FinalizationRegistry.prototype.cleanupSome is not implemented.'
     55    );
     56 
     57    assert_implements(
     58      typeof queueMicrotask === 'function',
     59      'queueMicrotask is not implemented.'
     60    );
     61 
     62    let ticks = 0;
     63    await emptyCells();
     64    await queueMicrotask(() => ticks++);
     65 
     66    finalizationRegistry.cleanupSome(cb);
     67 
     68    // cleanupSome will be invoked if there are empty cells left. If the
     69    // cleanupCallback already ran, then cb won't be called.
     70    let expectedCalled = cleanupCallback === 1 ? 0 : 1;
     71    // This asserts the registered object was emptied in the previous GC.
     72    assert_equals(holdings.length, expectedCalled, 'cleanupSome callback for the first time');
     73 
     74    // At this point, we can't assert if cleanupCallback was called, because it's
     75    // optional. Although, we can finally assert it's not gonna be called anymore
     76    // for the other executions of the Garbage Collector.
     77    // The chance of having it called only happens right after the
     78    // cell.[[Target]] is set to empty.
     79    assert_true(cleanupCallback >= 0, 'cleanupCallback might be 0');
     80    assert_true(cleanupCallback <= 1, 'cleanupCallback might be 1');
     81 
     82    // Restoring the cleanupCallback variable to 0 will help us asserting the
     83    // finalizationRegistry callback is not called again.
     84    cleanupCallback = 0;
     85 
     86    await maybeGarbageCollectAsync();
     87    await queueMicrotask(() => ticks++);
     88 
     89    finalizationRegistry.cleanupSome(cb);
     90 
     91    assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called anymore, no empty cells');
     92    assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #1');
     93 
     94    await maybeGarbageCollectAsync();
     95    await queueMicrotask(() => ticks++);
     96 
     97    finalizationRegistry.cleanupSome(cb);
     98 
     99    assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called again #2');
    100    assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #2');
    101    assert_equals(ticks, 3, 'ticks is 3');
    102 
    103    if (holdings.length) {
    104      assert_array_equals(holdings, ['a']);
    105    }
    106 
    107    await maybeGarbageCollectAsync();
    108  })().catch(resolveGarbageCollection);
    109 }, 'cleanupCallback has only one optional chance to be called for a GC that cleans up a registered target.');