diff --git a/packages/SwingSet/docs/vat-worker.md b/packages/SwingSet/docs/vat-worker.md index 6ea5a7143c9..7d554206fa1 100644 --- a/packages/SwingSet/docs/vat-worker.md +++ b/packages/SwingSet/docs/vat-worker.md @@ -24,6 +24,7 @@ The `Delivery` object is a hardened Array of data (Objects, Arrays, and Strings, * `['message', targetSlot, msg]` * `['notify', resolutions]` +* `['dropExports', vrefs]` In the `message` form, `targetSlot` is a object/promise reference (a string like `o+13` or `p-24`), which identifies the object or promise to which the message is being sent. This target can be a promise if the message is being pipelined to the result of some earlier message. diff --git a/packages/SwingSet/src/kernel/liveSlots.js b/packages/SwingSet/src/kernel/liveSlots.js index fa2348b3682..200f56eedf8 100644 --- a/packages/SwingSet/src/kernel/liveSlots.js +++ b/packages/SwingSet/src/kernel/liveSlots.js @@ -611,6 +611,13 @@ function build( } } + function dropExports(vrefs) { + assert(Array.isArray(vrefs)); + vrefs.map(vref => insistVatType('object', vref)); + vrefs.map(vref => assert(parseVatSlot(vref).allocatedByVat)); + console.log(`-- liveslots ignoring dropExports`); + } + // TODO: when we add notifyForward, guard against cycles function exitVat(completion) { @@ -674,7 +681,7 @@ function build( slotToVal.set(rootSlot, rootObject); } - const dispatch = harden({ deliver, notify }); + const dispatch = harden({ deliver, notify, dropExports }); return harden({ vatGlobals, setBuildRootObject, dispatch, m }); } diff --git a/packages/SwingSet/src/kernel/vatManager/supervisor-nodeworker.js b/packages/SwingSet/src/kernel/vatManager/supervisor-nodeworker.js index 0d4a11df122..ea21d14eeac 100644 --- a/packages/SwingSet/src/kernel/vatManager/supervisor-nodeworker.js +++ b/packages/SwingSet/src/kernel/vatManager/supervisor-nodeworker.js @@ -64,6 +64,11 @@ function doNotify(resolutions) { return doProcess(['notify', resolutions], errmsg); } +function doDropExports(vrefs) { + const errmsg = `vat.doDropExport failed`; + return doProcess(['dropExports', vrefs], errmsg); +} + parentPort.on('message', ([type, ...margs]) => { workerLog(`received`, type); if (type === 'start') { @@ -143,6 +148,8 @@ parentPort.on('message', ([type, ...margs]) => { doMessage(...dargs).then(res => sendUplink(['deliverDone', ...res])); } else if (dtype === 'notify') { doNotify(...dargs).then(res => sendUplink(['deliverDone', ...res])); + } else if (dtype === 'dropExports') { + doDropExports(...dargs).then(res => sendUplink(['deliverDone', ...res])); } else { assert.fail(X`bad delivery type ${dtype}`); } diff --git a/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-node.js b/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-node.js index dc8a261f329..fddfe8d1902 100644 --- a/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-node.js +++ b/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-node.js @@ -64,6 +64,11 @@ function doNotify(resolutions) { return doProcess(['notify', resolutions], errmsg); } +function doDropExports(vrefs) { + const errmsg = `vat.doDropExport failed`; + return doProcess(['dropExports', vrefs], errmsg); +} + const toParent = arrayEncoderStream(); toParent .pipe(netstringEncoderStream()) @@ -163,6 +168,8 @@ fromParent.on('data', ([type, ...margs]) => { doMessage(...dargs).then(res => sendUplink(['deliverDone', ...res])); } else if (dtype === 'notify') { doNotify(...dargs).then(res => sendUplink(['deliverDone', ...res])); + } else if (dtype === 'dropExports') { + doDropExports(...dargs).then(res => sendUplink(['deliverDone', ...res])); } else { assert.fail(X`bad delivery type ${dtype}`); } diff --git a/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-xsnap.js b/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-xsnap.js index a376c174c67..ced7f79000e 100644 --- a/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-xsnap.js +++ b/packages/SwingSet/src/kernel/vatManager/supervisor-subprocess-xsnap.js @@ -151,6 +151,12 @@ function makeWorker(port) { return doProcess(['notify', resolutions], errmsg); } + /** @type { (rs: unknown) => Promise } */ + function doDropExports(vrefs) { + const errmsg = `vat.dropExports failed`; + return doProcess(['dropExports', vrefs], errmsg); + } + /** * TODO: consider other methods per SES VirtualConsole. * See https://github.com/Agoric/agoric-sdk/issues/2146 @@ -277,6 +283,8 @@ function makeWorker(port) { return doMessage(dargs[0], dargs[1]); case 'notify': return doNotify(dargs[0]); + case 'dropExports': + return doDropExports(dargs[0]); default: assert.fail(X`bad delivery type ${dtype}`); } diff --git a/packages/SwingSet/test/test-liveslots.js b/packages/SwingSet/test/test-liveslots.js index c637d9dec7f..17b0f5a3fcb 100644 --- a/packages/SwingSet/test/test-liveslots.js +++ b/packages/SwingSet/test/test-liveslots.js @@ -15,8 +15,17 @@ function capargs(args, slots = []) { return capdata(JSON.stringify(args), slots); } -function caponeslot(slot) { - return capargs([{ '@qclass': 'slot', index: 0 }], [slot]); +function capdataOneSlot(slot) { + return capargs({ '@qclass': 'slot', iface: 'Alleged: export', index: 0 }, [ + slot, + ]); +} + +function capargsOneSlot(slot) { + return capargs( + [{ '@qclass': 'slot', iface: 'Alleged: export', index: 0 }], + [slot], + ); } function oneResolution(promiseID, rejected, data) { @@ -639,7 +648,7 @@ test('disavow', async t => { const import1 = 'o-1'; // root~.one(import1) // sendOnly - dispatch.deliver(rootA, 'one', caponeslot(import1), undefined); + dispatch.deliver(rootA, 'one', capargsOneSlot(import1), undefined); await waitUntilQuiescent(); t.deepEqual(log.shift(), { type: 'dropImports', slots: [import1] }); t.deepEqual(log.shift(), 'disavowed pres1'); @@ -672,3 +681,37 @@ test('disavow', async t => { t.deepEqual(log.shift(), 'tried to send to disavowed'); t.deepEqual(log, []); }); + +test('dropExports', async t => { + const { log, syscall } = buildSyscall(); + + function build(_vatPowers) { + const ex1 = Far('export', {}); + const root = Far('root', { + one() { + return ex1; + }, + }); + return root; + } + const dispatch = makeDispatch(syscall, build, true); + t.deepEqual(log, []); + const rootA = 'o+0'; + + // rp1 = root~.one() + // ex1 = await rp1 + const rp1 = 'p-1'; + dispatch.deliver(rootA, 'one', capargs([]), rp1); + await waitUntilQuiescent(); + const l1 = log.shift(); + const ex1 = l1.resolutions[0][2].slots[0]; + t.deepEqual(l1, { + type: 'resolve', + resolutions: [[rp1, false, capdataOneSlot(ex1)]], + }); + t.deepEqual(log, []); + + // now tell the vat to drop that export + dispatch.dropExports([ex1]); + // for now, all that we care about is that liveslots doesn't crash +});