Skip to content

Commit

Permalink
feat: propery handle remotables vs presences in weak collections
Browse files Browse the repository at this point in the history
  • Loading branch information
FUDCo committed May 21, 2021
1 parent 6a8833b commit e4a32a2
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 24 deletions.
8 changes: 4 additions & 4 deletions packages/SwingSet/src/kernel/liveSlots.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,8 @@ function build(
makeVirtualObjectRepresentative,
makeWeakStore,
makeKind,
RepairedWeakMap,
RepairedWeakSet,
VirtualObjectAwareWeakMap,
VirtualObjectAwareWeakSet,
} = makeVirtualObjectManager(
syscall,
allocateExportID,
Expand Down Expand Up @@ -816,8 +816,8 @@ function build(
});

const inescapableGlobalProperties = harden({
WeakMap: RepairedWeakMap,
WeakSet: RepairedWeakSet,
WeakMap: VirtualObjectAwareWeakMap,
WeakSet: VirtualObjectAwareWeakSet,
});

function setBuildRootObject(buildRootObject) {
Expand Down
20 changes: 10 additions & 10 deletions packages/SwingSet/src/kernel/virtualObjectManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,8 @@ export function makeVirtualObjectManager(
function virtualObjectKey(key) {
const vobjID = valToSlot.get(key);
if (vobjID) {
const { type, virtual } = parseVatSlot(vobjID);
if (type === 'object' && virtual) {
const { type, virtual, allocatedByVat } = parseVatSlot(vobjID);
if (type === 'object' && (virtual || !allocatedByVat)) {
return `ws${storeID}.${vobjID}`;
}
}
Expand Down Expand Up @@ -320,8 +320,8 @@ export function makeVirtualObjectManager(
function vrefKey(value) {
const vobjID = valToSlot.get(value);
if (vobjID) {
const { type, virtual } = parseVatSlot(vobjID);
if (type === 'object' && virtual) {
const { type, virtual, allocatedByVat } = parseVatSlot(vobjID);
if (type === 'object' && (virtual || !allocatedByVat)) {
return vobjID;
}
}
Expand All @@ -333,7 +333,7 @@ export function makeVirtualObjectManager(
const actualWeakMaps = new WeakMap();
const virtualObjectMaps = new WeakMap();

class RepairedWeakMap {
class VirtualObjectAwareWeakMap {
constructor() {
actualWeakMaps.set(this, new WeakMap());
virtualObjectMaps.set(this, new Map());
Expand Down Expand Up @@ -377,7 +377,7 @@ export function makeVirtualObjectManager(
}
}

Object.defineProperty(RepairedWeakMap, Symbol.toStringTag, {
Object.defineProperty(VirtualObjectAwareWeakMap, Symbol.toStringTag, {
value: 'WeakMap',
writable: false,
enumerable: false,
Expand All @@ -387,7 +387,7 @@ export function makeVirtualObjectManager(
const actualWeakSets = new WeakMap();
const virtualObjectSets = new WeakMap();

class RepairedWeakSet {
class VirtualObjectAwareWeakSet {
constructor() {
actualWeakSets.set(this, new WeakSet());
virtualObjectSets.set(this, new Set());
Expand Down Expand Up @@ -422,7 +422,7 @@ export function makeVirtualObjectManager(
}
}

Object.defineProperty(RepairedWeakSet, Symbol.toStringTag, {
Object.defineProperty(VirtualObjectAwareWeakSet, Symbol.toStringTag, {
value: 'WeakSet',
writable: false,
enumerable: false,
Expand Down Expand Up @@ -613,8 +613,8 @@ export function makeVirtualObjectManager(
return harden({
makeWeakStore,
makeKind,
RepairedWeakMap,
RepairedWeakSet,
VirtualObjectAwareWeakMap,
VirtualObjectAwareWeakSet,
flushCache: cache.flush,
makeVirtualObjectRepresentative,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,8 @@ test('virtualized weak collection operations', t => {
// collections

const {
RepairedWeakMap,
RepairedWeakSet,
VirtualObjectAwareWeakMap,
VirtualObjectAwareWeakSet,
makeKind,
} = makeFakeVirtualObjectManager(3);

Expand All @@ -359,8 +359,8 @@ test('virtualized weak collection operations', t => {
const zot3 = zotMaker(3, 'z3');
const zot4 = zotMaker(4, 'z4');

const wm1 = new RepairedWeakMap();
const wm2 = new RepairedWeakMap();
const wm1 = new VirtualObjectAwareWeakMap();
const wm2 = new VirtualObjectAwareWeakMap();
const nv1 = {};
const nv2 = { a: 47 };
wm1.set(zot1, 'zot #1');
Expand All @@ -385,8 +385,8 @@ test('virtualized weak collection operations', t => {
wm1.set(nv2, 'non-virtual object #2 revised');
t.is(wm1.get(nv2), 'non-virtual object #2 revised');

const ws1 = new RepairedWeakSet();
const ws2 = new RepairedWeakSet();
const ws1 = new VirtualObjectAwareWeakSet();
const ws2 = new VirtualObjectAwareWeakSet();
ws1.add(zot1);
ws2.add(zot2);
ws1.add(zot3);
Expand Down
163 changes: 163 additions & 0 deletions packages/SwingSet/test/virtualObjects/test-weakcollections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/* global __dirname globalThis */
import { test } from '../../tools/prepare-test-env-ava';

// eslint-disable-next-line import/order
import path from 'path';

import { provideHostStorage } from '../../src/hostStorage';
import { initializeSwingset, makeSwingsetController } from '../../src/index';
import { makeFakeVirtualObjectManager } from '../../tools/fakeVirtualObjectManager';
import makeNextLog from '../make-nextlog';

function capdata(body, slots = []) {
return harden({ body, slots });
}

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',
vats: {
bootstrap: {
sourceSpec: path.resolve(__dirname, 'vat-weakcollections-bootstrap.js'),
},
alice: {
sourceSpec: path.resolve(__dirname, 'vat-weakcollections-alice.js'),
},
},
};

const hostStorage = provideHostStorage();
const bootstrapResult = await initializeSwingset(config, [], hostStorage);
const c = await makeSwingsetController(hostStorage, {});
const nextLog = makeNextLog(c);

await c.run();
t.deepEqual(c.kpResolution(bootstrapResult), capargs('bootstrap done'));

async function doSimple(method) {
const sendArgs = capargs([], []);
const r = c.queueToVatExport('bootstrap', 'o+0', method, sendArgs);
await c.run();
t.is(c.kpStatus(r), 'fulfilled');
return c.kpResolution(r);
}

const preGCResult = await doSimple('runProbes');
t.deepEqual(preGCResult, capargs('probes done'));
t.deepEqual(nextLog(), [
'probe of sample-object returns imported item #0',
'probe of [object Promise] returns imported item #1',
'probe of [Alleged: remember-exp] returns mer',
'probe of [Alleged: holder-vo] returns mevo',
'probe of [object Promise] returns mep',
'probe of [Alleged: forget-exp] returns fer',
'probe of [Alleged: holder-vo] returns fevo',
'probe of [object Promise] returns fep',
]);
await doSimple('betweenProbes');
gc();
const postGCResult = await doSimple('runProbes');
t.deepEqual(postGCResult, capargs('probes done'));
t.deepEqual(nextLog(), [
'probe of sample-object returns imported item #0',
'probe of [object Promise] returns undefined',
'probe of [Alleged: remember-exp] returns mer',
'probe of [Alleged: holder-vo] returns mevo',
'probe of [object Promise] returns mep',
'probe of [Alleged: forget-exp] returns fer',
'probe of [Alleged: holder-vo] returns fevo',
'probe of [object Promise] returns fep',
]);
});

test('weakMap vref handling', async t => {
insistGC(t);
const log = [];
const {
VirtualObjectAwareWeakMap,
VirtualObjectAwareWeakSet,
valToSlot,
slotToVal,
} = makeFakeVirtualObjectManager(3, log);

function addCListEntry(slot, val) {
slotToVal.set(slot, val);
valToSlot.set(val, slot);
}

function removeCListEntry(slot, val) {
slotToVal.delete(slot);
valToSlot.delete(val);
}

const weakMap = new VirtualObjectAwareWeakMap();

function checkMap(vref, label, useVRef) {
const obj = {};
addCListEntry(vref, obj);
weakMap.set(obj, label);
t.is(weakMap.get(obj), label);
removeCListEntry(vref, obj);
const obj2 = {};
addCListEntry(vref, obj2);
if (useVRef) {
t.falsy(weakMap.has(obj));
t.truthy(weakMap.has(obj2));
t.is(weakMap.get(obj), undefined);
t.is(weakMap.get(obj2), label);
} else {
t.truthy(weakMap.has(obj));
t.falsy(weakMap.has(obj2));
t.is(weakMap.get(obj), label);
t.is(weakMap.get(obj2), undefined);
}
}

checkMap('o-1', 'imported presence', true);
checkMap('o+2', 'exported remotable', false);
checkMap('o+3/4', 'exported virtual object', true);
checkMap('p-5', 'imported promise', false);
checkMap('p+6', 'exported promise', false);
checkMap('d-7', 'imported device', false);

const weakSet = new VirtualObjectAwareWeakSet();

function checkSet(vref, useVRef) {
const obj = {};
addCListEntry(vref, obj);
weakSet.add(obj);
t.truthy(weakSet.has(obj));
removeCListEntry(vref, obj);
const obj2 = {};
addCListEntry(vref, obj2);
if (useVRef) {
t.falsy(weakSet.has(obj));
t.truthy(weakSet.has(obj2));
} else {
t.truthy(weakSet.has(obj));
t.falsy(weakSet.has(obj2));
}
}

checkSet('o-8', true);
checkSet('o+9', false);
checkSet('o+10/11', true);
checkSet('p-12', false);
checkSet('p+13', false);
checkSet('d-14', false);
});
69 changes: 69 additions & 0 deletions packages/SwingSet/test/virtualObjects/vat-weakcollections-alice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* global makeKind */
import { Far } from '@agoric/marshal';

function makeHolderInstance(state) {
function init(value) {
state.value = value;
}
const self = Far('holder-vo', {
getValue() {
return state.value;
},
setValue(newValue) {
state.value = newValue;
},
});
return { init, self };
}

const holderMaker = makeKind(makeHolderInstance);

export function buildRootObject() {
const testWeakMap = new WeakMap();
let memorableExportRemotable;
let memorableExportVirtualObject;
let memorableExportPromise;
let forgetableExportRemotable;
let forgetableExportVirtualObject;
let forgetableExportPromise;

return Far('root', {
prepareWeakMap(theirStuff) {
memorableExportRemotable = Far('remember-exp', {});
memorableExportVirtualObject = holderMaker('remember-exp-vo');
memorableExportPromise = new Promise((_res, _rej) => {});
forgetableExportRemotable = Far('forget-exp', {});
forgetableExportVirtualObject = holderMaker('forget-exp-vo');
forgetableExportPromise = new Promise((_res, _rej) => {});

const result = [
memorableExportRemotable,
memorableExportVirtualObject,
memorableExportPromise,
forgetableExportRemotable,
forgetableExportVirtualObject,
forgetableExportPromise,
];

let i = 0;
for (const item of theirStuff) {
testWeakMap.set(item, `imported item #${i}`);
i += 1;
}
testWeakMap.set(memorableExportRemotable, 'mer');
testWeakMap.set(memorableExportVirtualObject, 'mevo');
testWeakMap.set(memorableExportPromise, 'mep');
testWeakMap.set(forgetableExportRemotable, 'fer');
testWeakMap.set(forgetableExportVirtualObject, 'fevo');
testWeakMap.set(forgetableExportPromise, 'fep');
forgetableExportRemotable = null;
forgetableExportVirtualObject = null;
forgetableExportPromise = null;

return result;
},
probeWeakMap(probe) {
return testWeakMap.get(probe);
},
});
}
Loading

0 comments on commit e4a32a2

Please sign in to comment.