diff --git a/packages/SwingSet/package.json b/packages/SwingSet/package.json index b81876b72c1..1ca8471c9fe 100644 --- a/packages/SwingSet/package.json +++ b/packages/SwingSet/package.json @@ -87,9 +87,6 @@ "files": [ "test/**/test-*.js" ], - "nodeArguments": [ - "--expose-gc" - ], "require": [ "esm" ], diff --git a/packages/SwingSet/src/controller.js b/packages/SwingSet/src/controller.js index 44053879390..0cb033f63d6 100644 --- a/packages/SwingSet/src/controller.js +++ b/packages/SwingSet/src/controller.js @@ -17,10 +17,11 @@ import { importBundle } from '@agoric/import-bundle'; import { makeMeteringTransformer } from '@agoric/transform-metering'; import { xsnap, makeSnapstore } from '@agoric/xsnap'; +import engineGC from './engine-gc'; import { WeakRef, FinalizationRegistry } from './weakref'; import { startSubprocessWorker } from './spawnSubprocessWorker'; import { waitUntilQuiescent } from './waitUntilQuiescent'; -import { gcAndFinalize } from './gc-and-finalize'; +import { makeGcAndFinalize } from './gc-and-finalize'; import { insistStorageAPI } from './storageAPI'; import { insistCapData } from './capdata'; import { parseVatSlot } from './parseVatSlots'; @@ -272,7 +273,7 @@ export async function makeSwingsetController( const supercode = require.resolve( './kernel/vatManager/supervisor-subprocess-node.js', ); - const args = ['--expose-gc', '-r', 'esm', supercode]; + const args = ['-r', 'esm', supercode]; return startSubprocessWorker(process.execPath, args); } @@ -299,7 +300,7 @@ export async function makeSwingsetController( writeSlogObject, WeakRef, FinalizationRegistry, - gcAndFinalize, + gcAndFinalize: makeGcAndFinalize(engineGC), }; const kernelOptions = { verbose }; diff --git a/packages/SwingSet/src/engine-gc.js b/packages/SwingSet/src/engine-gc.js new file mode 100644 index 00000000000..492586770bd --- /dev/null +++ b/packages/SwingSet/src/engine-gc.js @@ -0,0 +1,16 @@ +/* eslint-disable global-require */ +/* global globalThis require */ +let bestGC = globalThis.gc; +if (typeof bestGC !== 'function') { + // Node.js v8 wizardry. + const v8 = require('v8'); + const vm = require('vm'); + v8.setFlagsFromString('--expose_gc'); + bestGC = vm.runInNewContext('gc'); + // Hide the gc global from new contexts/workers. + v8.setFlagsFromString('--no-expose_gc'); +} + +// Export a const. +const engineGC = bestGC; +export default engineGC; diff --git a/packages/SwingSet/src/gc-and-finalize.js b/packages/SwingSet/src/gc-and-finalize.js index c907dc6b575..059eb67d493 100644 --- a/packages/SwingSet/src/gc-and-finalize.js +++ b/packages/SwingSet/src/gc-and-finalize.js @@ -1,4 +1,4 @@ -/* global gc setImmediate */ +/* global setImmediate */ /* A note on our GC terminology: * @@ -33,9 +33,10 @@ * The transition from UNREACHABLE to COLLECTED can happen spontaneously, as * the JS engine decides it wants to perform GC. It will also happen * deliberately if we provoke a GC call with a magic function like `gc()` - * (when Node.js is run with `--expose-gc`, or when XS is configured to - * provide it as a C-level callback). We can force GC, but we cannot prevent - * it from happening at other times. + * (when Node.js imports `engine-gc`, which is morally-equivalent to + * running with `--expose-gc`, or when XS is configured to provide it as a + * C-level callback). We can force GC, but we cannot prevent it from happening + * at other times. * * FinalizationRegistry callbacks are defined to run on their own turn, so * the transition from COLLECTED to FINALIZED occurs at a turn boundary. @@ -63,23 +64,23 @@ * dummy pre-resolved Promise. */ -let alreadyWarned = false; -export async function gcAndFinalize() { - if (typeof gc !== 'function') { - if (!alreadyWarned) { - alreadyWarned = true; - console.warn( - Error(`no gc() function; disabling deterministic finalizers`), - ); - } - return; +export function makeGcAndFinalize(gcPower) { + if (typeof gcPower !== 'function') { + console.warn( + Error(`no gcPower() function; skipping finalizer provocation`), + ); } + return async function gcAndFinalize() { + if (typeof gcPower !== 'function') { + return; + } - // on Node.js, GC seems to work better if the promise queue is empty first - await new Promise(setImmediate); - // on xsnap, we must do it twice for some reason - await new Promise(setImmediate); - gc(); - // this gives finalizers a chance to run - await new Promise(setImmediate); + // on Node.js, GC seems to work better if the promise queue is empty first + await new Promise(setImmediate); + // on xsnap, we must do it twice for some reason + await new Promise(setImmediate); + gcPower(); + // this gives finalizers a chance to run + await new Promise(setImmediate); + }; } diff --git a/packages/SwingSet/src/kernel/vatManager/supervisor-nodeworker.js b/packages/SwingSet/src/kernel/vatManager/supervisor-nodeworker.js index 8a96a97b0f6..0efc4e3bae9 100644 --- a/packages/SwingSet/src/kernel/vatManager/supervisor-nodeworker.js +++ b/packages/SwingSet/src/kernel/vatManager/supervisor-nodeworker.js @@ -9,8 +9,9 @@ import '../../types'; import { assert, details as X } from '@agoric/assert'; import { importBundle } from '@agoric/import-bundle'; import { makeMarshal } from '@agoric/marshal'; +import engineGC from '../../engine-gc'; import { WeakRef, FinalizationRegistry } from '../../weakref'; -import { gcAndFinalize } from '../../gc-and-finalize'; +import { makeGcAndFinalize } from '../../gc-and-finalize'; import { waitUntilQuiescent } from '../../waitUntilQuiescent'; import { makeLiveSlots } from '../liveSlots'; import { @@ -79,7 +80,7 @@ parentPort.on('message', ([type, ...margs]) => { WeakRef, FinalizationRegistry, waitUntilQuiescent, - gcAndFinalize, + gcAndFinalize: makeGcAndFinalize(engineGC), }); const ls = makeLiveSlots( syscall, diff --git a/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-node.js b/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-node.js index b6b1e50a183..cc05db96e18 100644 --- a/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-node.js +++ b/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-node.js @@ -7,8 +7,9 @@ import fs from 'fs'; import { assert, details as X } from '@agoric/assert'; import { importBundle } from '@agoric/import-bundle'; import { makeMarshal } from '@agoric/marshal'; +import engineGC from '../../engine-gc'; import { WeakRef, FinalizationRegistry } from '../../weakref'; -import { gcAndFinalize } from '../../gc-and-finalize'; +import { makeGcAndFinalize } from '../../gc-and-finalize'; import { arrayEncoderStream, arrayDecoderStream } from '../../worker-protocol'; import { netstringEncoderStream, @@ -92,7 +93,7 @@ fromParent.on('data', ([type, ...margs]) => { WeakRef, FinalizationRegistry, waitUntilQuiescent, - gcAndFinalize, + gcAndFinalize: makeGcAndFinalize(engineGC), }); const ls = makeLiveSlots( syscall, diff --git a/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-xsnap.js b/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-xsnap.js index 91a8ad84dd8..23db6a85305 100644 --- a/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-xsnap.js +++ b/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-xsnap.js @@ -6,7 +6,7 @@ import { makeMarshal } from '@agoric/marshal'; import '../../types'; // grumble... waitUntilQuiescent is exported and closes over ambient authority import { waitUntilQuiescent } from '../../waitUntilQuiescent'; -import { gcAndFinalize } from '../../gc-and-finalize'; +import { makeGcAndFinalize } from '../../gc-and-finalize'; import { insistVatDeliveryObject, insistVatSyscallResult } from '../../message'; import { makeLiveSlots } from '../liveSlots'; @@ -185,7 +185,7 @@ function makeWorker(port) { WeakRef, FinalizationRegistry, waitUntilQuiescent, - gcAndFinalize, + gcAndFinalize: makeGcAndFinalize(globalThis.gc), }); const ls = makeLiveSlots( diff --git a/packages/SwingSet/test/liveslots-helpers.js b/packages/SwingSet/test/liveslots-helpers.js index 0fb5ee18a17..ed610ae325e 100644 --- a/packages/SwingSet/test/liveslots-helpers.js +++ b/packages/SwingSet/test/liveslots-helpers.js @@ -1,6 +1,8 @@ +import engineGC from '../src/engine-gc'; + import { WeakRef, FinalizationRegistry } from '../src/weakref'; import { waitUntilQuiescent } from '../src/waitUntilQuiescent'; -import { gcAndFinalize } from '../src/gc-and-finalize'; +import { makeGcAndFinalize } from '../src/gc-and-finalize'; import { makeLiveSlots } from '../src/kernel/liveSlots'; export function buildSyscall() { @@ -43,7 +45,7 @@ export function makeDispatch( WeakRef, FinalizationRegistry, waitUntilQuiescent, - gcAndFinalize, + gcAndFinalize: makeGcAndFinalize(engineGC), }); const { setBuildRootObject, dispatch } = makeLiveSlots( syscall, diff --git a/packages/SwingSet/test/test-gc-and-finalize.js b/packages/SwingSet/test/test-gc-and-finalize.js index ac6ccebe089..04a07696d56 100644 --- a/packages/SwingSet/test/test-gc-and-finalize.js +++ b/packages/SwingSet/test/test-gc-and-finalize.js @@ -1,21 +1,14 @@ -/* global gc FinalizationRegistry WeakRef */ +/* global FinalizationRegistry WeakRef */ // eslint-disable-next-line import/order import { test } from '../tools/prepare-test-env-ava'; import * as childProcess from 'child_process'; import * as os from 'os'; import { xsnap } from '@agoric/xsnap'; -import { gcAndFinalize } from '../src/gc-and-finalize'; +import engineGC from '../src/engine-gc'; +import { makeGcAndFinalize } from '../src/gc-and-finalize'; -test(`have gc() on Node.js`, async t => { - t.is(typeof gc, 'function', 'environment is missing top-level gc()'); - // Under Node.js, you must use `node --expose-gc PROGRAM`. Under AVA+Node, - // add `nodeArguments: [ "--expose-gc" ]` to the package.json 'ava:' - // stanza. Under XS, make sure your application (e.g. xsnap) provides a - // `gc` C callback on the global object. -}); - -function setup() { +function makeVictim() { const victim = { doomed: 'oh no' }; const finalized = ['finalizer not called']; const fr = new FinalizationRegistry(_tag => { @@ -26,13 +19,15 @@ function setup() { return { finalized, fr, wr }; } -async function provokeGC() { - // the transition from REACHABLE to UNREACHABLE happens as soon as setup() +async function provokeGC(myGC) { + const gcAndFinalize = makeGcAndFinalize(myGC); + + // the transition from REACHABLE to UNREACHABLE happens as soon as makeVictim() // finishes, and the local 'victim' binding goes out of scope // we must retain the FinalizationRegistry to let the callback fire // eslint-disable-next-line no-unused-vars - const { finalized, fr, wr } = setup(); + const { finalized, fr, wr } = makeVictim(); // the transition from UNREACHABLE to COLLECTED can happen at any moment, // but is far more likely to happen if we force it @@ -53,7 +48,7 @@ if ( } ltest(`can provoke gc on Node.js`, async t => { - const { wrState, finalizerState } = await provokeGC(); + const { wrState, finalizerState } = await provokeGC(engineGC); t.is(wrState, 'weakref is dead'); t.is(finalizerState, 'finalizer was called'); }); @@ -81,10 +76,10 @@ test(`can provoke gc on xsnap`, async t => { const opts = options(); const vat = xsnap(opts); const code = ` -${gcAndFinalize} -${setup} +${makeGcAndFinalize} +${makeVictim} ${provokeGC} -provokeGC().then(data => issueCommand(ArrayBuffer.fromString(JSON.stringify(data)))); +provokeGC(globalThis.gc).then(data => issueCommand(ArrayBuffer.fromString(JSON.stringify(data)))); `; await vat.evaluate(code); await vat.close(); diff --git a/packages/SwingSet/test/test-liveslots.js b/packages/SwingSet/test/test-liveslots.js index ee3b4072605..d2a835ef897 100644 --- a/packages/SwingSet/test/test-liveslots.js +++ b/packages/SwingSet/test/test-liveslots.js @@ -6,8 +6,9 @@ import { E } from '@agoric/eventual-send'; import { Far } from '@agoric/marshal'; import { makePromiseKit } from '@agoric/promise-kit'; import { assert, details as X } from '@agoric/assert'; +import engineGC from '../src/engine-gc'; import { waitUntilQuiescent } from '../src/waitUntilQuiescent'; -import { gcAndFinalize } from '../src/gc-and-finalize'; +import { makeGcAndFinalize } from '../src/gc-and-finalize'; import { makeLiveSlots } from '../src/kernel/liveSlots'; import { buildSyscall, makeDispatch } from './liveslots-helpers'; import { @@ -323,6 +324,7 @@ test('liveslots retires outbound promise IDs after reject', async t => { }); test('liveslots retains pending exported promise', async t => { + const gcAndFinalize = makeGcAndFinalize(engineGC); const { log, syscall } = buildSyscall(); let watch; const success = []; @@ -661,6 +663,7 @@ test('disavow', async t => { }); test('liveslots retains device nodes', async t => { + const gcAndFinalize = makeGcAndFinalize(engineGC); const { syscall } = buildSyscall(); let watch; const recognize = new WeakSet(); // real WeakSet diff --git a/packages/SwingSet/test/virtualObjects/test-retain-remotable.js b/packages/SwingSet/test/virtualObjects/test-retain-remotable.js index 3f16ece86cb..7df1b402604 100644 --- a/packages/SwingSet/test/virtualObjects/test-retain-remotable.js +++ b/packages/SwingSet/test/virtualObjects/test-retain-remotable.js @@ -4,7 +4,8 @@ import { test } from '../../tools/prepare-test-env-ava'; // eslint-disable-next-line import/order import { Far } from '@agoric/marshal'; -import { gcAndFinalize } from '../../src/gc-and-finalize'; +import engineGC from '../../src/engine-gc'; +import { makeGcAndFinalize } from '../../src/gc-and-finalize'; import { makeFakeVirtualObjectManager } from '../../tools/fakeVirtualObjectManager'; // empty object, used as makeWeakStore() key @@ -76,6 +77,7 @@ function stashRemotableFour(holderMaker) { } test('remotables retained by virtualized data', async t => { + const gcAndFinalize = makeGcAndFinalize(engineGC); const vomOptions = { cacheSize: 3, weak: true }; const vom = makeFakeVirtualObjectManager(vomOptions); const { makeWeakStore, makeKind } = vom; diff --git a/packages/SwingSet/test/virtualObjects/test-weakcollections.js b/packages/SwingSet/test/virtualObjects/test-weakcollections.js index ee2226602c7..0464c6ab975 100644 --- a/packages/SwingSet/test/virtualObjects/test-weakcollections.js +++ b/packages/SwingSet/test/virtualObjects/test-weakcollections.js @@ -1,9 +1,10 @@ -/* global __dirname globalThis */ +/* global __dirname */ import { test } from '../../tools/prepare-test-env-ava'; // eslint-disable-next-line import/order import path from 'path'; +import engineGC from '../../src/engine-gc'; import { provideHostStorage } from '../../src/hostStorage'; import { initializeSwingset, makeSwingsetController } from '../../src/index'; import { makeFakeVirtualObjectManager } from '../../tools/fakeVirtualObjectManager'; @@ -17,17 +18,7 @@ function capargs(args, slots = []) { return capdata(JSON.stringify(args), slots); } -let gc; -function insistGC(t) { - if (globalThis.gc) { - gc = globalThis.gc; - } else { - t.fail(`GC needs to be enabled for this test to work`); - } -} - test('weakMap in vat', async t => { - insistGC(t); const config = { bootstrap: 'bootstrap', defaultManagerType: 'local', @@ -70,7 +61,7 @@ test('weakMap in vat', async t => { 'probe of [object Promise] returns fep', ]); await doSimple('betweenProbes'); - gc(); + engineGC(); const postGCResult = await doSimple('runProbes'); t.deepEqual(postGCResult, capargs('probes done')); t.deepEqual(nextLog(), [ @@ -86,7 +77,6 @@ test('weakMap in vat', async t => { }); test('weakMap vref handling', async t => { - insistGC(t); const log = []; const { VirtualObjectAwareWeakMap, diff --git a/packages/agoric-cli/lib/start.js b/packages/agoric-cli/lib/start.js index 38ba1cb06a8..ff1f5e88528 100644 --- a/packages/agoric-cli/lib/start.js +++ b/packages/agoric-cli/lib/start.js @@ -41,12 +41,7 @@ export default async function startMain(progname, rawArgs, powers, opts) { const SDK_IMAGE = `agoric/agoric-sdk:${opts.dockerTag}`; const SOLO_IMAGE = `agoric/cosmic-swingset-solo:${opts.dockerTag}`; - const pspawnEnv = { - ...process.env, - // TODO: We'd love to --expose-gc in this environment variable, - // but Node.js rejects it. - // NODE_OPTIONS: `${process.env.NODE_OPTIONS || ''} --expose-gc`, - }; + const pspawnEnv = { ...process.env }; const pspawn = makePspawn({ env: pspawnEnv, spawn, log, chalk }); let keysSpawn; diff --git a/packages/dapp-svelte-wallet/api/src/wallet.js b/packages/dapp-svelte-wallet/api/src/wallet.js index fefa8db439b..221b895c423 100644 --- a/packages/dapp-svelte-wallet/api/src/wallet.js +++ b/packages/dapp-svelte-wallet/api/src/wallet.js @@ -269,7 +269,6 @@ export function buildRootObject(_vatPowers) { harden(preapprovedBridge); async function getWallet(bank) { - console.error('/// importing bank assets', bank); if (bank) { walletRoot.importBankAssets(bank); } diff --git a/packages/solo/test/test-home.js b/packages/solo/test/test-home.js index 5d52f3e5c0a..76355d9507e 100644 --- a/packages/solo/test/test-home.js +++ b/packages/solo/test/test-home.js @@ -169,7 +169,7 @@ test.serial('home.localTimerService makeRepeater', async t => { const notifier = E(localTimerService).makeNotifier(1, 1); await E(notifier).getUpdateSince(); - t.is(1, handler.getCalls()); + t.truthy(handler.getCalls() >= 1); t.truthy(handler.getArgs()[0] > timestamp); }); diff --git a/packages/swingset-runner/autobench b/packages/swingset-runner/autobench index ee0401a719a..c0c1faf6e23 100755 --- a/packages/swingset-runner/autobench +++ b/packages/swingset-runner/autobench @@ -35,7 +35,7 @@ for (const suite of SUITES) { } } spawnNodeSync([ - '--expose-gc', '-r', 'esm', 'bin/runner', '--init', + '-r', 'esm', 'bin/runner', '--init', '--benchmark', brounds, '--statsfile', benchstats, ...configFlags, 'run', `demo/${suite}`, '--quiet', '--prime', ]); diff --git a/packages/swingset-runner/bin/runner b/packages/swingset-runner/bin/runner index 0c8872891cf..992f3a628e1 100755 --- a/packages/swingset-runner/bin/runner +++ b/packages/swingset-runner/bin/runner @@ -1,6 +1,6 @@ -#!/usr/bin/env -S node --expose-gc -r esm +#!/usr/bin/env -S node -r esm -// #!/usr/bin/env -S node --inspect-brk --expose-gc -r esm +// #!/usr/bin/env -S node --inspect-brk -r esm /** * Simple boilerplate program providing linkage to launch an application written using modules within the diff --git a/packages/swingset-runner/bin/runner-debug b/packages/swingset-runner/bin/runner-debug index af0f7ce9f4c..d222198dcae 100755 --- a/packages/swingset-runner/bin/runner-debug +++ b/packages/swingset-runner/bin/runner-debug @@ -1,4 +1,4 @@ -#!/usr/bin/env -S node --inspect-brk --expose-gc -r esm +#!/usr/bin/env -S node --inspect-brk -r esm /** * Simple boilerplate program providing linkage to launch an application written using modules within the diff --git a/packages/swingset-runner/package.json b/packages/swingset-runner/package.json index 3874fb0b4fa..87cd2db2a8b 100644 --- a/packages/swingset-runner/package.json +++ b/packages/swingset-runner/package.json @@ -34,6 +34,7 @@ "@agoric/swingset-vat": "^0.17.2", "@agoric/tame-metering": "^1.3.9", "@agoric/zoe": "^0.15.7", + "expose-gc": "^1.0.0", "n-readlines": "^1.0.1", "yargs": "^16.1.0" }, diff --git a/packages/swingset-runner/src/main.js b/packages/swingset-runner/src/main.js index 3a74d6b8e3b..c8d30829182 100644 --- a/packages/swingset-runner/src/main.js +++ b/packages/swingset-runner/src/main.js @@ -13,6 +13,7 @@ import { makeSwingsetController, } from '@agoric/swingset-vat'; import { buildLoopbox } from '@agoric/swingset-vat/src/devices/loopbox'; +import engineGC from '@agoric/swingset-vat/src/engine-gc'; import { initSwingStore as initSimpleSwingStore } from '@agoric/swing-store-simple'; import { @@ -313,11 +314,6 @@ export async function main() { } if (forceGC) { - if (!globalThis.gc) { - fail( - 'To use --forcegc you must start node with the --expose-gc command line option', - ); - } if (!logMem) { log('Warning: --forcegc without --logmem may be a mistake'); } @@ -603,7 +599,7 @@ export async function main() { } const blockEndTime = readClock(); if (forceGC) { - globalThis.gc(); + engineGC(); } if (statLogger) { blockNumber += 1; diff --git a/packages/xsnap/test/gc.js b/packages/xsnap/test/gc.js index 17de5ee77fa..72b22713786 100644 --- a/packages/xsnap/test/gc.js +++ b/packages/xsnap/test/gc.js @@ -1,4 +1,4 @@ -/* global gc setImmediate */ +/* global setImmediate */ // This is a copy of a utility from packages/SwingSet/src/gc.js, which // swingset uses to force GC in the middle of a crank. We copy it into xsnap @@ -38,9 +38,10 @@ * The transition from UNREACHABLE to COLLECTED can happen spontaneously, as * the JS engine decides it wants to perform GC. It will also happen * deliberately if we provoke a GC call with a magic function like `gc()` - * (when Node.js is run with `--expose-gc`, or when XS is configured to - * provide it as a C-level callback). We can force GC, but we cannot prevent - * it from happening at other times. + * (when Node.js imports `engine-gc`, which is morally-equivalent to + * running with `--expose-gc`, or when XS is configured to provide it as a + * C-level callback). We can force GC, but we cannot prevent it from happening + * at other times. * * FinalizationRegistry callbacks are defined to run on their own turn, so * the transition from COLLECTED to FINALIZED occurs at a turn boundary. @@ -68,22 +69,23 @@ * dummy pre-resolved Promise. */ -let alreadyWarned = false; -export async function gcAndFinalize() { - if (typeof gc !== 'function') { - if (!alreadyWarned) { - alreadyWarned = true; - console.warn( - Error(`no gc() function; disabling deterministic finalizers`), - ); - } - return; +export function makeGcAndFinalize(gcPower) { + if (typeof gcPower !== 'function') { + console.warn( + Error(`no gcPower() function; skipping finalizer provocation`), + ); } - // on Node.js, GC seems to work better if the promise queue is empty first - await new Promise(setImmediate); - // on xsnap, we must do it twice for some reason - await new Promise(setImmediate); - gc(); - // this gives finalizers a chance to run - await new Promise(setImmediate); + return async function gcAndFinalize() { + if (typeof gcPower !== 'function') { + return; + } + + // on Node.js, GC seems to work better if the promise queue is empty first + await new Promise(setImmediate); + // on xsnap, we must do it twice for some reason + await new Promise(setImmediate); + gcPower(); + // this gives finalizers a chance to run + await new Promise(setImmediate); + }; } diff --git a/packages/xsnap/test/test-gc.js b/packages/xsnap/test/test-gc.js index efbb0043724..7ce6bdba220 100644 --- a/packages/xsnap/test/test-gc.js +++ b/packages/xsnap/test/test-gc.js @@ -1,12 +1,12 @@ -/* global gc FinalizationRegistry WeakRef */ +/* global FinalizationRegistry WeakRef */ import test from 'ava'; import * as childProcess from 'child_process'; import * as os from 'os'; import { xsnap } from '../src/xsnap'; -import { gcAndFinalize } from './gc'; +import { makeGcAndFinalize } from './gc'; -function setup() { +function makeVictim() { const victim = { doomed: 'oh no' }; const finalized = ['finalizer not called']; const fr = new FinalizationRegistry(_tag => { @@ -17,13 +17,15 @@ function setup() { return { finalized, fr, wr }; } -async function provokeGC() { - // the transition from REACHABLE to UNREACHABLE happens as soon as setup() +async function provokeGC(myGC) { + // eslint-disable-next-line no-undef + const gcAndFinalize = makeGcAndFinalize(myGC); + // the transition from REACHABLE to UNREACHABLE happens as soon as makeVictim() // finishes, and the local 'victim' binding goes out of scope // we must retain the FinalizationRegistry to let the callback fire // eslint-disable-next-line no-unused-vars - const { finalized, fr, wr } = setup(); + const { finalized, fr, wr } = makeVictim(); // the transition from UNREACHABLE to COLLECTED can happen at any moment, // but is far more likely to happen if we force it @@ -57,10 +59,10 @@ test(`can provoke gc on xsnap`, async t => { const opts = options(); const vat = xsnap(opts); const code = ` -${gcAndFinalize} -${setup} +${makeGcAndFinalize} +${makeVictim} ${provokeGC} -provokeGC().then(data => issueCommand(ArrayBuffer.fromString(JSON.stringify(data)))); +provokeGC(globalThis.gc).then(data => issueCommand(ArrayBuffer.fromString(JSON.stringify(data)))); `; await vat.evaluate(code); await vat.close(); diff --git a/yarn.lock b/yarn.lock index d74d68e9897..94da6f32d34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5817,6 +5817,11 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expose-gc@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/expose-gc/-/expose-gc-1.0.0.tgz#ba0e825b390cc3e7ab38fc5b945cd2b4018584b3" + integrity sha512-ecOHrdm+zyOCGIwX18/1RHkUWgxDqGGRiGhaNC+42jReTtudbm2ID/DMa/wpaHwqy5YQHPZvsDqRM2F2iZ0uVA== + express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"