tor-browser

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

finalizationRegistry.js (6723B)


      1 function checkPropertyDescriptor(obj, property, writable, enumerable,
      2                                 configurable) {
      3  let desc = Object.getOwnPropertyDescriptor(obj, property);
      4  assertEq(typeof desc, "object");
      5  assertEq(desc.writable, writable);
      6  assertEq(desc.enumerable, enumerable);
      7  assertEq(desc.configurable, configurable);
      8 }
      9 
     10 function assertThrowsTypeError(thunk) {
     11  let error;
     12  try {
     13    thunk();
     14  } catch (e) {
     15    error = e;
     16  }
     17  assertEq(error instanceof TypeError, true);
     18 }
     19 
     20 // 3.1 The FinalizationRegistry Constructor
     21 assertEq(typeof this.FinalizationRegistry, "function");
     22 
     23 // 3.1.1 FinalizationRegistry ( cleanupCallback ) 
     24 assertThrowsTypeError(() => new FinalizationRegistry());
     25 assertThrowsTypeError(() => new FinalizationRegistry(1));
     26 new FinalizationRegistry(x => 0);
     27 
     28 // 3.2 Properties of the FinalizationRegistry Constructor
     29 assertEq(Object.getPrototypeOf(FinalizationRegistry), Function.prototype);
     30 
     31 // 3.2.1 FinalizationRegistry.prototype
     32 checkPropertyDescriptor(FinalizationRegistry, 'prototype', false, false, false);
     33 
     34 // 3.3 Properties of the FinalizationRegistry Prototype Object
     35 let proto = FinalizationRegistry.prototype;
     36 assertEq(Object.getPrototypeOf(proto), Object.prototype);
     37 
     38 // 3.3.1 FinalizationRegistry.prototype.constructor
     39 assertEq(proto.constructor, FinalizationRegistry);
     40 
     41 // 3.3.2 FinalizationRegistry.prototype.register ( target , holdings [, unregisterToken ] )
     42 assertEq(proto.hasOwnProperty('register'), true);
     43 assertEq(typeof proto.register, 'function');
     44 
     45 // 3.3.3 FinalizationRegistry.prototype.unregister ( unregisterToken )
     46 assertEq(proto.hasOwnProperty('unregister'), true);
     47 assertEq(typeof proto.unregister, 'function');
     48 
     49 // 3.3.4 FinalizationRegistry.prototype.cleanupSome ( [ callback ] )
     50 assertEq(proto.hasOwnProperty('cleanupSome'), true);
     51 assertEq(typeof proto.cleanupSome, 'function');
     52 
     53 // 3.3.5 FinalizationRegistry.prototype [ @@toStringTag ]
     54 assertEq(proto[Symbol.toStringTag], "FinalizationRegistry");
     55 checkPropertyDescriptor(proto, Symbol.toStringTag, false, false, true);
     56 
     57 // 3.4 Properties of FinalizationRegistry Instances
     58 let registry = new FinalizationRegistry(x => 0);
     59 assertEq(Object.getPrototypeOf(registry), proto);
     60 assertEq(Object.getOwnPropertyNames(registry).length, 0);
     61 
     62 let heldValues = [];
     63 registry = new FinalizationRegistry(value => {
     64  heldValues.push(value);
     65 });
     66 
     67 // Test a single target.
     68 heldValues = [];
     69 registry.register({}, 42);
     70 gc();
     71 drainJobQueue();
     72 assertEq(heldValues.length, 1);
     73 assertEq(heldValues[0], 42);
     74 
     75 // Test multiple targets.
     76 heldValues = [];
     77 for (let i = 0; i < 100; i++) {
     78  registry.register({}, i);
     79 }
     80 gc();
     81 drainJobQueue();
     82 assertEq(heldValues.length, 100);
     83 heldValues = heldValues.sort((a, b) => a - b);
     84 for (let i = 0; i < 100; i++) {
     85  assertEq(heldValues[i], i);
     86 }
     87 
     88 // Test a single object in multiple registries
     89 heldValues = [];
     90 let heldValues2 = [];
     91 let registry2 = new FinalizationRegistry(value => {
     92  heldValues2.push(value);
     93 });
     94 {
     95  let object = {};
     96  registry.register(object, 1);
     97  registry2.register(object, 2);
     98  object = null;
     99 }
    100 gc();
    101 drainJobQueue();
    102 assertEq(heldValues.length, 1);
    103 assertEq(heldValues[0], 1);
    104 assertEq(heldValues2.length, 1);
    105 assertEq(heldValues2[0], 2);
    106 
    107 // Unregister a single target.
    108 heldValues = [];
    109 let token = {};
    110 registry.register({}, 1, token);
    111 registry.unregister(token);
    112 gc();
    113 drainJobQueue();
    114 assertEq(heldValues.length, 0);
    115 
    116 // Unregister multiple targets.
    117 heldValues = [];
    118 let token2 = {};
    119 registry.register({}, 1, token);
    120 registry.register({}, 2, token2);
    121 registry.register({}, 3, token);
    122 registry.register({}, 4, token2);
    123 registry.unregister(token);
    124 gc();
    125 drainJobQueue();
    126 assertEq(heldValues.length, 2);
    127 heldValues = heldValues.sort((a, b) => a - b);
    128 assertEq(heldValues[0], 2);
    129 assertEq(heldValues[1], 4);
    130 
    131 // Watch object in another global.
    132 let other = newGlobal({newCompartment: true});
    133 heldValues = [];
    134 registry.register(evalcx('({})', other), 1);
    135 gc();
    136 drainJobQueue();
    137 assertEq(heldValues.length, 1);
    138 assertEq(heldValues[0], 1);
    139 
    140 // Pass heldValues from another global.
    141 let heldValue = evalcx('{}', other);
    142 heldValues = [];
    143 registry.register({}, heldValue);
    144 gc();
    145 drainJobQueue();
    146 assertEq(heldValues.length, 1);
    147 assertEq(heldValues[0], heldValue);
    148 
    149 // Pass unregister token from another global.
    150 token = evalcx('({})', other);
    151 heldValues = [];
    152 registry.register({}, 1, token);
    153 gc();
    154 drainJobQueue();
    155 assertEq(heldValues.length, 1);
    156 assertEq(heldValues[0], 1);
    157 heldValues = [];
    158 registry.register({}, 1, token);
    159 registry.unregister(token);
    160 gc();
    161 drainJobQueue();
    162 assertEq(heldValues.length, 0);
    163 
    164 // FinalizationRegistry is designed to be subclassable.
    165 class MyRegistry extends FinalizationRegistry {
    166  constructor(callback) {
    167    super(callback);
    168  }
    169 }
    170 let r2 = new MyRegistry(value => {
    171  heldValues.push(value);
    172 });
    173 heldValues = [];
    174 r2.register({}, 42);
    175 gc();
    176 drainJobQueue();
    177 assertEq(heldValues.length, 1);
    178 assertEq(heldValues[0], 42);
    179 
    180 // Test cleanupSome.
    181 heldValues = [];
    182 let r5 = new FinalizationRegistry(v => heldValues.push(v));
    183 r5.register({}, 1);
    184 r5.register({}, 2);
    185 r5.register({}, 3);
    186 gc();
    187 r5.cleanupSome();
    188 assertEq(heldValues.length, 3);
    189 heldValues = heldValues.sort((a, b) => a - b);
    190 assertEq(heldValues[0], 1);
    191 assertEq(heldValues[1], 2);
    192 assertEq(heldValues[2], 3);
    193 
    194 // Test trying to call cleanupSome in callback.
    195 let r6 = new FinalizationRegistry(x => {
    196  r6.cleanupSome();
    197 });
    198 r6.register({}, 1);
    199 gc();
    200 drainJobQueue();
    201 
    202 // Test trying to call cleanupSome in callback with multiple values.
    203 let callbackCounter7 = 0;
    204 let r7 = new FinalizationRegistry(x => {
    205  callbackCounter7++;
    206  r7.cleanupSome();
    207 });
    208 r7.register({}, 1);
    209 r7.register({}, 2);
    210 r7.register({}, 3);
    211 r7.register({}, 4);
    212 gc();
    213 drainJobQueue();
    214 assertEq(callbackCounter7, 4);
    215 
    216 // Test that targets don't keep the finalization registry alive.
    217 let target = {};
    218 registry = new FinalizationRegistry(value => undefined);
    219 registry.register(target, 1);
    220 let weakRef = new WeakRef(registry);
    221 registry = undefined;
    222 assertEq(typeof weakRef.deref(), 'object');
    223 drainJobQueue();
    224 gc();
    225 assertEq(weakRef.deref(), undefined);
    226 assertEq(typeof target, 'object');
    227 
    228 // Test that targets don't keep the finalization registry alive when also
    229 // used as the unregister token.
    230 registry = new FinalizationRegistry(value => undefined);
    231 registry.register(target, 1, target);
    232 weakRef = new WeakRef(registry);
    233 registry = undefined;
    234 assertEq(typeof weakRef.deref(), 'object');
    235 drainJobQueue();
    236 gc();
    237 assertEq(weakRef.deref(), undefined);
    238 assertEq(typeof target, 'object');
    239 
    240 // Test that cleanup doesn't happen if the finalization registry dies.
    241 heldValues = [];
    242 new FinalizationRegistry(value => {
    243  heldValues.push(value);
    244 }).register({}, 1);
    245 gc();
    246 drainJobQueue();
    247 assertEq(heldValues.length, 0);