Revert "[heap][test] Fix weakrefs tests for conservative stack scanning"
This reverts commit 20a954f4bc
.
Reason for revert: Alas, GC stress failures:
https://ci.chromium.org/ui/p/v8/builders/ci/V8%20Linux64%20GC%20Stress%20-%20custom%20snapshot/45646/overview
Original change's description:
> [heap][test] Fix weakrefs tests for conservative stack scanning
>
> 31 out of the 36 JS tests in test/mjsunit/harmony/weakrefs/ rely on
> precise GC with the following general pattern: they allocate some
> objects, clear all references to them, invoke a GC, then perform
> some test that assumes that the GC has reclaimed the objects.
> When conservative stack scanning is used, this may fail.
>
> This CL fixes the tests, ensuring that a precise GC will be invoked
> when necessary, without scanning the stack. To achieve this, the GC
> has to be invoked in asynchronous execution mode, which ensures that
> it will be invoked from the event loop without a stack. In some
> cases, this change requires a non-trivial change in the tests.
>
> In 5 tests, part of the test's objective was to verify that a weak
> reference is not cleared before the end of the turn. In those, it
> was not possible to invoke GC asynchronously, as this would
> immediately start a new turn. These tests still use synchronous GC
> and they have been modified, if necessary, to allow for CSS (i.e.,
> to not test that all possible garbage is reclaimed after a
> sequential GC). Because of CSS, these tests may not always test
> everything that they were intended to.
>
> Tests with trivial fix:
>
> - cleanup-from-different-realm
> - cleanup
> - cleanup-proxy-from-different-realm
> - cleanupsome-2
> - cleanupsome-after-unregister
> - cleanupsome
> - finalizationregistry-keeps-holdings-alive
> - multiple-dirty-finalization-groups
> - stress-finalizationregistry-dirty-enqueue
> - undefined-holdings
> - unregister-after-cleanup
> - unregister-before-cleanup
> - unregister-called-twice
> - unregister-inside-cleanup2
> - unregister-inside-cleanup3
> - unregister-inside-cleanup
> - unregister-many
> - unregister-when-cleanup-already-scheduled
> - weak-cell-basics
>
> Tests with non-trivial fixes; same logic but very restructured:
>
> - cleanup-is-not-a-microtask:
> - cleanup-on-detached-realm
> - finalizationregistry-scheduled-for-cleanup-multiple-times
> - finalizationregistry-independent-lifetime
> - finalizationregistry-independent-lifetime-multiple
> - reentrant-gc-from-cleanup
> - symbol-in-finalizationregistry
> (was 2nd part of former symbol-as-weakref-target-gc)
> - weak-unregistertoken
>
> Tests with non-trivial fixes; same logic, restructured, using
> synchronous GC:
>
> - finalizationregistry-and-weakref
> - symbol-as-weakref-target-gc
> (was 1st part of former symbol-as-weakref-target-gc)
> - two-weakrefs
> - weakref-creation-keeps-alive
> - weakref-deref-keeps-alive
>
> Bug: v8:13257
> Bug: v8:13662
> Change-Id: I53586bd16cdb98fa976e1fa798ef498bdf286238
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4191774
> Reviewed-by: Marja Hölttä <marja@chromium.org>
> Reviewed-by: Shu-yu Guo <syg@chromium.org>
> Commit-Queue: Nikolaos Papaspyrou <nikolaos@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#85477}
Bug: v8:13257
Bug: v8:13662
Change-Id: Icc7a907928ccac058f8acdf320c21b2df04c1b78
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4192256
Auto-Submit: Shu-yu Guo <syg@chromium.org>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/main@{#85479}
This commit is contained in:
parent
930b17be77
commit
95b79bf04b
@ -4,36 +4,31 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let r = Realm.create();
|
||||
|
||||
let r = Realm.create();
|
||||
let cleanup = Realm.eval(r, "var stored_global; function cleanup() { stored_global = globalThis; } cleanup");
|
||||
let realm_global_this = Realm.eval(r, "globalThis");
|
||||
|
||||
let cleanup = Realm.eval(r, "var stored_global; function cleanup() { stored_global = globalThis; } cleanup");
|
||||
let realm_global_this = Realm.eval(r, "globalThis");
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and a register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
let weak_cell;
|
||||
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
(function () {
|
||||
let object = {};
|
||||
fg.register(object, {});
|
||||
// Object goes out of scope.
|
||||
})();
|
||||
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// Assert that the cleanup function was called in its Realm.
|
||||
let timeout_func = function () {
|
||||
let stored_global = Realm.eval(r, "stored_global;");
|
||||
assertNotEquals(stored_global, globalThis);
|
||||
assertEquals(stored_global, realm_global_this);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object, {});
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
gc();
|
||||
|
||||
// Assert that the cleanup function was called in its Realm.
|
||||
let timeout_func = function() {
|
||||
let stored_global = Realm.eval(r, "stored_global;");
|
||||
assertNotEquals(stored_global, globalThis);
|
||||
assertEquals(stored_global, realm_global_this);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -2,55 +2,60 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
// Flags: --expose-gc --noincremental-marking --allow-natives-syntax
|
||||
|
||||
// This test asserts that the cleanup function call, scheduled by GC, is
|
||||
// not a microtask but a normal task.
|
||||
// This test asserts that the cleanup function call, scheduled by GC, is a
|
||||
// microtask and not a normal task.
|
||||
|
||||
(async function () {
|
||||
// Inside a microtask, cause GC (which should schedule the cleanup as
|
||||
// microtask). lso schedule another microtask. Assert that the cleanup
|
||||
// function ran before the other microtask.
|
||||
|
||||
let microtaskInvoked = false;
|
||||
const microtask = () => {
|
||||
assertFalse(cleanedUp);
|
||||
assertFalse(microtaskInvoked);
|
||||
microtaskInvoked = true;
|
||||
};
|
||||
let cleanedUp = false;
|
||||
|
||||
let cleanedUp = false;
|
||||
const cleanup = (holdings) => {
|
||||
assertFalse(cleanedUp);
|
||||
assertTrue(microtaskInvoked);
|
||||
cleanedUp = true;
|
||||
};
|
||||
function scheduleMicrotask(func) {
|
||||
Promise.resolve().then(func);
|
||||
}
|
||||
|
||||
const fg = new FinalizationRegistry(cleanup);
|
||||
let log = [];
|
||||
|
||||
(function() {
|
||||
// Use a closure here to avoid other references to object which might keep
|
||||
// it alive (e.g., stack frames pointing to it).
|
||||
const object = {};
|
||||
fg.register(object, {});
|
||||
})();
|
||||
let cleanup = (holdings) => {
|
||||
cleanedUp = true;
|
||||
}
|
||||
|
||||
// The GC will schedule the cleanup as a regular task.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
assertFalse(cleanedUp);
|
||||
|
||||
// Schedule the microtask.
|
||||
Promise.resolve().then(microtask);
|
||||
|
||||
// Nothing else hasn't been called yet, as we're still in synchronous
|
||||
// execution.
|
||||
assertFalse(microtaskInvoked);
|
||||
assertFalse(cleanedUp);
|
||||
|
||||
// The microtask and the cleanup callbacks will verify that these two are
|
||||
// invoked in the right order: microtask -> cleanup.
|
||||
setTimeout(() => { assertTrue(cleanedUp); }, 0);
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let o = null;
|
||||
|
||||
(function() {
|
||||
// Use a closure here to avoid other references to o which might keep it alive
|
||||
// (e.g., stack frames pointing to it).
|
||||
o = {};
|
||||
fg.register(o, {});
|
||||
})();
|
||||
|
||||
let microtask = function() {
|
||||
log.push("first_microtask");
|
||||
|
||||
// cause GC during a microtask
|
||||
o = null;
|
||||
gc();
|
||||
}
|
||||
|
||||
assertFalse(cleanedUp);
|
||||
|
||||
// enqueue microtask that triggers GC
|
||||
Promise.resolve().then(microtask);
|
||||
|
||||
// but cleanup callback hasn't been called yet, as we're still in
|
||||
// synchronous execution
|
||||
assertFalse(cleanedUp);
|
||||
|
||||
// flush the microtask queue to run the microtask that triggers GC
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
// still no cleanup callback, because it runs after as a separate task
|
||||
assertFalse(cleanedUp);
|
||||
|
||||
setTimeout(() => {
|
||||
assertTrue(cleanedUp);
|
||||
}, 0);
|
||||
|
@ -4,66 +4,35 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
|
||||
const r = Realm.create();
|
||||
const FG = Realm.eval(r, "FinalizationRegistry");
|
||||
Realm.detachGlobal(r);
|
||||
|
||||
const cleanup_not_run = function (holdings) {
|
||||
assertUnreachable();
|
||||
}
|
||||
let fg_not_run = new FG(cleanup_not_run);
|
||||
|
||||
(function () {
|
||||
const object = {};
|
||||
fg_not_run.register(object, "first");
|
||||
// Object becomes unreachable.
|
||||
})();
|
||||
|
||||
let cleanedUp = false;
|
||||
let fg_run;
|
||||
|
||||
// Schedule a GC, which will schedule fg_not_run for cleanup.
|
||||
// Here and below, we need to invoke GC asynchronously and wait for it to
|
||||
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
|
||||
// may not be reclaimed because of conservative stack scanning and the test
|
||||
// may not work as intended.
|
||||
let task_1_gc = (async function () {
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// Disposing the realm cancels the already scheduled fg_not_run's finalizer.
|
||||
Realm.dispose(r);
|
||||
|
||||
const cleanup = function (holdings) {
|
||||
assertEquals(holdings, "second");
|
||||
assertFalse(cleanedUp);
|
||||
cleanedUp = true;
|
||||
}
|
||||
fg_run = new FG(cleanup);
|
||||
|
||||
// FGs that are alive after disposal can still schedule tasks.
|
||||
(function () {
|
||||
const object = {};
|
||||
fg_run.register(object, "second");
|
||||
// Object becomes unreachable.
|
||||
})();
|
||||
})();
|
||||
|
||||
// Schedule a second GC for execution after that, which will now schedule
|
||||
// fg_run for cleanup.
|
||||
let task_2_gc = (async function () {
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// Check that the cleanup task has had the chance to run yet.
|
||||
assertFalse(cleanedUp);
|
||||
})();
|
||||
|
||||
// Wait for the two GCs to be executed.
|
||||
await task_1_gc;
|
||||
await task_2_gc;
|
||||
|
||||
// Give the cleanup task a chance to run and check it worked correctly.
|
||||
setTimeout(function () { assertTrue(cleanedUp); }, 0);
|
||||
let cleanedUp = false;
|
||||
let r = Realm.create();
|
||||
let FG = Realm.eval(r, "FinalizationRegistry");
|
||||
Realm.detachGlobal(r);
|
||||
|
||||
let fg_not_run = new FG(() => {
|
||||
assertUnreachable();
|
||||
});
|
||||
(() => {
|
||||
fg_not_run.register({});
|
||||
})();
|
||||
|
||||
gc();
|
||||
|
||||
// Disposing the realm cancels the already scheduled fg_not_run's finalizer.
|
||||
Realm.dispose(r);
|
||||
|
||||
let fg = new FG(()=> {
|
||||
cleanedUp = true;
|
||||
});
|
||||
|
||||
// FGs that are alive after disposal can still schedule tasks.
|
||||
(() => {
|
||||
let object = {};
|
||||
fg.register(object, {});
|
||||
|
||||
// object becomes unreachable.
|
||||
})();
|
||||
|
||||
gc();
|
||||
|
||||
setTimeout(function() { assertTrue(cleanedUp); }, 0);
|
||||
|
@ -4,39 +4,31 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let r = Realm.create();
|
||||
|
||||
let r = Realm.create();
|
||||
let cleanup = Realm.eval(r, "var stored_global; let cleanup = new Proxy(function() { stored_global = globalThis;}, {}); cleanup");
|
||||
let realm_global_this = Realm.eval(r, "globalThis");
|
||||
|
||||
let cleanup = Realm.eval(r, "var stored_global; let cleanup = new Proxy(function() { stored_global = globalThis;}, {}); cleanup");
|
||||
let realm_global_this = Realm.eval(r, "globalThis");
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
let weak_cell;
|
||||
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
let weak_cell;
|
||||
|
||||
(function () {
|
||||
let object = {};
|
||||
fg.register(object, "holdings");
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// Assert that the cleanup function was called in its Realm.
|
||||
let timeout_func = function () {
|
||||
let stored_global = Realm.eval(r, "stored_global;");
|
||||
assertNotEquals(stored_global, globalThis);
|
||||
assertEquals(stored_global, realm_global_this);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object, "holdings");
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
gc();
|
||||
|
||||
// Assert that the cleanup function was called in its Realm.
|
||||
let timeout_func = function() {
|
||||
let stored_global = Realm.eval(r, "stored_global;");
|
||||
assertNotEquals(stored_global, globalThis);
|
||||
assertEquals(stored_global, realm_global_this);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,56 +4,48 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_called = 0;
|
||||
let holdings_list = [];
|
||||
let cleanup = function(holdings) {
|
||||
holdings_list.push(holdings);
|
||||
cleanup_called++;
|
||||
}
|
||||
|
||||
let cleanup_called = 0;
|
||||
let holdings_list = [];
|
||||
let cleanup = function (holdings) {
|
||||
holdings_list.push(holdings);
|
||||
cleanup_called++;
|
||||
}
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let o1 = {};
|
||||
let o2 = {};
|
||||
|
||||
// Ignition holds references to objects in temporary registers. These will be
|
||||
// released when the function exits. So only access o inside a function to
|
||||
// prevent any references to objects in temporary registers when a gc is
|
||||
(function () {
|
||||
fg.register(o1, 1);
|
||||
fg.register(o2, 2);
|
||||
})();
|
||||
|
||||
// Here and below, we need to invoke GC asynchronously and wait for it to
|
||||
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
|
||||
// may not be reclaimed because of conservative stack scanning and the test
|
||||
// may not work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(cleanup_called, 0);
|
||||
|
||||
// Drop the last references to o1 and o2.
|
||||
(function () {
|
||||
o1 = null;
|
||||
o2 = null;
|
||||
})();
|
||||
|
||||
// GC will reclaim the target objects; the cleanup function will be called the
|
||||
// next time we enter the event loop.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(cleanup_called, 0);
|
||||
|
||||
let timeout_func = function () {
|
||||
assertEquals(cleanup_called, 2);
|
||||
assertEquals(holdings_list.length, 2);
|
||||
if (holdings_list[0] == 1) {
|
||||
assertEquals(holdings_list[1], 2);
|
||||
} else {
|
||||
assertEquals(holdings_list[0], 2);
|
||||
assertEquals(holdings_list[1], 1);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let o1 = {};
|
||||
let o2 = {};
|
||||
|
||||
// Ignition holds references to objects in temporary registers. These will be
|
||||
// released when the function exits. So only access o inside a function to
|
||||
// prevent any references to objects in temporary registers when a gc is
|
||||
(function() {
|
||||
fg.register(o1, 1);
|
||||
fg.register(o2, 2);
|
||||
})();
|
||||
|
||||
gc();
|
||||
assertEquals(cleanup_called, 0);
|
||||
|
||||
// Drop the last references to o1 and o2.
|
||||
(function() {
|
||||
o1 = null;
|
||||
o2 = null;
|
||||
})();
|
||||
|
||||
// GC will reclaim the target objects; the cleanup function will be called the
|
||||
// next time we enter the event loop.
|
||||
gc();
|
||||
assertEquals(cleanup_called, 0);
|
||||
|
||||
let timeout_func = function() {
|
||||
assertEquals(cleanup_called, 2);
|
||||
assertEquals(holdings_list.length, 2);
|
||||
if (holdings_list[0] == 1) {
|
||||
assertEquals(holdings_list[1], 2);
|
||||
} else {
|
||||
assertEquals(holdings_list[0], 2);
|
||||
assertEquals(holdings_list[1], 1);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,34 +4,26 @@
|
||||
|
||||
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking --allow-natives-syntax
|
||||
|
||||
(async function () {
|
||||
let cleanup_count = 0;
|
||||
let cleanup_holdings = [];
|
||||
let cleanup = function(holdings) {
|
||||
cleanup_holdings.push(holdings);
|
||||
++cleanup_count;
|
||||
}
|
||||
|
||||
let cleanup_count = 0;
|
||||
let cleanup_holdings = [];
|
||||
let cleanup = function (holdings) {
|
||||
cleanup_holdings.push(holdings);
|
||||
++cleanup_count;
|
||||
}
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
(function () {
|
||||
let o = {};
|
||||
fg.register(o, "holdings");
|
||||
|
||||
assertEquals(0, cleanup_count);
|
||||
})();
|
||||
|
||||
// GC will detect o as dead.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// passing no callback, should trigger cleanup function
|
||||
fg.cleanupSome();
|
||||
assertEquals(1, cleanup_count);
|
||||
assertEquals(1, cleanup_holdings.length);
|
||||
assertEquals("holdings", cleanup_holdings[0]);
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
(function() {
|
||||
let o = {};
|
||||
fg.register(o, "holdings");
|
||||
|
||||
assertEquals(0, cleanup_count);
|
||||
})();
|
||||
|
||||
// GC will detect o as dead.
|
||||
gc();
|
||||
|
||||
// passing no callback, should trigger cleanup function
|
||||
fg.cleanupSome();
|
||||
assertEquals(1, cleanup_count);
|
||||
assertEquals(1, cleanup_holdings.length);
|
||||
assertEquals("holdings", cleanup_holdings[0]);
|
||||
|
@ -4,39 +4,31 @@
|
||||
|
||||
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_count = 0;
|
||||
let cleanup_holdings = [];
|
||||
let cleanup = function(holdings) {
|
||||
cleanup_holdings.push(holdings);
|
||||
++cleanup_count;
|
||||
}
|
||||
|
||||
let cleanup_count = 0;
|
||||
let cleanup_holdings = [];
|
||||
let cleanup = function (holdings) {
|
||||
cleanup_holdings.push(holdings);
|
||||
++cleanup_count;
|
||||
}
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key = { "k": "this is the key" };
|
||||
(function () {
|
||||
let o = {};
|
||||
weak_cell = fg.register(o, "holdings", key);
|
||||
|
||||
// cleanupSome won't do anything since there are no reclaimed targets.
|
||||
fg.cleanupSome();
|
||||
assertEquals(0, cleanup_count);
|
||||
return o;
|
||||
})();
|
||||
|
||||
// GC will detect the WeakCell as dirty.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// Unregister the tracked object just before calling cleanupSome.
|
||||
fg.unregister(key);
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key = {"k": "this is the key"};
|
||||
(function() {
|
||||
let o = {};
|
||||
weak_cell = fg.register(o, "holdings", key);
|
||||
|
||||
// cleanupSome won't do anything since there are no reclaimed targets.
|
||||
fg.cleanupSome();
|
||||
|
||||
assertEquals(0, cleanup_count);
|
||||
|
||||
return o;
|
||||
})();
|
||||
|
||||
// GC will detect the WeakCell as dirty.
|
||||
gc();
|
||||
|
||||
// Unregister the tracked object just before calling cleanupSome.
|
||||
fg.unregister(key);
|
||||
|
||||
fg.cleanupSome();
|
||||
|
||||
assertEquals(0, cleanup_count);
|
||||
|
@ -4,39 +4,31 @@
|
||||
|
||||
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking --allow-natives-syntax
|
||||
|
||||
(async function () {
|
||||
let cleanup_count = 0;
|
||||
let cleanup_holdings = [];
|
||||
let cleanup = function(holdings) {
|
||||
%AbortJS("shouldn't be called");
|
||||
}
|
||||
|
||||
let cleanup_count = 0;
|
||||
let cleanup_holdings = [];
|
||||
let cleanup = function (holdings) {
|
||||
%AbortJS("shouldn't be called");
|
||||
}
|
||||
let cleanup2 = function(holdings) {
|
||||
cleanup_holdings.push(holdings);
|
||||
++cleanup_count;
|
||||
}
|
||||
|
||||
let cleanup2 = function (holdings) {
|
||||
cleanup_holdings.push(holdings);
|
||||
++cleanup_count;
|
||||
}
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
(function () {
|
||||
let o = {};
|
||||
fg.register(o, "holdings");
|
||||
|
||||
// cleanupSome won't do anything since there are no reclaimed targets.
|
||||
fg.cleanupSome(cleanup2);
|
||||
assertEquals(0, cleanup_count);
|
||||
})();
|
||||
|
||||
// GC will detect o as dead.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
(function() {
|
||||
let o = {};
|
||||
fg.register(o, "holdings");
|
||||
|
||||
// cleanupSome won't do anything since there are no reclaimed targets.
|
||||
fg.cleanupSome(cleanup2);
|
||||
assertEquals(1, cleanup_count);
|
||||
assertEquals(1, cleanup_holdings.length);
|
||||
assertEquals("holdings", cleanup_holdings[0]);
|
||||
|
||||
assertEquals(0, cleanup_count);
|
||||
})();
|
||||
|
||||
// GC will detect o as dead.
|
||||
gc();
|
||||
|
||||
fg.cleanupSome(cleanup2);
|
||||
assertEquals(1, cleanup_count);
|
||||
assertEquals(1, cleanup_holdings.length);
|
||||
assertEquals("holdings", cleanup_holdings[0]);
|
||||
|
@ -4,46 +4,39 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_called = false;
|
||||
let cleanup = function(holdings) {
|
||||
assertFalse(cleanup_called);
|
||||
let holdings_list = [];
|
||||
holdings_list.push(holdings);
|
||||
assertEquals(1, holdings_list.length);
|
||||
assertEquals("holdings", holdings_list[0]);
|
||||
cleanup_called = true;
|
||||
}
|
||||
|
||||
let cleanup_called = false;
|
||||
const cleanup = function(holdings) {
|
||||
assertFalse(cleanup_called);
|
||||
assertEquals("holdings", holdings);
|
||||
cleanup_called = true;
|
||||
}
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let weak_ref;
|
||||
(function() {
|
||||
let o = {};
|
||||
weak_ref = new WeakRef(o);
|
||||
fg.register(o, "holdings");
|
||||
})();
|
||||
|
||||
const fg = new FinalizationRegistry(cleanup);
|
||||
let weak_ref;
|
||||
(function() {
|
||||
const o = {};
|
||||
weak_ref = new WeakRef(o);
|
||||
fg.register(o, "holdings");
|
||||
})();
|
||||
// Since the WeakRef was created during this turn, it is not cleared by GC. The
|
||||
// pointer inside the FinalizationRegistry is not cleared either, since the WeakRef
|
||||
// keeps the target object alive.
|
||||
gc();
|
||||
(function() {
|
||||
assertNotEquals(undefined, weak_ref.deref());
|
||||
})();
|
||||
|
||||
// Since the WeakRef was created during this turn, it is not cleared by GC. The
|
||||
// pointer inside the FinalizationRegistry is not cleared either, since the WeakRef
|
||||
// keeps the target object alive.
|
||||
// Here we invoke GC synchronously and, with conservative stack scanning, there is
|
||||
// a chance that the object is not reclaimed now. In any case, the WeakRef should
|
||||
// not be cleared.
|
||||
// Trigger gc in next task
|
||||
setTimeout(() => {
|
||||
gc();
|
||||
|
||||
assertNotEquals(undefined, weak_ref.deref());
|
||||
assertFalse(cleanup_called);
|
||||
|
||||
// Trigger GC in next task. Now the WeakRef is cleared but the cleanup has
|
||||
// not been called yet.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
assertEquals(undefined, weak_ref.deref());
|
||||
assertFalse(cleanup_called);
|
||||
|
||||
// Check that the cleanup callback was called in a follow up task.
|
||||
setTimeout(() => { assertTrue(cleanup_called); }, 0);
|
||||
|
||||
})();
|
||||
// Check that cleanup callback was called in a follow up task
|
||||
setTimeout(() => {
|
||||
assertTrue(cleanup_called);
|
||||
assertEquals(undefined, weak_ref.deref());
|
||||
}, 0);
|
||||
}, 0);
|
||||
|
@ -4,49 +4,29 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking --no-concurrent-inlining
|
||||
|
||||
(async function () {
|
||||
|
||||
let cleanup_called = false;
|
||||
function cleanup(holdings) {
|
||||
cleanup_called = true;
|
||||
};
|
||||
|
||||
let cleanup_called_2 = false;
|
||||
function cleanup2(holdings) {
|
||||
cleanup_called_2 = true;
|
||||
};
|
||||
|
||||
const fg = new FinalizationRegistry(cleanup);
|
||||
|
||||
let task_1_gc = (async function () {
|
||||
const fg2 = new FinalizationRegistry(cleanup2);
|
||||
|
||||
(function () {
|
||||
fg.register({}, "holdings1");
|
||||
fg2.register({}, "holdings2");
|
||||
})();
|
||||
|
||||
// Schedule fg and fg2 for cleanup.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertFalse(cleanup_called);
|
||||
assertFalse(cleanup_called_2);
|
||||
})();
|
||||
|
||||
// Schedule a task to collect fg2, but fg is still alive.
|
||||
let task_2_gc = (async function () {
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertFalse(cleanup_called);
|
||||
assertFalse(cleanup_called_2);
|
||||
let cleanup_called = false;
|
||||
function cleanup(holdings) {
|
||||
cleanup_called = true;
|
||||
};
|
||||
let cleanup_called_2 = false;
|
||||
function cleanup2(holdings) {
|
||||
cleanup_called_2 = true;
|
||||
};
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
(function() {
|
||||
let fg2 = new FinalizationRegistry(cleanup2);
|
||||
(function() {
|
||||
fg.register({}, {});
|
||||
fg2.register({}, {});
|
||||
})();
|
||||
|
||||
// Wait for the two GC tasks to be executed.
|
||||
await task_1_gc;
|
||||
await task_2_gc;
|
||||
|
||||
// Check that only the cleanup for fg will be called.
|
||||
setTimeout(function() {
|
||||
assertTrue(cleanup_called);
|
||||
assertFalse(cleanup_called_2);
|
||||
}, 0);
|
||||
|
||||
// Schedule fg and fg2 for cleanup.
|
||||
gc();
|
||||
})();
|
||||
|
||||
// Collect fg2, but fg is still alive.
|
||||
gc();
|
||||
|
||||
setTimeout(function() {
|
||||
assertTrue(cleanup_called);
|
||||
assertFalse(cleanup_called_2);
|
||||
}, 0);
|
||||
|
@ -4,38 +4,22 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking --no-concurrent-recompilation
|
||||
|
||||
(async function () {
|
||||
|
||||
let cleanup_called = false;
|
||||
function cleanup(holdings) {
|
||||
cleanup_called = true;
|
||||
};
|
||||
|
||||
let task_1_gc = (async function () {
|
||||
const fg = new FinalizationRegistry(cleanup);
|
||||
|
||||
(function () {
|
||||
let x = {};
|
||||
fg.register(x, "holdings");
|
||||
x = null;
|
||||
})();
|
||||
|
||||
// Schedule fg for cleanup.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertFalse(cleanup_called);
|
||||
let cleanup_called = false;
|
||||
function cleanup(holdings) {
|
||||
cleanup_called = true;
|
||||
};
|
||||
(function() {
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
(function() {
|
||||
let x = {};
|
||||
fg.register(x, {});
|
||||
x = null;
|
||||
})();
|
||||
|
||||
// Schedule a task to collect fg, which should result in cleanup not called.
|
||||
let task_2_gc = (async function () {
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertFalse(cleanup_called);
|
||||
})();
|
||||
|
||||
// Wait for the two GC tasks to be executed.
|
||||
await task_1_gc;
|
||||
await task_2_gc;
|
||||
|
||||
// Check that the cleanup will not be called.
|
||||
setTimeout(function () { assertFalse(cleanup_called); }, 0);
|
||||
|
||||
// Schedule fg for cleanup.
|
||||
gc();
|
||||
})();
|
||||
|
||||
// Collect fg, which should result in cleanup not called.
|
||||
gc();
|
||||
|
||||
setTimeout(function() { assertFalse(cleanup_called); }, 0);
|
||||
|
@ -4,48 +4,40 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
|
||||
let cleanup_called = false;
|
||||
let holdings_list = [];
|
||||
let cleanup = function (holdings) {
|
||||
assertFalse(cleanup_called);
|
||||
holdings_list.push(holdings);
|
||||
cleanup_called = true;
|
||||
}
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let o1 = {};
|
||||
let holdings = { 'a': 'this is the holdings object' };
|
||||
|
||||
// Ignition holds references to objects in temporary registers. These will be
|
||||
// released when the function exits. So only access o inside a function to
|
||||
// prevent any references to objects in temporary registers when a gc is
|
||||
// triggered.
|
||||
(() => { fg.register(o1, holdings); })()
|
||||
|
||||
// Here and below, we need to invoke GC asynchronously and wait for it to
|
||||
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
|
||||
// may not be reclaimed because of conservative stack scanning and the test
|
||||
// may not work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
let cleanup_called = false;
|
||||
let holdings_list = [];
|
||||
let cleanup = function(holdings) {
|
||||
assertFalse(cleanup_called);
|
||||
holdings_list.push(holdings);
|
||||
cleanup_called = true;
|
||||
}
|
||||
|
||||
// Drop the last references to o1.
|
||||
(() => { o1 = null; })()
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let o1 = {};
|
||||
let holdings = {'a': 'this is the holdings object'};
|
||||
|
||||
// Drop the last reference to the holdings. The FinalizationRegistry keeps it
|
||||
// alive, so the cleanup function will be called as normal.
|
||||
holdings = null;
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertFalse(cleanup_called);
|
||||
// Ignition holds references to objects in temporary registers. These will be
|
||||
// released when the function exits. So only access o inside a function to
|
||||
// prevent any references to objects in temporary registers when a gc is
|
||||
// triggered.
|
||||
(() => {fg.register(o1, holdings);})()
|
||||
|
||||
let timeout_func = function () {
|
||||
assertTrue(cleanup_called);
|
||||
assertEquals(holdings_list.length, 1);
|
||||
assertEquals(holdings_list[0].a, "this is the holdings object");
|
||||
}
|
||||
gc();
|
||||
assertFalse(cleanup_called);
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
// Drop the last references to o1.
|
||||
(() => {o1 = null;})()
|
||||
|
||||
})();
|
||||
// Drop the last reference to the holdings. The FinalizationRegistry keeps it
|
||||
// alive, so the cleanup function will be called as normal.
|
||||
holdings = null;
|
||||
gc();
|
||||
assertFalse(cleanup_called);
|
||||
|
||||
let timeout_func = function() {
|
||||
assertTrue(cleanup_called);
|
||||
assertEquals(holdings_list.length, 1);
|
||||
assertEquals(holdings_list[0].a, "this is the holdings object");
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -5,78 +5,63 @@
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
// Flags: --no-stress-flush-code
|
||||
|
||||
(async function () {
|
||||
let cleanup0_call_count = 0;
|
||||
let cleanup0_holdings_count = 0;
|
||||
|
||||
let cleanup0_call_count = 0;
|
||||
let cleanup1_call_count = 0;
|
||||
let cleanup1_call_count = 0;
|
||||
let cleanup1_holdings_count = 0;
|
||||
|
||||
let cleanup0 = function (holdings) {
|
||||
++cleanup0_call_count;
|
||||
}
|
||||
let cleanup0 = function(holdings) {
|
||||
++cleanup0_holdings_count;
|
||||
++cleanup0_call_count;
|
||||
}
|
||||
|
||||
let cleanup1 = function (holdings) {
|
||||
++cleanup1_call_count;
|
||||
}
|
||||
let cleanup1 = function(holdings) {
|
||||
++cleanup1_holdings_count;
|
||||
++cleanup1_call_count;
|
||||
}
|
||||
|
||||
let fg0 = new FinalizationRegistry(cleanup0);
|
||||
let fg1 = new FinalizationRegistry(cleanup1);
|
||||
let fg0 = new FinalizationRegistry(cleanup0);
|
||||
let fg1 = new FinalizationRegistry(cleanup1);
|
||||
|
||||
// Register 1 weak reference for each FinalizationRegistry and kill the
|
||||
// objects they point to.
|
||||
(function () {
|
||||
// The objects need to be inside a closure so that we can reliably kill
|
||||
// them.
|
||||
let objects = [];
|
||||
objects[0] = {};
|
||||
objects[1] = {};
|
||||
fg0.register(objects[0], "holdings0-0");
|
||||
fg1.register(objects[1], "holdings1-0");
|
||||
// Drop the references to the objects.
|
||||
objects = [];
|
||||
})();
|
||||
// Register 1 weak reference for each FinalizationRegistry and kill the objects they point to.
|
||||
(function() {
|
||||
// The objects need to be inside a closure so that we can reliably kill them.
|
||||
let objects = [];
|
||||
objects[0] = {};
|
||||
objects[1] = {};
|
||||
|
||||
// Schedule a GC, which will schedule both fg0 and fg1 for cleanup.
|
||||
// Here and below, we need to invoke GC asynchronously and wait for it to
|
||||
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
|
||||
// may not be reclaimed because of conservative stack scanning and the test
|
||||
// may not work as intended.
|
||||
let task_1_gc = (async function () {
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// Before the cleanup task has a chance to run, do the same thing again, so
|
||||
// both FinalizationRegistries are (again) scheduled for cleanup. This has to
|
||||
// be a IIFE function (so that we can reliably kill the objects) so we cannot
|
||||
// use the same function as before.
|
||||
(function () {
|
||||
let objects = [];
|
||||
objects[0] = {};
|
||||
objects[1] = {};
|
||||
fg0.register(objects[0], "holdings0-1");
|
||||
fg1.register(objects[1], "holdings1-1");
|
||||
objects = [];
|
||||
})();
|
||||
})();
|
||||
|
||||
// Schedule a second GC for execution after that, which will again schedule
|
||||
// both fg0 and fg1 for cleanup.
|
||||
let task_2_gc = (async function () {
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// Check that no cleanup task has had the chance to run yet.
|
||||
assertEquals(0, cleanup0_call_count);
|
||||
assertEquals(0, cleanup1_call_count);
|
||||
})();
|
||||
|
||||
// Wait for the two GCs to be executed.
|
||||
await task_1_gc;
|
||||
await task_2_gc;
|
||||
|
||||
let timeout_func = function () {
|
||||
assertEquals(2, cleanup0_call_count);
|
||||
assertEquals(2, cleanup1_call_count);
|
||||
}
|
||||
|
||||
// Give the cleanup task a chance to run and check it worked correctly.
|
||||
setTimeout(timeout_func, 0);
|
||||
fg0.register(objects[0], "holdings0-0");
|
||||
fg1.register(objects[1], "holdings1-0");
|
||||
|
||||
// Drop the references to the objects.
|
||||
objects = [];
|
||||
})();
|
||||
|
||||
// Will schedule both fg0 and fg1 for cleanup.
|
||||
gc();
|
||||
|
||||
// Before the cleanup task has a chance to run, do the same thing again, so both
|
||||
// FinalizationRegistries are (again) scheduled for cleanup. This has to be a IIFE function
|
||||
// (so that we can reliably kill the objects) so we cannot use the same function
|
||||
// as before.
|
||||
(function() {
|
||||
let objects = [];
|
||||
objects[0] = {};
|
||||
objects[1] = {};
|
||||
fg0.register(objects[0], "holdings0-1");
|
||||
fg1.register(objects[1], "holdings1-1");
|
||||
objects = [];
|
||||
})();
|
||||
|
||||
gc();
|
||||
|
||||
let timeout_func = function() {
|
||||
assertEquals(2, cleanup0_call_count);
|
||||
assertEquals(2, cleanup0_holdings_count);
|
||||
assertEquals(2, cleanup1_call_count);
|
||||
assertEquals(2, cleanup1_holdings_count);
|
||||
}
|
||||
|
||||
// Give the cleanup task a chance to run.
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,42 +4,37 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup_holdings_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
++cleanup_holdings_count;
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function (holdings) {
|
||||
++cleanup_call_count;
|
||||
}
|
||||
let fg1 = new FinalizationRegistry(cleanup);
|
||||
let fg2 = new FinalizationRegistry(cleanup);
|
||||
|
||||
let fg1 = new FinalizationRegistry(cleanup);
|
||||
let fg2 = new FinalizationRegistry(cleanup);
|
||||
// Create two objects and register them in FinalizationRegistries. The objects need
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
|
||||
// Create two objects and register them in FinalizationRegistries. The objects need
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
(function() {
|
||||
let object1 = {};
|
||||
fg1.register(object1, "holdings1");
|
||||
|
||||
(function () {
|
||||
let object1 = {};
|
||||
fg1.register(object1, "holdings1");
|
||||
|
||||
let object2 = {};
|
||||
fg2.register(object2, "holdings2");
|
||||
|
||||
// object1 and object2 go out of scope.
|
||||
})();
|
||||
|
||||
// This GC will discover dirty WeakCells and schedule cleanup.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function () {
|
||||
assertEquals(2, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
let object2 = {};
|
||||
fg2.register(object2, "holdings2");
|
||||
|
||||
// object1 and object2 go out of scope.
|
||||
})();
|
||||
|
||||
// This GC will discover dirty WeakCells and schedule cleanup.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function() {
|
||||
assertEquals(2, cleanup_call_count);
|
||||
assertEquals(2, cleanup_holdings_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,27 +4,20 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let call_count = 0;
|
||||
let reentrant_gc = function(holdings) {
|
||||
gc();
|
||||
call_count++;
|
||||
}
|
||||
|
||||
let call_count = 0;
|
||||
const reentrant_gc = function (holdings) {
|
||||
gc();
|
||||
call_count++;
|
||||
}
|
||||
let fg = new FinalizationRegistry(reentrant_gc);
|
||||
|
||||
const fg = new FinalizationRegistry(reentrant_gc);
|
||||
(function() {
|
||||
fg.register({}, 42);
|
||||
})();
|
||||
|
||||
(function () {
|
||||
fg.register({}, 42);
|
||||
})();
|
||||
gc();
|
||||
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, call_count);
|
||||
|
||||
setTimeout(function () { assertEquals(1, call_count); }, 0);
|
||||
|
||||
})();
|
||||
setTimeout(function() {
|
||||
assertEquals(1, call_count);
|
||||
}, 0);
|
||||
|
@ -4,41 +4,33 @@
|
||||
|
||||
// Flags: --stress-compaction --expose-gc
|
||||
|
||||
(async function () {
|
||||
// Test that the dirty FinalizationRegistries that are enqueued during GC have
|
||||
// their slots correctly recorded by the GC.
|
||||
|
||||
// Test that the dirty FinalizationRegistries that are enqueued during GC have
|
||||
// their slots correctly recorded by the GC.
|
||||
// 1) Create many JSFinalizationRegistry objects so that they span several pages
|
||||
// (page size is 256kb).
|
||||
let registries = [];
|
||||
for (let i = 0; i < 1024 * 8; i++) {
|
||||
registries.push(new FinalizationRegistry(() => {}));
|
||||
}
|
||||
|
||||
// 1) Create many JSFinalizationRegistry objects so that they span several pages
|
||||
// (page size is 256kb).
|
||||
let registries = [];
|
||||
for (let i = 0; i < 1024 * 8; i++) {
|
||||
registries.push(new FinalizationRegistry(() => { }));
|
||||
}
|
||||
|
||||
// 2) Force two GCs to ensure that JSFinalizatonRegistry objects are tenured.
|
||||
// Here and below, we need to invoke GC asynchronously and wait for it to
|
||||
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
|
||||
// may not be reclaimed because of conservative stack scanning and the test
|
||||
// may not work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// 3) In a function: create a dummy target and register it in all
|
||||
// JSFinalizatonRegistry objects.
|
||||
(function () {
|
||||
let garbage = {};
|
||||
registries.forEach((fr) => {
|
||||
fr.register(garbage, 42);
|
||||
});
|
||||
garbage = null;
|
||||
})();
|
||||
|
||||
// 4) Outside the function where the target is unreachable: force GC to collect
|
||||
// the object.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
// 5) Force another GC to test that the slot was correctly updated.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
// 2) Force two GCs to ensure that JSFinalizatonRegistry objects are tenured.
|
||||
gc();
|
||||
gc();
|
||||
|
||||
// 3) In a function: create a dummy target and register it in all
|
||||
// JSFinalizatonRegistry objects.
|
||||
(function() {
|
||||
let garbage = {};
|
||||
registries.forEach((fr) => {
|
||||
fr.register(garbage, 42);
|
||||
});
|
||||
garbage = null;
|
||||
})();
|
||||
|
||||
// 4) Outside the function where the target is unreachable: force GC to collect
|
||||
// the object.
|
||||
gc();
|
||||
|
||||
// 5) Force another GC to test that the slot was correctly updated.
|
||||
gc();
|
||||
|
@ -4,29 +4,36 @@
|
||||
|
||||
// Flags: --harmony-symbol-as-weakmap-key --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
|
||||
(function TestWeakRefWithSymbolGC() {
|
||||
let weakRef;
|
||||
(function () {
|
||||
{
|
||||
const innerKey = Symbol('123');
|
||||
weakRef = new WeakRef(innerKey);
|
||||
})();
|
||||
|
||||
}
|
||||
// Since the WeakRef was created during this turn, it is not cleared by GC.
|
||||
// Here we invoke GC synchronously and, with conservative stack scanning, there is
|
||||
// a chance that the object is not reclaimed now. In any case, the WeakRef should
|
||||
// not be cleared.
|
||||
gc();
|
||||
|
||||
assertNotEquals(undefined, weakRef.deref());
|
||||
|
||||
// Trigger GC again in next task. Now the WeakRef is cleared.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
assertEquals(undefined, weakRef.deref());
|
||||
|
||||
// Next task.
|
||||
setTimeout(() => {
|
||||
gc();
|
||||
assertEquals(undefined, weakRef.deref());
|
||||
}, 0);
|
||||
})();
|
||||
|
||||
(function TestFinalizationRegistryWithSymbolGC() {
|
||||
let cleanUpCalled = false;
|
||||
const fg = new FinalizationRegistry((target) => {
|
||||
assertEquals('123', target);
|
||||
cleanUpCalled = true;
|
||||
});
|
||||
(function () {
|
||||
const innerKey = Symbol('123');
|
||||
fg.register(innerKey, '123');
|
||||
})();
|
||||
gc();
|
||||
assertFalse(cleanUpCalled);
|
||||
// Check that cleanup callback was called in a follow up task.
|
||||
setTimeout(() => {
|
||||
assertTrue(cleanUpCalled);
|
||||
}, 0);
|
||||
})();
|
||||
|
@ -1,30 +0,0 @@
|
||||
// Copyright 2023 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-symbol-as-weakmap-key --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
|
||||
let cleanUpCalled = false;
|
||||
const fg = new FinalizationRegistry((target) => {
|
||||
assertEquals('123', target);
|
||||
cleanUpCalled = true;
|
||||
});
|
||||
|
||||
(function () {
|
||||
const innerKey = Symbol('123');
|
||||
fg.register(innerKey, '123');
|
||||
})();
|
||||
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertFalse(cleanUpCalled);
|
||||
|
||||
// Check that cleanup callback was called in a follow up task.
|
||||
setTimeout(() => { assertTrue(cleanUpCalled); }, 0);
|
||||
|
||||
})();
|
@ -14,9 +14,6 @@ let wr2;
|
||||
})();
|
||||
|
||||
// Since the WeakRefs were created during this turn, they're not cleared by GC.
|
||||
// Here and below, we invoke GC synchronously and, with conservative stack
|
||||
// scanning, there is a chance that the object is not reclaimed now. In any
|
||||
// case, the WeakRef should not be cleared.
|
||||
gc();
|
||||
|
||||
(function() {
|
||||
@ -26,29 +23,25 @@ gc();
|
||||
|
||||
// New task
|
||||
setTimeout(function() {
|
||||
(function () { wr1.deref(); })();
|
||||
wr1.deref();
|
||||
o1 = null;
|
||||
gc(); // deref makes sure we don't clean up wr1
|
||||
(function () { assertNotEquals(undefined, wr1.deref()); })();
|
||||
|
||||
// New task
|
||||
setTimeout(function() {
|
||||
(function () { wr2.deref(); })();
|
||||
wr2.deref();
|
||||
o2 = null;
|
||||
gc(); // deref makes sure we don't clean up wr2
|
||||
(function () { assertNotEquals(undefined, wr2.deref()); })();
|
||||
|
||||
// New task
|
||||
(async function () {
|
||||
// Trigger GC again to make sure the two WeakRefs are cleared.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
setTimeout(function() {
|
||||
assertEquals(undefined, wr1.deref());
|
||||
assertEquals(undefined, wr2.deref());
|
||||
})();
|
||||
gc();
|
||||
|
||||
// New task
|
||||
setTimeout(function() {
|
||||
assertEquals(undefined, wr2.deref());
|
||||
}, 0);
|
||||
}, 0);
|
||||
}, 0);
|
||||
}, 0);
|
||||
|
@ -4,39 +4,34 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup_holdings_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
assertEquals(holdings, undefined);
|
||||
++cleanup_holdings_count;
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function (holdings) {
|
||||
assertEquals(holdings, undefined);
|
||||
++cleanup_call_count;
|
||||
}
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
|
||||
// a closure so that we can reliably kill them!
|
||||
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
|
||||
// a closure so that we can reliably kill them!
|
||||
|
||||
(function () {
|
||||
let object = {};
|
||||
fg.register(object);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target object and schedule cleanup.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function () {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target object and schedule cleanup.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
assertEquals(1, cleanup_holdings_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,46 +4,42 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup_holdings_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
assertEquals("holdings", holdings);
|
||||
++cleanup_holdings_count;
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function (holdings) {
|
||||
assertEquals("holdings", holdings);
|
||||
++cleanup_call_count;
|
||||
}
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key = {"k": "this is the key"};
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key = { "k": "this is the key" };
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
|
||||
(function () {
|
||||
let object = {};
|
||||
fg.register(object, "holdings", key);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target object and schedule cleanup.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function () {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
|
||||
// Unregister an already cleaned-up weak reference.
|
||||
let success = fg.unregister(key);
|
||||
assertFalse(success);
|
||||
|
||||
// Assert that it didn't do anything.
|
||||
setTimeout(() => { assertEquals(1, cleanup_call_count); }, 0);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object, "holdings", key);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target object and schedule cleanup.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
assertEquals(1, cleanup_holdings_count);
|
||||
|
||||
// Unregister an already cleaned-up weak reference.
|
||||
let success = fg.unregister(key);
|
||||
assertFalse(success);
|
||||
|
||||
// Assert that it didn't do anything.
|
||||
setTimeout(() => { assertEquals(1, cleanup_call_count); }, 0);
|
||||
setTimeout(() => { assertEquals(1, cleanup_holdings_count); }, 0);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,42 +4,34 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function (holdings) {
|
||||
++cleanup_call_count;
|
||||
}
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key = {"k": "this is the key"};
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key = { "k": "this is the key" };
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object, "my holdings", key);
|
||||
|
||||
(function () {
|
||||
let object = {};
|
||||
fg.register(object, "my holdings", key);
|
||||
|
||||
// Clear the WeakCell before the GC has a chance to discover it.
|
||||
let success = fg.unregister(key);
|
||||
assertTrue(success);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target object.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function won't be called, since we called unregister.
|
||||
let timeout_func = function () {
|
||||
assertEquals(0, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
// Clear the WeakCell before the GC has a chance to discover it.
|
||||
let success = fg.unregister(key);
|
||||
assertTrue(success);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target object.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function won't be called, since we called unregister.
|
||||
let timeout_func = function() {
|
||||
assertEquals(0, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,47 +4,39 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function (holdings) {
|
||||
++cleanup_call_count;
|
||||
}
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key = {"k": "this is the key"};
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key = { "k": "this is the key" };
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs
|
||||
// to be inside a closure so that we can reliably kill them!
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object, "holdings", key);
|
||||
|
||||
(function () {
|
||||
let object = {};
|
||||
fg.register(object, "holdings", key);
|
||||
// Unregister before the GC has a chance to discover the object.
|
||||
let success = fg.unregister(key);
|
||||
assertTrue(success);
|
||||
|
||||
// Unregister before the GC has a chance to discover the object.
|
||||
let success = fg.unregister(key);
|
||||
assertTrue(success);
|
||||
|
||||
// Call unregister again (just to assert we handle this gracefully).
|
||||
success = fg.unregister(key);
|
||||
assertFalse(success);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target object.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function won't be called, since the weak reference
|
||||
// was unregistered.
|
||||
let timeout_func = function () {
|
||||
assertEquals(0, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
// Call unregister again (just to assert we handle this gracefully).
|
||||
success = fg.unregister(key);
|
||||
assertFalse(success);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target object.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function won't be called, since the weak reference
|
||||
// was unregistered.
|
||||
let timeout_func = function() {
|
||||
assertEquals(0, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,42 +4,37 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup_holdings_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
assertEquals(holdings, "holdings");
|
||||
let success = fg.unregister(key);
|
||||
assertFalse(success);
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function (holdings) {
|
||||
assertEquals(holdings, "holdings");
|
||||
let success = fg.unregister(key);
|
||||
assertFalse(success);
|
||||
++cleanup_holdings_count;
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
++cleanup_call_count;
|
||||
}
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
|
||||
// a closure so that we can reliably kill them!
|
||||
let key = {"k": "this is the key"};
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
|
||||
// a closure so that we can reliably kill them!
|
||||
let key = { "k": "this is the key" };
|
||||
|
||||
(function () {
|
||||
let object = {};
|
||||
fg.register(object, "holdings", key);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will discover dirty WeakCells and schedule cleanup.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function () {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object, "holdings", key);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will discover dirty WeakCells and schedule cleanup.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
assertEquals(1, cleanup_holdings_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,50 +4,45 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
// See which target we're cleaning up and unregister the other one.
|
||||
if (holdings == 1) {
|
||||
let success = fg.unregister(key2);
|
||||
assertTrue(success);
|
||||
} else {
|
||||
assertSame(holdings, 2);
|
||||
let success = fg.unregister(key1);
|
||||
assertTrue(success);
|
||||
}
|
||||
++cleanup_call_count;
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup_holdings_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
// See which target we're cleaning up and unregister the other one.
|
||||
if (holdings == 1) {
|
||||
let success = fg.unregister(key2);
|
||||
assertTrue(success);
|
||||
} else {
|
||||
assertSame(holdings, 2);
|
||||
let success = fg.unregister(key1);
|
||||
assertTrue(success);
|
||||
}
|
||||
++cleanup_holdings_count;
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key1 = {"k": "first key"};
|
||||
let key2 = {"k": "second key"};
|
||||
// Create two objects and register them in the FinalizationRegistry. The objects
|
||||
// need to be inside a closure so that we can reliably kill them!
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key1 = {"k": "first key"};
|
||||
let key2 = {"k": "second key"};
|
||||
// Create two objects and register them in the FinalizationRegistry. The objects
|
||||
// need to be inside a closure so that we can reliably kill them!
|
||||
|
||||
(function() {
|
||||
let object1 = {};
|
||||
fg.register(object1, 1, key1);
|
||||
let object2 = {};
|
||||
fg.register(object2, 2, key2);
|
||||
|
||||
// object1 and object2 go out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim target objects and schedule cleanup.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called and cleaned up one holdings (but not the other one).
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
(function() {
|
||||
let object1 = {};
|
||||
fg.register(object1, 1, key1);
|
||||
let object2 = {};
|
||||
fg.register(object2, 2, key2);
|
||||
|
||||
// object1 and object2 go out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim target objects and schedule cleanup.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called and cleaned up one holdings (but not the other one).
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
assertEquals(1, cleanup_holdings_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,48 +4,44 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup_holdings_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
assertEquals(holdings, "holdings");
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
assertEquals(holdings, "holdings");
|
||||
// There's one more object with the same key that we haven't
|
||||
// cleaned up yet so we should be able to unregister the
|
||||
// callback for that one.
|
||||
let success = fg.unregister(key);
|
||||
|
||||
// There's one more object with the same key that we haven't
|
||||
// cleaned up yet so we should be able to unregister the
|
||||
// callback for that one.
|
||||
let success = fg.unregister(key);
|
||||
assertTrue(success);
|
||||
assertTrue(success);
|
||||
|
||||
++cleanup_call_count;
|
||||
}
|
||||
++cleanup_holdings_count;
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
|
||||
// a closure so that we can reliably kill them!
|
||||
let key = {"k": "this is the key"};
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
|
||||
// a closure so that we can reliably kill them!
|
||||
let key = {"k": "this is the key"};
|
||||
|
||||
(function() {
|
||||
let object = {};
|
||||
let object2 = {};
|
||||
fg.register(object, "holdings", key);
|
||||
fg.register(object2, "holdings", key);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will discover dirty WeakCells and schedule cleanup.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
(function() {
|
||||
let object = {};
|
||||
let object2 = {};
|
||||
fg.register(object, "holdings", key);
|
||||
fg.register(object2, "holdings", key);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will discover dirty WeakCells and schedule cleanup.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function was called.
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
assertEquals(1, cleanup_holdings_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,51 +4,46 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup_holdings_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
assertEquals("holdings2", holdings);
|
||||
++cleanup_holdings_count;
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
assertEquals("holdings2", holdings);
|
||||
++cleanup_call_count;
|
||||
}
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key1 = {"k": "key1"};
|
||||
let key2 = {"k": "key2"};
|
||||
// Create three objects and register them in the FinalizationRegistry. The objects
|
||||
// need to be inside a closure so that we can reliably kill them!
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let key1 = {"k": "key1"};
|
||||
let key2 = {"k": "key2"};
|
||||
// Create three objects and register them in the FinalizationRegistry. The objects
|
||||
// need to be inside a closure so that we can reliably kill them!
|
||||
(function() {
|
||||
let object1a = {};
|
||||
fg.register(object1a, "holdings1a", key1);
|
||||
|
||||
(function() {
|
||||
let object1a = {};
|
||||
fg.register(object1a, "holdings1a", key1);
|
||||
let object1b = {};
|
||||
fg.register(object1b, "holdings1b", key1);
|
||||
|
||||
let object1b = {};
|
||||
fg.register(object1b, "holdings1b", key1);
|
||||
let object2 = {};
|
||||
fg.register(object2, "holdings2", key2);
|
||||
|
||||
let object2 = {};
|
||||
fg.register(object2, "holdings2", key2);
|
||||
|
||||
// Unregister before the GC has a chance to discover the objects.
|
||||
let success = fg.unregister(key1);
|
||||
assertTrue(success);
|
||||
|
||||
// objects go out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target objects.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function will be called only for the reference which
|
||||
// was not unregistered.
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
// Unregister before the GC has a chance to discover the objects.
|
||||
let success = fg.unregister(key1);
|
||||
assertTrue(success);
|
||||
|
||||
// objects go out of scope.
|
||||
})();
|
||||
|
||||
// This GC will reclaim the target objects.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Assert that the cleanup function will be called only for the reference which
|
||||
// was not unregistered.
|
||||
let timeout_func = function() {
|
||||
assertEquals(1, cleanup_call_count);
|
||||
assertEquals(1, cleanup_holdings_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,42 +4,34 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
++cleanup_call_count;
|
||||
}
|
||||
|
||||
let cleanup_call_count = 0;
|
||||
let cleanup = function(holdings) {
|
||||
++cleanup_call_count;
|
||||
}
|
||||
let key = {"k": "this is my key"};
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
|
||||
// a closure so that we can reliably kill them!
|
||||
|
||||
let key = {"k": "this is my key"};
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
|
||||
// a closure so that we can reliably kill them!
|
||||
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object, {}, key);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will discover dirty WeakCells and schedule cleanup.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Unregister the object from the FinalizationRegistry before cleanup has ran.
|
||||
let success = fg.unregister(key);
|
||||
assertTrue(success);
|
||||
|
||||
// Assert that the cleanup function won't be called.
|
||||
let timeout_func = function() {
|
||||
assertEquals(0, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
(function() {
|
||||
let object = {};
|
||||
fg.register(object, {}, key);
|
||||
|
||||
// object goes out of scope.
|
||||
})();
|
||||
|
||||
// This GC will discover dirty WeakCells and schedule cleanup.
|
||||
gc();
|
||||
assertEquals(0, cleanup_call_count);
|
||||
|
||||
// Unregister the object from the FinalizationRegistry before cleanup has ran.
|
||||
let success = fg.unregister(key);
|
||||
assertTrue(success);
|
||||
|
||||
// Assert that the cleanup function won't be called.
|
||||
let timeout_func = function() {
|
||||
assertEquals(0, cleanup_call_count);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,44 +4,36 @@
|
||||
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
(async function () {
|
||||
|
||||
let cleanup_called = false;
|
||||
let cleanup = function(holdings_arg) {
|
||||
assertFalse(cleanup_called);
|
||||
assertEquals(holdings_arg, holdings);
|
||||
cleanup_called = true;
|
||||
}
|
||||
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let o = {};
|
||||
let holdings = {'h': 55};
|
||||
|
||||
// Ignition holds references to objects in temporary registers. These will be
|
||||
// released when the function exits. So only access o inside a function to
|
||||
// prevent any references to objects in temporary registers when a gc is
|
||||
// triggered.
|
||||
(() => { fg.register(o, holdings); })()
|
||||
|
||||
// Here and below, we need to invoke GC asynchronously and wait for it to
|
||||
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
|
||||
// may not be reclaimed because of conservative stack scanning and the test
|
||||
// may not work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
let cleanup_called = false;
|
||||
let cleanup = function(holdings_arg) {
|
||||
assertFalse(cleanup_called);
|
||||
assertEquals(holdings_arg, holdings);
|
||||
cleanup_called = true;
|
||||
}
|
||||
|
||||
// Drop the last reference to o.
|
||||
(() => { o = null; })()
|
||||
let fg = new FinalizationRegistry(cleanup);
|
||||
let o = {};
|
||||
let holdings = {'h': 55};
|
||||
|
||||
// GC will clear the WeakCell; the cleanup function will be called the next time
|
||||
// we enter the event loop.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertFalse(cleanup_called);
|
||||
// Ignition holds references to objects in temporary registers. These will be
|
||||
// released when the function exits. So only access o inside a function to
|
||||
// prevent any references to objects in temporary registers when a gc is
|
||||
// triggered.
|
||||
(() => { fg.register(o, holdings); })()
|
||||
|
||||
let timeout_func = function() {
|
||||
assertTrue(cleanup_called);
|
||||
}
|
||||
gc();
|
||||
assertFalse(cleanup_called);
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
// Drop the last reference to o.
|
||||
(() => { o = null; })()
|
||||
|
||||
})();
|
||||
// GC will clear the WeakCell; the cleanup function will be called the next time
|
||||
// we enter the event loop.
|
||||
gc();
|
||||
assertFalse(cleanup_called);
|
||||
|
||||
let timeout_func = function() {
|
||||
assertTrue(cleanup_called);
|
||||
}
|
||||
|
||||
setTimeout(timeout_func, 0);
|
||||
|
@ -4,30 +4,19 @@
|
||||
|
||||
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking
|
||||
|
||||
const cleanup = function (holdings) { globalThis.FRRan = true; };
|
||||
const FR = new FinalizationRegistry(cleanup);
|
||||
|
||||
(function () {
|
||||
var FR = new FinalizationRegistry (function (holdings) { globalThis.FRRan = true; });
|
||||
{
|
||||
let obj = {};
|
||||
// obj is its own unregister token and becomes unreachable after this
|
||||
// block. If the unregister token is held strongly this test will not
|
||||
// terminate.
|
||||
FR.register(obj, 42, obj);
|
||||
})();
|
||||
|
||||
}
|
||||
function tryAgain() {
|
||||
(async function () {
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
if (globalThis.FRRan || FR.cleanupSome()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(tryAgain, 0);
|
||||
})();
|
||||
gc();
|
||||
if (globalThis.FRRan || FR.cleanupSome()) {
|
||||
return;
|
||||
}
|
||||
setTimeout(tryAgain, 0);
|
||||
}
|
||||
tryAgain();
|
||||
|
@ -5,29 +5,22 @@
|
||||
// Flags: --expose-gc --noincremental-marking
|
||||
|
||||
let wr;
|
||||
(function () {
|
||||
(function() {
|
||||
let o = {};
|
||||
wr = new WeakRef(o);
|
||||
// Don't deref here, we want to test that the creation is enough to keep the
|
||||
// WeakRef alive until the end of the turn.
|
||||
})();
|
||||
|
||||
// Here we invoke GC synchronously and, with conservative stack scanning,
|
||||
// there is a chance that the object is not reclaimed now. In any case,
|
||||
// the WeakRef should not be cleared.
|
||||
gc();
|
||||
|
||||
// Since the WeakRef was created during this turn, it is not cleared by GC.
|
||||
assertNotEquals(undefined, wr.deref());
|
||||
(function() {
|
||||
assertNotEquals(undefined, wr.deref());
|
||||
})();
|
||||
|
||||
// Next task.
|
||||
setTimeout(() => {
|
||||
(async function () {
|
||||
// Trigger GC again to make sure the two WeakRefs are cleared.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
assertEquals(undefined, wr.deref());
|
||||
})();
|
||||
gc();
|
||||
assertEquals(undefined, wr.deref());
|
||||
}, 0);
|
||||
|
@ -6,43 +6,40 @@
|
||||
|
||||
let wr;
|
||||
let wr_control; // control WeakRef for testing what happens without deref
|
||||
(function () {
|
||||
(function() {
|
||||
let o1 = {};
|
||||
wr = new WeakRef(o1);
|
||||
let o2 = {};
|
||||
wr_control = new WeakRef(o2);
|
||||
})();
|
||||
|
||||
let strong = { a: wr.deref(), b: wr_control.deref() };
|
||||
let strong = {a: wr.deref(), b: wr_control.deref()};
|
||||
|
||||
// Here and below, we invoke GC synchronously and, with conservative stack
|
||||
// scanning, there is a chance that the object is not reclaimed now. In any
|
||||
// case, the WeakRefs should not be cleared.
|
||||
gc();
|
||||
|
||||
// Next task.
|
||||
setTimeout(function() {
|
||||
// Call deref inside a closure, trying to avoid accidentally storing a strong
|
||||
// reference into the object in the stack frame.
|
||||
(function () { wr.deref(); })();
|
||||
(function() {
|
||||
wr.deref();
|
||||
})();
|
||||
|
||||
strong = null;
|
||||
|
||||
// This GC should clear wr_control (modulo CSS), since nothing was keeping it
|
||||
// alive, but it should not clear wr.
|
||||
// This GC will clear wr_control.
|
||||
gc();
|
||||
(function () { assertNotEquals(undefined, wr.deref()); })();
|
||||
|
||||
// Next task.
|
||||
(async function () {
|
||||
// Trigger GC again to make sure the two WeakRefs are cleared.
|
||||
// We need to invoke GC asynchronously and wait for it to finish, so that
|
||||
// it doesn't need to scan the stack. Otherwise, the objects may not be
|
||||
// reclaimed because of conservative stack scanning and the test may not
|
||||
// work as intended.
|
||||
await gc({ type: 'major', execution: 'async' });
|
||||
|
||||
assertEquals(undefined, wr.deref());
|
||||
(function() {
|
||||
assertNotEquals(undefined, wr.deref());
|
||||
// Now the control WeakRef got cleared, since nothing was keeping it alive.
|
||||
assertEquals(undefined, wr_control.deref());
|
||||
})();
|
||||
|
||||
// Next task.
|
||||
setTimeout(function() {
|
||||
gc();
|
||||
|
||||
assertEquals(undefined, wr.deref());
|
||||
}, 0);
|
||||
}, 0);
|
||||
|
Loading…
Reference in New Issue
Block a user