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.optional.any.js (3650B)


      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    let ticks = 0;
     58    await emptyCells();
     59    await ticks++;
     60 
     61    finalizationRegistry.cleanupSome(cb);
     62 
     63    // cleanupSome will be invoked if there are empty cells left. If the
     64    // cleanupCallback already ran, then cb won't be called.
     65    let expectedCalled = cleanupCallback === 1 ? 0 : 1;
     66    // This asserts the registered object was emptied in the previous GC.
     67    assert_equals(holdings.length, expectedCalled, 'cleanupSome callback for the first time');
     68 
     69    // At this point, we can't assert if cleanupCallback was called, because it's
     70    // optional. Although, we can finally assert it's not gonna be called anymore
     71    // for the other executions of the Garbage Collector.
     72    // The chance of having it called only happens right after the
     73    // cell.[[Target]] is set to empty.
     74    assert_true(cleanupCallback >= 0, 'cleanupCallback might be 0');
     75    assert_true(cleanupCallback <= 1, 'cleanupCallback might be 1');
     76 
     77    // Restoring the cleanupCallback variable to 0 will help us asserting the
     78    // finalizationRegistry callback is not called again.
     79    cleanupCallback = 0;
     80 
     81    await maybeGarbageCollectAsync();
     82    await ticks++;
     83 
     84    finalizationRegistry.cleanupSome(cb);
     85 
     86    assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called anymore, no empty cells');
     87    assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #1');
     88 
     89    await maybeGarbageCollectAsync();
     90    await ticks++;
     91 
     92    finalizationRegistry.cleanupSome(cb);
     93 
     94    assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called again #2');
     95    assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #2');
     96    assert_equals(ticks, 3, 'ticks is 3');
     97 
     98    if (holdings.length) {
     99      assert_array_equals(holdings, ['a']);
    100    }
    101 
    102    await maybeGarbageCollectAsync();
    103  })().catch(resolveGarbageCollection);
    104 }, 'cleanupCallback has only one optional chance to be called for a GC that cleans up a registered target.');