Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

Commit

Permalink
Merge branch '26-devices'
Browse files Browse the repository at this point in the history
* add SO(presence) to let device code sendOnly() to vats
* more tests

refs #26
  • Loading branch information
warner committed May 1, 2019
2 parents 1e6b327 + ecd0497 commit 6e9d394
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 25 deletions.
45 changes: 44 additions & 1 deletion src/kernel/deviceSlots.js
Expand Up @@ -4,7 +4,7 @@ import { QCLASS, mustPassByPresence, makeMarshal } from './marshal';

// 'makeDeviceSlots' is a subset of makeLiveSlots, for device code

function build(syscall, rootObject, forDeviceName) {
function build(syscall, makeRoot, forDeviceName) {
const enableLSDebug = false;
function lsdebug(...args) {
if (enableLSDebug) {
Expand All @@ -18,6 +18,8 @@ function build(syscall, rootObject, forDeviceName) {
});
}

const outstandingProxies = new WeakSet();

function slotToKey(slot) {
if (
slot.type === 'deviceExport' ||
Expand Down Expand Up @@ -117,6 +119,46 @@ function build(syscall, rootObject, forDeviceName) {

const m = makeMarshal(serializeSlot, unserializeSlot);

function PresenceHandler(importSlot) {
return {
get(target, prop) {
lsdebug(`PreH proxy.get(${prop})`);
if (prop !== `${prop}`) {
return undefined;
}
const p = (...args) => {
const ser = m.serialize(harden({ args }));
syscall.sendOnly(importSlot, prop, ser.argsString, ser.slots);
};
return p;
},
has(_target, _prop) {
return true;
},
};
}

function SO(x) {
// SO(x).name(args)
//
// SO returns a proxy, like the E() in liveSlots. However SO's proxy does
// a sendOnly() rather than a send(), so it doesn't return a Promise. And
// since devices don't accept Promises either, SO(x) must be given a
// presence, not a promise that might resolve to a presence.

if (outstandingProxies.has(x)) {
throw new Error('SO(SO(x)) is invalid');
}
const slot = valToSlot.get(x);
if (!slot || slot.type !== 'import') {
throw new Error(`SO(x) must be called on a Presence, not ${x}`);
}
const handler = PresenceHandler(slot);
const p = harden(new Proxy({}, handler));
outstandingProxies.add(p);
return p;
}

function invoke(deviceID, method, data, slots) {
lsdebug(`ls[${forDeviceName}].dispatch.invoke ${deviceID}.${method}`);
const t = getTarget(deviceID);
Expand All @@ -141,6 +183,7 @@ function build(syscall, rootObject, forDeviceName) {
return harden({ data: ser.argsString, slots: ser.slots });
}

const rootObject = makeRoot(SO);
mustPassByPresence(rootObject);
const rootSlot = { type: 'deviceExport', id: 0 };
valToSlot.set(rootObject, rootSlot);
Expand Down
24 changes: 21 additions & 3 deletions test/files-devices/bootstrap-2.js
Expand Up @@ -5,9 +5,9 @@ export default function setup(syscall, state, helpers) {
return helpers.makeLiveSlots(
syscall,
state,
(_E, D) =>
(E, D) =>
harden({
bootstrap(argv, vats, devices) {
async bootstrap(argv, vats, devices) {
if (argv[0] === '1') {
log(`calling d2.method1`);
const ret = D(devices.d2).method1('hello');
Expand All @@ -23,7 +23,25 @@ export default function setup(syscall, state, helpers) {
// they should still be able to accept and return them
const o = harden({});
const ret = D(devices.d2).method3(o);
log(`ret ${ret.obj === o}`);
log(`ret ${ret === o}`);
} else if (argv[0] === '4') {
log(`calling d2.method4`);
// now exercise sendOnly on pass-by-presence objects
const o = harden({
foo(obj) {
log(`d2.m4 foo`);
D(obj).bar('hello');
log(`d2.m4 did bar`);
},
});
const ret = D(devices.d2).method4(o);
log(`ret ${ret}`);
} else if (argv[0] === '5') {
log(`calling v2.method5`);
const p = E(vats.left).left5(devices.d2);
log(`called`);
const ret = await p;
log(`ret ${ret}`);
}
},
}),
Expand Down
57 changes: 36 additions & 21 deletions test/files-devices/device-2.js
Expand Up @@ -4,27 +4,42 @@ export default function setup(syscall, helpers, _endowments) {
const { log } = helpers;
return helpers.makeDeviceSlots(
syscall,
harden({
method1(arg) {
log(`method1 ${arg}`);
return 'done';
},
method2() {
const d2 = harden({});
const d3 = harden({
method3(arg) {
log(`method3 ${arg === d2}`);
return harden({ key: 'value' });
},
});
log(`method2`);
return harden([d2, d3]);
},
method3(o) {
log(`method3`);
return o;
},
}),
SO =>
harden({
method1(arg) {
log(`method1 ${arg}`);
return 'done';
},
method2() {
const d2 = harden({});
const d3 = harden({
method3(arg) {
log(`method3 ${arg === d2}`);
return harden({ key: 'value' });
},
});
log(`method2`);
return harden([d2, d3]);
},
method3(o) {
log(`method3`);
return o;
},
method4(o) {
log(`method4`);
const obj = harden({
bar(arg) {
log(`method4.bar ${arg}`);
},
});
SO(o).foo(obj);
return 'method4 done';
},
method5(arg) {
log(`method5 ${arg}`);
return 'ok';
},
}),
helpers.name,
);
}
19 changes: 19 additions & 0 deletions test/files-devices/vat-left.js
@@ -0,0 +1,19 @@
const harden = require('@agoric/harden');

export default function setup(syscall, state, helpers) {
const { log } = helpers;
return helpers.makeLiveSlots(
syscall,
state,
(E, D) =>
harden({
left5(d2) {
log(`left5`);
const ret = D(d2).method5('hello');
log(`left5 did d2.method5, got ${ret}`);
return 'done';
},
}),
helpers.vatID,
);
}
53 changes: 53 additions & 0 deletions test/test-devices.js
Expand Up @@ -80,6 +80,7 @@ async function test2(t, mode, withSES) {
devices: [['d2', require.resolve('./files-devices/device-2'), {}]],
bootstrapIndexJS: require.resolve('./files-devices/bootstrap-2'),
};
config.vatSources.set('left', require.resolve('./files-devices/vat-left.js'));
const c = await buildVatController(config, withSES, [mode]);
await c.step();
if (mode === '1') {
Expand All @@ -91,6 +92,42 @@ async function test2(t, mode, withSES) {
'method3 true',
'value',
]);
} else if (mode === '3') {
t.deepEqual(c.dump().log, ['calling d2.method3', 'method3', 'ret true']);
} else if (mode === '4') {
t.deepEqual(c.dump().log, [
'calling d2.method4',
'method4',
'ret method4 done',
]);
await c.step();
t.deepEqual(c.dump().log, [
'calling d2.method4',
'method4',
'ret method4 done',
'd2.m4 foo',
'method4.bar hello',
'd2.m4 did bar',
]);
} else if (mode === '5') {
t.deepEqual(c.dump().log, ['calling v2.method5', 'called']);
await c.step();
t.deepEqual(c.dump().log, [
'calling v2.method5',
'called',
'left5',
'method5 hello',
'left5 did d2.method5, got ok',
]);
await c.step();
t.deepEqual(c.dump().log, [
'calling v2.method5',
'called',
'left5',
'method5 hello',
'left5 did d2.method5, got ok',
'ret done',
]);
}
t.end();
}
Expand Down Expand Up @@ -118,3 +155,19 @@ test('d2.3 with SES', async t => {
test('d2.3 without SES', async t => {
await test2(t, '3', false);
});

test('d2.4 with SES', async t => {
await test2(t, '4', true);
});

test('d2.4 without SES', async t => {
await test2(t, '4', false);
});

test('d2.5 with SES', async t => {
await test2(t, '5', true);
});

test('d2.5 without SES', async t => {
await test2(t, '5', false);
});

0 comments on commit 6e9d394

Please sign in to comment.