From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../tests/js/builtins/Array.DefineOwnProperty.html | 24 ++ .../js/builtins/Array.prototype.join-order.html | 86 +++++++ .../web-platform/tests/js/builtins/Math.max.html | 13 + .../web-platform/tests/js/builtins/Math.maxmin.js | 57 +++++ .../web-platform/tests/js/builtins/Math.min.html | 13 + .../tests/js/builtins/Object.prototype.freeze.html | 35 +++ .../Object.prototype.getOwnPropertyNames.html | 56 +++++ .../Object.prototype.hasOwnProperty-order.html | 21 ++ ...t.prototype.hasOwnProperty-prototype-chain.html | 35 +++ .../Object.prototype.preventExtensions.html | 35 +++ .../tests/js/builtins/Object.prototype.seal.html | 35 +++ .../Promise-incumbent-global-subframe.sub.html | 12 + .../Promise-incumbent-global-subsubframe.sub.html | 13 + .../js/builtins/Promise-incumbent-global.sub.html | 20 ++ .../tests/js/builtins/Promise-subclassing.html | 265 +++++++++++++++++++++ .../js/builtins/WeakMap.prototype-properties.html | 104 ++++++++ ...leanup-prevented-with-reference.optional.any.js | 51 ++++ ...eanup-prevented-with-unregister.optional.any.js | 58 +++++ ...leanupCallback-gets-a-microtask.optional.any.js | 61 +++++ ...k-throws-onerror-interaction.optional.window.js | 70 ++++++ ...-cleanupCallback-queueMicrotask.optional.any.js | 109 +++++++++ ...eueMicrotaskMutationObserver.optional.window.js | 115 +++++++++ ...-chance-to-call-cleanupCallback.optional.any.js | 104 ++++++++ .../holdings-multiple-values.optional.any.js | 66 +++++ .../builtins/weakrefs/reentrancy.optional.any.js | 51 ++++ .../weakrefs/resources/maybe-garbage-collect.js | 72 ++++++ .../return-undefined-with-gc.optional.any.js | 79 ++++++ .../unregister-cleaned-up-cell.optional.any.js | 73 ++++++ 28 files changed, 1733 insertions(+) create mode 100644 testing/web-platform/tests/js/builtins/Array.DefineOwnProperty.html create mode 100644 testing/web-platform/tests/js/builtins/Array.prototype.join-order.html create mode 100644 testing/web-platform/tests/js/builtins/Math.max.html create mode 100644 testing/web-platform/tests/js/builtins/Math.maxmin.js create mode 100644 testing/web-platform/tests/js/builtins/Math.min.html create mode 100644 testing/web-platform/tests/js/builtins/Object.prototype.freeze.html create mode 100644 testing/web-platform/tests/js/builtins/Object.prototype.getOwnPropertyNames.html create mode 100644 testing/web-platform/tests/js/builtins/Object.prototype.hasOwnProperty-order.html create mode 100644 testing/web-platform/tests/js/builtins/Object.prototype.hasOwnProperty-prototype-chain.html create mode 100644 testing/web-platform/tests/js/builtins/Object.prototype.preventExtensions.html create mode 100644 testing/web-platform/tests/js/builtins/Object.prototype.seal.html create mode 100644 testing/web-platform/tests/js/builtins/Promise-incumbent-global-subframe.sub.html create mode 100644 testing/web-platform/tests/js/builtins/Promise-incumbent-global-subsubframe.sub.html create mode 100644 testing/web-platform/tests/js/builtins/Promise-incumbent-global.sub.html create mode 100644 testing/web-platform/tests/js/builtins/Promise-subclassing.html create mode 100644 testing/web-platform/tests/js/builtins/WeakMap.prototype-properties.html create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/cleanup-prevented-with-reference.optional.any.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/cleanup-prevented-with-unregister.optional.any.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/finalizationregistry-cleanupCallback-gets-a-microtask.optional.any.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/finalizationregistry-cleanupCallback-throws-onerror-interaction.optional.window.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback-queueMicrotask.optional.any.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback-queueMicrotaskMutationObserver.optional.window.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback.optional.any.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/holdings-multiple-values.optional.any.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/reentrancy.optional.any.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/resources/maybe-garbage-collect.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/return-undefined-with-gc.optional.any.js create mode 100644 testing/web-platform/tests/js/builtins/weakrefs/unregister-cleaned-up-cell.optional.any.js (limited to 'testing/web-platform/tests/js/builtins') diff --git a/testing/web-platform/tests/js/builtins/Array.DefineOwnProperty.html b/testing/web-platform/tests/js/builtins/Array.DefineOwnProperty.html new file mode 100644 index 0000000000..40ed00a4c7 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Array.DefineOwnProperty.html @@ -0,0 +1,24 @@ + +Array.[[DefineOwnProperty]] + + + + +
+ diff --git a/testing/web-platform/tests/js/builtins/Array.prototype.join-order.html b/testing/web-platform/tests/js/builtins/Array.prototype.join-order.html new file mode 100644 index 0000000000..ce091a5e54 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Array.prototype.join-order.html @@ -0,0 +1,86 @@ + +Array.prototype.join + + + + + +
+ diff --git a/testing/web-platform/tests/js/builtins/Math.max.html b/testing/web-platform/tests/js/builtins/Math.max.html new file mode 100644 index 0000000000..a4a6ae27c8 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Math.max.html @@ -0,0 +1,13 @@ + +Math.max + + + + + + +
+ + diff --git a/testing/web-platform/tests/js/builtins/Math.maxmin.js b/testing/web-platform/tests/js/builtins/Math.maxmin.js new file mode 100644 index 0000000000..bf7b2cd8cd --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Math.maxmin.js @@ -0,0 +1,57 @@ +function testMathMaxMin(aFun) { + var test_error = { name: "test" }; + test(function() { + assert_throws_exactly(test_error, function() { + Math[aFun](NaN, { + valueOf: function() { + throw test_error; + } + }); + }); + }, "ToNumber should be called on all arguments: NaN."); + test(function() { + assert_throws_exactly(test_error, function() { + Math[aFun](-Infinity, { + valueOf: function() { + throw test_error; + } + }); + }); + }, "ToNumber should be called on all arguments: -Infinity."); + test(function() { + assert_throws_exactly(test_error, function() { + Math[aFun](Infinity, { + valueOf: function() { + throw test_error; + } + }); + }); + }, "ToNumber should be called on all arguments: Infinity."); + test(function() { + assert_throws_exactly(test_error, function() { + Math[aFun]({ + valueOf: function() { + throw test_error; + } + }, + { + valueOf: function() { + throw 7; + } + }); + }); + }, "ToNumber should be called left to right."); + test(function() { + assert_equals(Math[aFun]("1"), 1); + }, "Should return a number."); + test(function() { + var expected = { + "max": 0, + "min": -0 + } + assert_equals(Math[aFun](0, -0), expected[aFun]); + assert_equals(Math[aFun](-0, 0), expected[aFun]); + assert_equals(Math[aFun](0, 0), 0); + assert_equals(Math[aFun](-0, -0), -0); + }, "Should handle negative zero correctly."); +} diff --git a/testing/web-platform/tests/js/builtins/Math.min.html b/testing/web-platform/tests/js/builtins/Math.min.html new file mode 100644 index 0000000000..4ae71b9d76 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Math.min.html @@ -0,0 +1,13 @@ + +Math.min + + + + + + +
+ + diff --git a/testing/web-platform/tests/js/builtins/Object.prototype.freeze.html b/testing/web-platform/tests/js/builtins/Object.prototype.freeze.html new file mode 100644 index 0000000000..1e5ed418a9 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Object.prototype.freeze.html @@ -0,0 +1,35 @@ + + + + +Object.freeze + + + + + + + + diff --git a/testing/web-platform/tests/js/builtins/Object.prototype.getOwnPropertyNames.html b/testing/web-platform/tests/js/builtins/Object.prototype.getOwnPropertyNames.html new file mode 100644 index 0000000000..582f41ba10 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Object.prototype.getOwnPropertyNames.html @@ -0,0 +1,56 @@ + +Object.prototype.getOwnPropertyNames + + + + +
+ diff --git a/testing/web-platform/tests/js/builtins/Object.prototype.hasOwnProperty-order.html b/testing/web-platform/tests/js/builtins/Object.prototype.hasOwnProperty-order.html new file mode 100644 index 0000000000..50325d9ea9 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Object.prototype.hasOwnProperty-order.html @@ -0,0 +1,21 @@ + +Object.prototype.hasOwnProperty + + + + + +
+ diff --git a/testing/web-platform/tests/js/builtins/Object.prototype.hasOwnProperty-prototype-chain.html b/testing/web-platform/tests/js/builtins/Object.prototype.hasOwnProperty-prototype-chain.html new file mode 100644 index 0000000000..7c02257fbc --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Object.prototype.hasOwnProperty-prototype-chain.html @@ -0,0 +1,35 @@ + + + + +Object.prototype.hasOwnProperty: Check prototype chain + + + + + + + + \ No newline at end of file diff --git a/testing/web-platform/tests/js/builtins/Object.prototype.preventExtensions.html b/testing/web-platform/tests/js/builtins/Object.prototype.preventExtensions.html new file mode 100644 index 0000000000..ceea7b3dd1 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Object.prototype.preventExtensions.html @@ -0,0 +1,35 @@ + + + + +Object.preventExtensions + + + + + + + + diff --git a/testing/web-platform/tests/js/builtins/Object.prototype.seal.html b/testing/web-platform/tests/js/builtins/Object.prototype.seal.html new file mode 100644 index 0000000000..ad84d8c218 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Object.prototype.seal.html @@ -0,0 +1,35 @@ + + + + +Object.seal + + + + + + + + \ No newline at end of file diff --git a/testing/web-platform/tests/js/builtins/Promise-incumbent-global-subframe.sub.html b/testing/web-platform/tests/js/builtins/Promise-incumbent-global-subframe.sub.html new file mode 100644 index 0000000000..dde0ac953e --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Promise-incumbent-global-subframe.sub.html @@ -0,0 +1,12 @@ + + + diff --git a/testing/web-platform/tests/js/builtins/Promise-incumbent-global-subsubframe.sub.html b/testing/web-platform/tests/js/builtins/Promise-incumbent-global-subsubframe.sub.html new file mode 100644 index 0000000000..9edd9d278a --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Promise-incumbent-global-subsubframe.sub.html @@ -0,0 +1,13 @@ + + diff --git a/testing/web-platform/tests/js/builtins/Promise-incumbent-global.sub.html b/testing/web-platform/tests/js/builtins/Promise-incumbent-global.sub.html new file mode 100644 index 0000000000..6ae0a9fe5e --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Promise-incumbent-global.sub.html @@ -0,0 +1,20 @@ + + + + + + + diff --git a/testing/web-platform/tests/js/builtins/Promise-subclassing.html b/testing/web-platform/tests/js/builtins/Promise-subclassing.html new file mode 100644 index 0000000000..2349c07b05 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/Promise-subclassing.html @@ -0,0 +1,265 @@ + + + + + + diff --git a/testing/web-platform/tests/js/builtins/WeakMap.prototype-properties.html b/testing/web-platform/tests/js/builtins/WeakMap.prototype-properties.html new file mode 100644 index 0000000000..2c2bddfeb7 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/WeakMap.prototype-properties.html @@ -0,0 +1,104 @@ + +WeakMap.prototype + + + + + +
+ diff --git a/testing/web-platform/tests/js/builtins/weakrefs/cleanup-prevented-with-reference.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/cleanup-prevented-with-reference.optional.any.js new file mode 100644 index 0000000000..da902ac4f1 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/cleanup-prevented-with-reference.optional.any.js @@ -0,0 +1,51 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry.prototype.cleanupSome +info: | + FinalizationRegistry.prototype.cleanupSome ( [ callback ] ) + + 1. Let finalizationRegistry be the this value. + 2. If Type(finalizationRegistry) is not Object, throw a TypeError exception. + 3. If finalizationRegistry does not have a [[Cells]] internal slot, throw a TypeError exception. + 4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception. + 5. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback). + 6. Return undefined. +---*/ + +let holdingsList = []; +function cb(holding) { + holdingsList.push(holding); +}; +let finalizationRegistry = new FinalizationRegistry(function() {}); + +let referenced = {}; + +function emptyCells() { + let target = {}; + finalizationRegistry.register(target, 'target!'); + finalizationRegistry.register(referenced, 'referenced'); + + let prom = maybeGarbageCollectAndCleanupAsync(target); + target = null; + + return prom; +} + +promise_test(() => { + return (async () => { + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' + ); + + await emptyCells(); + finalizationRegistry.cleanupSome(cb); + + assert_equals(holdingsList.length, 1); + assert_equals(holdingsList[0], 'target!'); + assert_equals(typeof referenced, 'object', 'referenced preserved'); + })().catch(resolveGarbageCollection); +}, 'cleanupCallback has only one optional chance to be called for a GC that cleans up a registered target.'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/cleanup-prevented-with-unregister.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/cleanup-prevented-with-unregister.optional.any.js new file mode 100644 index 0000000000..640f21e7f3 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/cleanup-prevented-with-unregister.optional.any.js @@ -0,0 +1,58 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry.prototype.cleanupSome +info: | + FinalizationRegistry.prototype.cleanupSome ( [ callback ] ) + + 1. Let finalizationRegistry be the this value. + 2. If Type(finalizationRegistry) is not Object, throw a TypeError exception. + 3. If finalizationRegistry does not have a [[Cells]] internal slot, throw a TypeError exception. + 4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception. + 5. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback). + 6. Return undefined. + + FinalizationRegistry.prototype.unregister ( unregisterToken ) + + 1. Let removed be false. + 2. For each Record { [[Target]], [[Holdings]], [[UnregisterToken]] } cell that is an element of finalizationRegistry.[[Cells]], do + a. If SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then + i. Remove cell from finalizationRegistry.[[Cells]]. + ii. Set removed to true. + 3. Return removed. +---*/ +let token = {}; +let finalizationRegistry = new FinalizationRegistry(function() {}); + +function emptyCells() { + let target = {}; + finalizationRegistry.register(target, 'target!', token); + + let prom = maybeGarbageCollectAndCleanupAsync(target); + target = null; + + return prom; +} + +promise_test(() => { + return (async () => { + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' + ); + + await emptyCells(); + let called = 0; + + let res = finalizationRegistry.unregister(token); + assert_equals(res, true, 'unregister target before iterating over it in cleanup'); + + finalizationRegistry.cleanupSome((holding) => { + called += 1; + }); + + assert_equals(called, 0, 'callback was not called'); + })().catch(resolveGarbageCollection); +}, 'Cleanup might be prevented with an unregister usage'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/finalizationregistry-cleanupCallback-gets-a-microtask.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/finalizationregistry-cleanupCallback-gets-a-microtask.optional.any.js new file mode 100644 index 0000000000..c3a84418f3 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/finalizationregistry-cleanupCallback-gets-a-microtask.optional.any.js @@ -0,0 +1,61 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry-target +info: | + FinalizationRegistry ( cleanupCallback ) + + Execution + At any time, if a set of objects S is not live, an ECMAScript implementation may perform the + following steps atomically: + + For each obj of S, do + For each WeakRef ref such that ref.[[WeakRefTarget]] is obj, do + Set ref.[[WeakRefTarget]] to empty. + For each FinalizationRegistry fg such that fg.[[Cells]] contains cell, such that + cell.[[WeakRefTarget]] is obj, + Set cell.[[WeakRefTarget]] to empty. + Optionally, perform ! HostCleanupFinalizationRegistry(fg). + + HostCleanupFinalizationRegistry(finalizationRegistry) + + HostCleanupFinalizationRegistry is an implementation-defined abstract operation that is expected + to call CleanupFinalizationRegistry(finalizationRegistry) at some point in the future, if + possible. The host's responsibility is to make this call at a time which does not interrupt + synchronous ECMAScript code execution. +---*/ + +let count = 1_000; +let calls = 0; +let registries = []; +let callback = function() { + calls++; +}; +for (let i = 0; i < count; i++) { + registries.push( + new FinalizationRegistry(callback) + ); +} +setup({ allow_uncaught_exception: true }); + +promise_test((test) => { + assert_implements( + typeof FinalizationRegistry.prototype.register === 'function', + 'FinalizationRegistry.prototype.register is not implemented.' + ); + return (async () => { + + { + let target = {}; + for (let registry of registries) { + registry.register(target, 1); + } + target = null; + } + + await maybeGarbageCollectAsync(); + await test.step_wait(() => calls === count, `Expected ${count} registry cleanups.`); + })().catch(resolveGarbageCollection); +}, 'HostCleanupFinalizationRegistry is an implementation-defined abstract operation that is expected to call CleanupFinalizationRegistry(finalizationRegistry) at some point in the future, if possible.'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/finalizationregistry-cleanupCallback-throws-onerror-interaction.optional.window.js b/testing/web-platform/tests/js/builtins/weakrefs/finalizationregistry-cleanupCallback-throws-onerror-interaction.optional.window.js new file mode 100644 index 0000000000..71d6fbe174 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/finalizationregistry-cleanupCallback-throws-onerror-interaction.optional.window.js @@ -0,0 +1,70 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry-target +info: | + FinalizationRegistry ( cleanupCallback ) + + CleanupFinalizationRegistry ( finalizationRegistry [ , callback ] ) + + The following steps are performed: + + Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots. + If callback is not present or undefined, set callback to finalizationRegistry.[[CleanupCallback]]. + While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is + empty, then an implementation may perform the following steps, + Choose any such cell. + Remove cell from finalizationRegistry.[[Cells]]. + Perform ? Call(callback, undefined, « cell.[[HeldValue]] »). + Return NormalCompletion(undefined). + + EDITOR'S NOTE + When called from HostCleanupFinalizationRegistry, if calling the callback throws an error, this will be caught within the RunJobs algorithm and reported to the host. HTML does not apply the RunJobs algorithm, but will also report the error, which may call window.onerror. +---*/ + +let error = new Error('FinalizationRegistryError'); + +let finalizationRegistry = new FinalizationRegistry(function() { + throw error; +}); + +setup({ allow_uncaught_exception: true }); + +promise_test((test) => { + assert_implements( + typeof FinalizationRegistry.prototype.register === 'function', + 'FinalizationRegistry.prototype.register is not implemented.' + ); + + return (async () => { + + let resolve; + let reject; + let deferred = new Promise((resolverFn, rejecterFn) => { + resolve = resolverFn; + reject = rejecterFn; + }); + + window.onerror = test.step_func((message, source, lineno, colno, exception) => { + assert_equals(exception, error, 'window.onerror received the intended error object.'); + resolve(); + }); + + { + let target = {}; + let heldValue = 1; + finalizationRegistry.register(target, heldValue); + target = null; + } + + await maybeGarbageCollectAsync(); + + // Since the process of garbage collection is non-deterministic, we cannot know when + // (if ever) it will actually occur. + test.step_timeout(() => { reject(); }, 5000); + + return deferred; + })().catch(resolveGarbageCollection); +}, 'When called from HostCleanupFinalizationRegistry, if calling the callback throws an error, this will be caught within the RunJobs algorithm and reported to the host. HTML does not apply the RunJobs algorithm, but will also report the error, which may call window.onerror.'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback-queueMicrotask.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback-queueMicrotask.optional.any.js new file mode 100644 index 0000000000..456281c520 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback-queueMicrotask.optional.any.js @@ -0,0 +1,109 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// ├──> maybeGarbageCollectAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry-target +info: | + FinalizationRegistry ( cleanupCallback ) + + FinalizationRegistry.prototype.cleanupSome ( [ callback ] ) + + ... + 4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception. + 5. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback). + 6. Return undefined. + + Execution + + At any time, if an object obj is not live, an ECMAScript implementation may perform the following steps atomically: + + 1. For each WeakRef ref such that ref.[[Target]] is obj, + a. Set ref.[[Target]] to empty. + 2. For each FinalizationRegistry finalizationRegistry such that finalizationRegistry.[[Cells]] contains cell, such that cell.[[Target]] is obj, + a. Set cell.[[Target]] to empty. + b. Optionally, perform ! HostCleanupFinalizationRegistry(finalizationRegistry). +---*/ + + +let cleanupCallback = 0; +let holdings = []; +function cb(holding) { + holdings.push(holding); +} + +let finalizationRegistry = new FinalizationRegistry(function() { + cleanupCallback += 1; +}); + +function emptyCells() { + let target = {}; + finalizationRegistry.register(target, 'a'); + + let prom = maybeGarbageCollectAndCleanupAsync(target); + target = null; + + return prom; +} + +promise_test(() => { + return (async () => { + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' + ); + + assert_implements( + typeof queueMicrotask === 'function', + 'queueMicrotask is not implemented.' + ); + + let ticks = 0; + await emptyCells(); + await queueMicrotask(() => ticks++); + + finalizationRegistry.cleanupSome(cb); + + // cleanupSome will be invoked if there are empty cells left. If the + // cleanupCallback already ran, then cb won't be called. + let expectedCalled = cleanupCallback === 1 ? 0 : 1; + // This asserts the registered object was emptied in the previous GC. + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback for the first time'); + + // At this point, we can't assert if cleanupCallback was called, because it's + // optional. Although, we can finally assert it's not gonna be called anymore + // for the other executions of the Garbage Collector. + // The chance of having it called only happens right after the + // cell.[[Target]] is set to empty. + assert_true(cleanupCallback >= 0, 'cleanupCallback might be 0'); + assert_true(cleanupCallback <= 1, 'cleanupCallback might be 1'); + + // Restoring the cleanupCallback variable to 0 will help us asserting the + // finalizationRegistry callback is not called again. + cleanupCallback = 0; + + await maybeGarbageCollectAsync(); + await queueMicrotask(() => ticks++); + + finalizationRegistry.cleanupSome(cb); + + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called anymore, no empty cells'); + assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #1'); + + await maybeGarbageCollectAsync(); + await queueMicrotask(() => ticks++); + + finalizationRegistry.cleanupSome(cb); + + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called again #2'); + assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #2'); + assert_equals(ticks, 3, 'ticks is 3'); + + if (holdings.length) { + assert_array_equals(holdings, ['a']); + } + + await maybeGarbageCollectAsync(); + })().catch(resolveGarbageCollection); +}, 'cleanupCallback has only one optional chance to be called for a GC that cleans up a registered target.'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback-queueMicrotaskMutationObserver.optional.window.js b/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback-queueMicrotaskMutationObserver.optional.window.js new file mode 100644 index 0000000000..7b29f00943 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback-queueMicrotaskMutationObserver.optional.window.js @@ -0,0 +1,115 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// ├──> maybeGarbageCollectAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry-target +info: | + FinalizationRegistry ( cleanupCallback ) + + FinalizationRegistry.prototype.cleanupSome ( [ callback ] ) + + ... + 4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception. + 5. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback). + 6. Return undefined. + + Execution + + At any time, if an object obj is not live, an ECMAScript implementation may perform the following steps atomically: + + 1. For each WeakRef ref such that ref.[[Target]] is obj, + a. Set ref.[[Target]] to empty. + 2. For each FinalizationRegistry finalizationRegistry such that finalizationRegistry.[[Cells]] contains cell, such that cell.[[Target]] is obj, + a. Set cell.[[Target]] to empty. + b. Optionally, perform ! HostCleanupFinalizationRegistry(finalizationRegistry). +---*/ + + +let cleanupCallback = 0; +let holdings = []; +function cb(holding) { + holdings.push(holding); +} + +let finalizationRegistry = new FinalizationRegistry(function() { + cleanupCallback += 1; +}); + +function emptyCells() { + let target = {}; + finalizationRegistry.register(target, 'a'); + + let prom = maybeGarbageCollectAndCleanupAsync(target); + target = null; + + return prom; +} + +function queueMicrotaskByMutationObserver(callback) { + const textNode = document.createTextNode(''); + new MutationObserver(callback).observe(textNode, { characterData: true }); + textNode.data = 1; +} + +promise_test(() => { + return (async () => { + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' + ); + + assert_implements( + typeof MutationObserver === 'function', + 'MutationObserver is not implemented.' + ); + + let ticks = 0; + await emptyCells(); + await queueMicrotaskByMutationObserver(() => ticks++); + + finalizationRegistry.cleanupSome(cb); + + // cleanupSome will be invoked if there are empty cells left. If the + // cleanupCallback already ran, then cb won't be called. + let expectedCalled = cleanupCallback === 1 ? 0 : 1; + // This asserts the registered object was emptied in the previous GC. + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback for the first time'); + + // At this point, we can't assert if cleanupCallback was called, because it's + // optional. Although, we can finally assert it's not gonna be called anymore + // for the other executions of the Garbage Collector. + // The chance of having it called only happens right after the + // cell.[[Target]] is set to empty. + assert_true(cleanupCallback >= 0, 'cleanupCallback might be 0'); + assert_true(cleanupCallback <= 1, 'cleanupCallback might be 1'); + + // Restoring the cleanupCallback variable to 0 will help us asserting the + // finalizationRegistry callback is not called again. + cleanupCallback = 0; + + await maybeGarbageCollectAsync(); + await queueMicrotaskByMutationObserver(() => ticks++); + + finalizationRegistry.cleanupSome(cb); + + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called anymore, no empty cells'); + assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #1'); + + await maybeGarbageCollectAsync(); + await queueMicrotaskByMutationObserver(() => ticks++); + + finalizationRegistry.cleanupSome(cb); + + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called again #2'); + assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #2'); + assert_equals(ticks, 3, 'ticks is 3'); + + if (holdings.length) { + assert_array_equals(holdings, ['a']); + } + + await maybeGarbageCollectAsync(); + })().catch(resolveGarbageCollection); +}, 'cleanupCallback has only one optional chance to be called for a GC that cleans up a registered target.'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback.optional.any.js new file mode 100644 index 0000000000..92cd322869 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/gc-has-one-chance-to-call-cleanupCallback.optional.any.js @@ -0,0 +1,104 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// ├──> maybeGarbageCollectAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry-target +info: | + FinalizationRegistry ( cleanupCallback ) + + FinalizationRegistry.prototype.cleanupSome ( [ callback ] ) + + ... + 4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception. + 5. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback). + 6. Return undefined. + + Execution + + At any time, if an object obj is not live, an ECMAScript implementation may perform the following steps atomically: + + 1. For each WeakRef ref such that ref.[[Target]] is obj, + a. Set ref.[[Target]] to empty. + 2. For each FinalizationRegistry finalizationRegistry such that finalizationRegistry.[[Cells]] contains cell, such that cell.[[Target]] is obj, + a. Set cell.[[Target]] to empty. + b. Optionally, perform ! HostCleanupFinalizationRegistry(finalizationRegistry). +---*/ + + +let cleanupCallback = 0; +let holdings = []; +function cb(holding) { + holdings.push(holding); +} + +let finalizationRegistry = new FinalizationRegistry(function() { + cleanupCallback += 1; +}); + +function emptyCells() { + let target = {}; + finalizationRegistry.register(target, 'a'); + + let prom = maybeGarbageCollectAndCleanupAsync(target); + target = null; + + return prom; +} + +promise_test(() => { + return (async () => { + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' + ); + + let ticks = 0; + await emptyCells(); + await ticks++; + + finalizationRegistry.cleanupSome(cb); + + // cleanupSome will be invoked if there are empty cells left. If the + // cleanupCallback already ran, then cb won't be called. + let expectedCalled = cleanupCallback === 1 ? 0 : 1; + // This asserts the registered object was emptied in the previous GC. + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback for the first time'); + + // At this point, we can't assert if cleanupCallback was called, because it's + // optional. Although, we can finally assert it's not gonna be called anymore + // for the other executions of the Garbage Collector. + // The chance of having it called only happens right after the + // cell.[[Target]] is set to empty. + assert_true(cleanupCallback >= 0, 'cleanupCallback might be 0'); + assert_true(cleanupCallback <= 1, 'cleanupCallback might be 1'); + + // Restoring the cleanupCallback variable to 0 will help us asserting the + // finalizationRegistry callback is not called again. + cleanupCallback = 0; + + await maybeGarbageCollectAsync(); + await ticks++; + + finalizationRegistry.cleanupSome(cb); + + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called anymore, no empty cells'); + assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #1'); + + await maybeGarbageCollectAsync(); + await ticks++; + + finalizationRegistry.cleanupSome(cb); + + assert_equals(holdings.length, expectedCalled, 'cleanupSome callback is not called again #2'); + assert_equals(cleanupCallback, 0, 'cleanupCallback is not called again #2'); + assert_equals(ticks, 3, 'ticks is 3'); + + if (holdings.length) { + assert_array_equals(holdings, ['a']); + } + + await maybeGarbageCollectAsync(); + })().catch(resolveGarbageCollection); +}, 'cleanupCallback has only one optional chance to be called for a GC that cleans up a registered target.'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/holdings-multiple-values.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/holdings-multiple-values.optional.any.js new file mode 100644 index 0000000000..604174e467 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/holdings-multiple-values.optional.any.js @@ -0,0 +1,66 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-properties-of-the-finalization-registry-constructor +info: | + FinalizationRegistry.prototype.cleanupSome ( [ callback ] ) + + 1. Let finalizationRegistry be the this value. + ... + 5. Perform ! CleanupFinalizationRegistry(finalizationRegistry, callback). + ... + + CleanupFinalizationRegistry ( finalizationRegistry [ , callback ] ) + + ... + 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is ~empty~, then an implementation may perform the following steps, + a. Choose any such cell. + b. Remove cell from finalizationRegistry.[[Cells]]. + c. Perform ? Call(callback, undefined, << cell.[[HeldValue]] >>). + +---*/ + +function check(value, expectedName) { + let holdings = []; + let called = 0; + let finalizationRegistry = new FinalizationRegistry(function() {}); + + function callback(holding) { + called += 1; + holdings.push(holding); + } + + // This is internal to avoid conflicts + function emptyCells(value) { + let target = {}; + finalizationRegistry.register(target, value); + + let prom = maybeGarbageCollectAndCleanupAsync(target); + target = null; + + return prom; + } + + return emptyCells(value).then(function() { + finalizationRegistry.cleanupSome(callback); + assert_equals(called, 1, expectedName); + assert_equals(holdings.length, 1, expectedName); + assert_equals(holdings[0], value, expectedName); + }); +} + +test(() => + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' +), 'Requires FinalizationRegistry.prototype.cleanupSome'); +promise_test(() => check(undefined, 'undefined'), '`undefined` as registered holding value'); +promise_test(() => check(null, 'null'), '`null` as registered holding value'); +promise_test(() => check('', 'the empty string'), '`""` as registered holding value'); +promise_test(() => check({}, 'object'), '`{}` as registered holding value'); +promise_test(() => check(42, 'number'), '`42` as registered holding value'); +promise_test(() => check(true, 'true'), '`true` as registered holding value'); +promise_test(() => check(false, 'false'), '`false` as registered holding value'); +promise_test(() => check(Symbol(1), 'symbol'), '`Symbol(1)` as registered holding value'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/reentrancy.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/reentrancy.optional.any.js new file mode 100644 index 0000000000..fa7b7d55c4 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/reentrancy.optional.any.js @@ -0,0 +1,51 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-properties-of-the-finalization-registry-constructor +---*/ + +let called = 0; +let endOfCall = 0; +let finalizationRegistry = new FinalizationRegistry(function() {}); + +function callback(holding) { + called += 1; + + if (called === 1) { + // Atempt to re-enter the callback. + let nestedCallbackRan = false; + finalizationRegistry.cleanupSome(() => { nestedCallbackRan = true }); + assert_equals(nestedCallbackRan, true); + } + + endOfCall += 1; +} + +function emptyCells() { + let o1 = {}; + let o2 = {}; + // Register more than one objects to test reentrancy. + finalizationRegistry.register(o1, 'holdings 1'); + finalizationRegistry.register(o2, 'holdings 2'); + + let prom = maybeGarbageCollectAndCleanupAsync(o1); + o1 = null; + + return prom; +} + +promise_test(() => { + return (async () => { + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' + ); + await emptyCells(); + finalizationRegistry.cleanupSome(callback); + + assert_equals(called, 1, 'callback was called'); + assert_equals(endOfCall, 1, 'callback finished'); + })().catch(resolveGarbageCollection); +}, 'cleanupCallback has only one optional chance to be called for a GC that cleans up a registered target.'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/resources/maybe-garbage-collect.js b/testing/web-platform/tests/js/builtins/weakrefs/resources/maybe-garbage-collect.js new file mode 100644 index 0000000000..8bd4cac309 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/resources/maybe-garbage-collect.js @@ -0,0 +1,72 @@ +/** + * maybeGarbageCollectAsync + * + * It might garbage collect, it might not. If it doesn't, that's ok. + */ +self.maybeGarbageCollectAsync = garbageCollect; + +/** + * maybeGarbageCollectKeptObjectsAsync + * + * Based on "asyncGCDeref" in https://github.com/tc39/test262/blob/master/harness/async-gc.js + * + * @return {Promise} Resolves to a trigger if ClearKeptObjects + * exists to provide one + */ +async function maybeGarbageCollectKeptObjectsAsync() { + let trigger; + + if (typeof ClearKeptObjects === 'function') { + trigger = ClearKeptObjects(); + } + + await maybeGarbageCollectAsync(); + + return trigger; +} + +/** + * maybeGarbageCollectAndCleanupAsync + * + * Based on "asyncGC" in https://github.com/tc39/test262/blob/master/harness/async-gc.js + * + * @return {undefined} + */ +async function maybeGarbageCollectAndCleanupAsync(...targets) { + let finalizationRegistry = new FinalizationRegistry(() => {}); + let length = targets.length; + + for (let target of targets) { + finalizationRegistry.register(target, 'target'); + target = null; + } + + targets = null; + + await 'tick'; + await maybeGarbageCollectKeptObjectsAsync(); + + let names = []; + + finalizationRegistry.cleanupSome(name => names.push(name)); + + if (names.length !== length) { + throw maybeGarbageCollectAndCleanupAsync.NOT_COLLECTED; + } +} + +maybeGarbageCollectAndCleanupAsync.NOT_COLLECTED = Symbol('Object was not collected'); + +/** + * resolveGarbageCollection + * + * Based on "resolveAsyncGC" in https://github.com/tc39/test262/blob/master/harness/async-gc.js + * + * @param {Error} error An error object. + * @return {undefined} + */ +function resolveGarbageCollection(error) { + if (error && error !== maybeGarbageCollectAndCleanupAsync.NOT_COLLECTED) { + throw error; + } +} diff --git a/testing/web-platform/tests/js/builtins/weakrefs/return-undefined-with-gc.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/return-undefined-with-gc.optional.any.js new file mode 100644 index 0000000000..a5d23bf696 --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/return-undefined-with-gc.optional.any.js @@ -0,0 +1,79 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry.prototype.cleanupSome +info: | + FinalizationRegistry.prototype.cleanupSome ( [ callback ] ) + + 1. Let finalizationRegistry be the this value. + 2. If Type(finalizationRegistry) is not Object, throw a TypeError exception. + 3. If finalizationRegistry does not have a [[Cells]] internal slot, throw a TypeError exception. + 4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception. + 5. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback). + 6. Return undefined. +---*/ + +let called; +let fn = function() { + called += 1; + return 39; +}; +let cb = function() { + called += 1; + return 42; +}; +let finalizationRegistry = new FinalizationRegistry(fn); + +function emptyCells() { + let target = {}; + finalizationRegistry.register(target); + + let prom = maybeGarbageCollectAndCleanupAsync(target); + target = null; + + return prom; +} + +promise_test(() => { + return (async () => { + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' + ); + + await emptyCells(); + called = 0; + assert_equals(finalizationRegistry.cleanupSome(cb), undefined, 'regular callback'); + assert_equals(called, 1); + + await emptyCells(); + called = 0; + assert_equals(finalizationRegistry.cleanupSome(fn), undefined, 'regular callback, same FG cleanup function'); + assert_equals(called, 1); + + await emptyCells(); + called = 0; + assert_equals(finalizationRegistry.cleanupSome(), undefined, 'undefined (implicit) callback, defer to FB callback'); + assert_equals(called, 1); + + await emptyCells(); + called = 0; + assert_equals(finalizationRegistry.cleanupSome(undefined), undefined, 'undefined (explicit) callback, defer to FB callback'); + assert_equals(called, 1); + + await emptyCells(); + assert_equals(finalizationRegistry.cleanupSome(() => 1), undefined, 'arrow function'); + + await emptyCells(); + assert_equals(finalizationRegistry.cleanupSome(async function() {}), undefined, 'async function'); + + await emptyCells(); + assert_equals(finalizationRegistry.cleanupSome(function *() {}), undefined, 'generator'); + + await emptyCells(); + assert_equals(finalizationRegistry.cleanupSome(async function *() {}), undefined, 'async generator'); + + })().catch(resolveGarbageCollection); +}, 'Return undefined regardless the result of CleanupFinalizationRegistry'); diff --git a/testing/web-platform/tests/js/builtins/weakrefs/unregister-cleaned-up-cell.optional.any.js b/testing/web-platform/tests/js/builtins/weakrefs/unregister-cleaned-up-cell.optional.any.js new file mode 100644 index 0000000000..920ea7619c --- /dev/null +++ b/testing/web-platform/tests/js/builtins/weakrefs/unregister-cleaned-up-cell.optional.any.js @@ -0,0 +1,73 @@ +// META: script=/common/gc.js +// META: script=resources/maybe-garbage-collect.js +// ├──> maybeGarbageCollectAndCleanupAsync +// └──> resolveGarbageCollection +/*--- +esid: sec-finalization-registry.prototype.unregister +info: | + FinalizationRegistry.prototype.cleanupSome ( [ callback ] ) + + 1. Let finalizationRegistry be the this value. + ... + 5. Perform ! CleanupFinalizationRegistry(finalizationRegistry, callback). + ... + + CleanupFinalizationRegistry ( finalizationRegistry [ , callback ] ) + + ... + 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is ~empty~, then an implementation may perform the following steps, + a. Choose any such cell. + b. Remove cell from finalizationRegistry.[[Cells]]. + c. Perform ? Call(callback, undefined, << cell.[[HeldValue]] >>). + ... + + FinalizationRegistry.prototype.unregister ( unregisterToken ) + + 1. Let removed be false. + 2. For each Record { [[Target]], [[Holdings]], [[UnregisterToken]] } cell that is an element of finalizationRegistry.[[Cells]], do + a. If SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then + i. Remove cell from finalizationRegistry.[[Cells]]. + ii. Set removed to true. + 3. Return removed. + +---*/ + +let value = 'target!'; +let token = {}; +let finalizationRegistry = new FinalizationRegistry(function() {}); + +function emptyCells() { + let target = {}; + finalizationRegistry.register(target, value, token); + + let prom = maybeGarbageCollectAndCleanupAsync(target); + target = null; + + return prom; +} + +promise_test(() => { + return (async () => { + assert_implements( + typeof FinalizationRegistry.prototype.cleanupSome === 'function', + 'FinalizationRegistry.prototype.cleanupSome is not implemented.' + ); + + await emptyCells(); + let called = 0; + let holdings = []; + finalizationRegistry.cleanupSome((holding) => { + called += 1; + holdings.push(holding); + }); + + assert_equals(called, 1); + assert_equals(holdings.length, 1); + assert_equals(holdings[0], value); + + let res = finalizationRegistry.unregister(token); + assert_equals(res, false, 'unregister after iterating over it in cleanup'); + + })().catch(resolveGarbageCollection); +}, 'Cannot unregister a cell that has been cleaned up'); + -- cgit v1.2.3