-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
"Raw devices" bypass the deviceSlots layer and allow device code direct access to `syscall`, and the arguments arriving through the `dispatch` object it must produce. This makes some patterns much easier to implement, such as producing new device nodes as part of the device's API (e.g. one device node per code bundle). It also provides vatstoreGet/Set/Delete, so the device code can manage one piece of state at a time, instead of doing an expensive read-modify-write cycle on a single large aggregate state object. A helper library named deviceTools.js was added to make it slightly easier to write a raw device. In the longer run (see #1346), we'd like these devices to support Promises and plain object references. This change doesn't go that far. The remaining limitations are: * the deviceTools.js library refuses to handle exported objects, imported foreign device nodes, or promises of any sort * the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`) refuses to handle exported objects and exported promises * the vat outbound translator (vatTranslator.js `translateCallNow`) refuses to handle promises * liveslots rejects promises in `D()` arguments refs #1346
- Loading branch information
Showing
7 changed files
with
483 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import { assert, details as X } from '@agoric/assert'; | ||
import { makeMarshal, Far } from '@endo/marshal'; | ||
import { parseVatSlot } from './parseVatSlots.js'; | ||
|
||
// raw devices can use this to build a set of convenience tools for | ||
// serialization/unserialization | ||
|
||
export function buildSerializationTools(syscall, deviceName) { | ||
// TODO: prevent our Presence/DeviceNode objects from being accidentally be | ||
// marshal-serialized into persistent state | ||
|
||
const presences = new WeakMap(); | ||
const myDeviceNodes = new WeakMap(); | ||
|
||
function slotFromPresence(p) { | ||
return presences.get(p); | ||
} | ||
function presenceForSlot(slot) { | ||
const { type, allocatedByVat } = parseVatSlot(slot); | ||
assert.equal(type, 'object'); | ||
assert.equal(allocatedByVat, false); | ||
const p = Far('presence', { | ||
send(method, args) { | ||
assert.typeof(method, 'string'); | ||
assert(Array.isArray(args), args); | ||
// eslint-disable-next-line no-use-before-define | ||
const capdata = serialize(args); | ||
syscall.sendOnly(slot, method, capdata); | ||
}, | ||
}); | ||
presences.set(p, slot); | ||
return p; | ||
} | ||
|
||
function slotFromMyDeviceNode(dn) { | ||
return myDeviceNodes.get(dn); | ||
} | ||
function deviceNodeForSlot(slot) { | ||
const { type, allocatedByVat } = parseVatSlot(slot); | ||
assert.equal(type, 'device'); | ||
assert.equal(allocatedByVat, true); | ||
const dn = Far('device node', {}); | ||
myDeviceNodes.set(dn, slot); | ||
return dn; | ||
} | ||
|
||
function convertSlotToVal(slot) { | ||
const { type, allocatedByVat } = parseVatSlot(slot); | ||
if (type === 'object') { | ||
assert(!allocatedByVat, X`devices cannot yet allocate objects ${slot}`); | ||
return presenceForSlot(slot); | ||
} else if (type === 'device') { | ||
assert( | ||
allocatedByVat, | ||
X`devices should yet not be given other devices '${slot}'`, | ||
); | ||
return deviceNodeForSlot(slot); | ||
} else if (type === 'promise') { | ||
assert.fail(X`devices should not yet be given promises '${slot}'`); | ||
} else { | ||
assert.fail(X`unrecognized slot type '${type}'`); | ||
} | ||
} | ||
|
||
function convertValToSlot(val) { | ||
const objSlot = slotFromPresence(val); | ||
if (objSlot) { | ||
return objSlot; | ||
} | ||
const devnodeSlot = slotFromMyDeviceNode(val); | ||
if (devnodeSlot) { | ||
return devnodeSlot; | ||
} | ||
throw Error(X`unable to convert value ${val}`); | ||
} | ||
|
||
const m = makeMarshal(convertValToSlot, convertSlotToVal, { | ||
marshalName: `device:${deviceName}`, | ||
// TODO Temporary hack. | ||
// See https://github.com/Agoric/agoric-sdk/issues/2780 | ||
errorIdNum: 60000, | ||
}); | ||
|
||
// for invoke(), these will unserialize the arguments, and serialize the | ||
// response (into a vatresult with the 'ok' header) | ||
const unserialize = capdata => m.unserialize(capdata); | ||
const serialize = data => m.serialize(harden(data)); | ||
const returnFromInvoke = args => harden(['ok', serialize(args)]); | ||
|
||
const tools = { | ||
slotFromPresence, | ||
presenceForSlot, | ||
slotFromMyDeviceNode, | ||
deviceNodeForSlot, | ||
unserialize, | ||
returnFromInvoke, | ||
}; | ||
|
||
return harden(tools); | ||
} | ||
harden(buildSerializationTools); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.