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