Skip to content

Commit

Permalink
refactor(daemon): Maker table (#2209)
Browse files Browse the repository at this point in the history
Converts the maker if/else chain into a lookup table for faster dispatch
and better type hints. The new maker table type identified the
extraneous WebBundleFormula left over from an earlier refactor.
  • Loading branch information
kriskowal authored Apr 15, 2024
2 parents 94b8ac3 + b5523a9 commit 1f6c0b4
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 104 deletions.
192 changes: 102 additions & 90 deletions packages/daemon/src/daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,87 +460,93 @@ const makeDaemonCore = async (
);
};

/**
* @param {string} id
* @param {string} formulaNumber
* @param {import('./types.js').Formula} formula
* @param {import('./types.js').Context} context
*/
const evaluateFormula = async (id, formulaNumber, formula, context) => {
if (formula.type === 'eval') {
return makeEval(
formula.worker,
formula.source,
formula.names,
formula.values,
context,
);
} else if (formula.type === 'readable-blob') {
return makeReadableBlob(formula.content);
} else if (formula.type === 'lookup') {
return makeLookup(formula.hub, formula.path, context);
} else if (formula.type === 'worker') {
return makeIdentifiedWorker(formulaNumber, context);
} else if (formula.type === 'make-unconfined') {
return makeUnconfined(
formula.worker,
formula.powers,
formula.specifier,
context,
);
} else if (formula.type === 'make-bundle') {
return makeBundle(
formula.worker,
formula.powers,
formula.bundle,
context,
);
} else if (formula.type === 'host') {
/** @type {import('./types.js').FormulaMakerTable} */
const makers = {
eval: ({ worker, source, names, values }, context) =>
makeEval(worker, source, names, values, context),
'readable-blob': ({ content }) => makeReadableBlob(content),
lookup: ({ hub, path }, context) => makeLookup(hub, path, context),
worker: (_formula, context, _id, formulaNumber) =>
makeIdentifiedWorker(formulaNumber, context),
'make-unconfined': (
{ worker: workerId, powers: powersId, specifier },
context,
) => makeUnconfined(workerId, powersId, specifier, context),
'make-bundle': (
{ worker: workerId, powers: powersId, bundle: bundleId },
context,
) => makeBundle(workerId, powersId, bundleId, context),
host: async (
{
handle: handleId,
petStore: petStoreId,
inspector: inspectorId,
worker: workerId,
endo: endoId,
networks: networksId,
},
context,
id,
) => {
// Behold, recursion:
// eslint-disable-next-line no-use-before-define
const agent = await makeHost(
id,
formula.handle,
formula.petStore,
formula.inspector,
formula.worker,
formula.endo,
formula.networks,
handleId,
petStoreId,
inspectorId,
workerId,
endoId,
networksId,
leastAuthorityId,
platformNames,
context,
);
const handle = agent.handle();
agentIdForHandle.set(handle, id);
return agent;
} else if (formula.type === 'guest') {
},
guest: async (
{
handle: handleId,
hostAgent: hostAgentId,
hostHandle: hostHandleId,
petStore: petStoreId,
worker: workerId,
},
context,
id,
) => {
// Behold, recursion:
// eslint-disable-next-line no-use-before-define
const agent = await makeGuest(
id,
formula.handle,
formula.hostAgent,
formula.hostHandle,
formula.petStore,
formula.worker,
handleId,
hostAgentId,
hostHandleId,
petStoreId,
workerId,
context,
);
const handle = agent.handle();
agentIdForHandle.set(handle, id);
return agent;
} else if (formula.type === 'handle') {
},
handle: async ({ agent: agentId }) => {
const agent = /** @type {import('./types.js').EndoAgent} */ (
// Behold, recursion:
// eslint-disable-next-line no-use-before-define
await provide(formula.agent)
await provide(agentId)
);
const handle = agent.handle();
agentIdForHandle.set(handle, formula.agent);
agentIdForHandle.set(handle, agentId);
return handle;
} else if (formula.type === 'endo') {
},
endo: async ({ host: hostId, networks: networksId, peers: peersId }) => {
// Gateway is equivalent to E's "nonce locator". It provides a value for
// a formula identifier to a remote client.
const gateway = Far('Gateway', {
/** @param {string} requestedId */
provide: async requestedId => {
const { node } = parseId(requestedId);
if (node !== ownNodeIdentifier) {
Expand All @@ -565,7 +571,7 @@ const makeDaemonCore = async (
return /** @type {Promise<import('./types.js').EndoHost>} */ (
// Behold, recursion:
// eslint-disable-next-line no-use-before-define
provide(formula.host)
provide(hostId)
);
},
leastAuthority: () => {
Expand All @@ -583,7 +589,7 @@ const makeDaemonCore = async (
/** @type {import('./types.js').EndoDirectory} */ (
// Behold, recursion:
// eslint-disable-next-line no-use-before-define
await provide(formula.networks)
await provide(networksId)
);
const networkIds = await networksDirectory.listIdentifiers();
await Promise.allSettled(
Expand All @@ -597,7 +603,7 @@ const makeDaemonCore = async (
addPeerInfo: async peerInfo => {
const knownPeers = /** @type {import('./types.js').PetStore} */ (
// eslint-disable-next-line no-use-before-define
await provide(formula.peers)
await provide(peersId)
);
const { node: nodeIdentifier, addresses } = peerInfo;
if (knownPeers.has(nodeIdentifier)) {
Expand All @@ -607,16 +613,19 @@ const makeDaemonCore = async (
}
const { id: peerId } =
// eslint-disable-next-line no-use-before-define
await formulatePeer(formula.networks, addresses);
await formulatePeer(networksId, addresses);
await knownPeers.write(nodeIdentifier, peerId);
},
});
return endoBootstrap;
} else if (formula.type === 'loopback-network') {
// Behold, forward-reference:
// eslint-disable-next-line no-use-before-define
return makeLoopbackNetwork({ provide });
} else if (formula.type === 'least-authority') {
},
'loopback-network': () =>
makeLoopbackNetwork({
// Behold, forward-reference:
// eslint-disable-next-line no-use-before-define
provide,
}),
'least-authority': () => {
const disallowedFn = async () => {
throw new Error('not allowed');
};
Expand Down Expand Up @@ -649,35 +658,49 @@ const makeDaemonCore = async (
)
)
);
} else if (formula.type === 'pet-store') {
return petStorePowers.makeIdentifiedPetStore(
},
'pet-store': (_formula, _context, _id, formulaNumber) =>
petStorePowers.makeIdentifiedPetStore(
formulaNumber,
'pet-store',
assertPetName,
);
} else if (formula.type === 'known-peers-store') {
return petStorePowers.makeIdentifiedPetStore(
),
'known-peers-store': (_formula, _context, _id, formulaNumber) =>
petStorePowers.makeIdentifiedPetStore(
formulaNumber,
'known-peers-store',
// The known peers store is just a pet store that only accepts node identifiers
// (i.e. formula numbers) as "names".
assertValidNumber,
);
} else if (formula.type === 'pet-inspector') {
),
'pet-inspector': ({ petStore: petStoreId }) =>
// Behold, unavoidable forward-reference:
// eslint-disable-next-line no-use-before-define
return makePetStoreInspector(formula.petStore);
} else if (formula.type === 'directory') {
makePetStoreInspector(petStoreId),
directory: ({ petStore: petStoreId }, context) =>
// Behold, forward-reference:
// eslint-disable-next-line no-use-before-define
return makeIdentifiedDirectory({
petStoreId: formula.petStore,
makeIdentifiedDirectory({
petStoreId,
context,
});
} else if (formula.type === 'peer') {
}),
peer: ({ networks: networksId, addresses: addressesId }, context) =>
// Behold, forward reference:
// eslint-disable-next-line no-use-before-define
return makePeer(formula.networks, formula.addresses, context);
makePeer(networksId, addressesId, context),
};

/**
* @param {string} id
* @param {string} formulaNumber
* @param {import('./types.js').Formula} formula
* @param {import('./types.js').Context} context
*/
const evaluateFormula = async (id, formulaNumber, formula, context) => {
if (Object.hasOwn(makers, formula.type)) {
const make = makers[formula.type];
// @ts-expect-error TypeScript is trying too hard to infer the unknown.
return /** @type {unknown} */ (make(formula, context, id, formulaNumber));
} else {
throw new TypeError(`Invalid formula: ${q(formula)}`);
}
Expand Down Expand Up @@ -746,6 +769,7 @@ const makeDaemonCore = async (
context,
).then(value => {
if (typeof value === 'object' && value !== null) {
// @ts-expect-error TypeScript seems to believe the value might be a string here.
idForRef.add(value, id);
}
return value;
Expand Down Expand Up @@ -787,6 +811,9 @@ const makeDaemonCore = async (
return controller;
};

/** @type {import('./types.js').DaemonCore['provide']} */
const provide = id => provideController(id).value;

/**
* @param {string} nodeIdentifier
* @returns {Promise<string>}
Expand Down Expand Up @@ -816,21 +843,6 @@ const makeDaemonCore = async (
return controller.context.cancel(reason);
};

/** @type {import('./types.js').DaemonCore['provide']} */
const provide = id => {
const controller = /** @type {import('./types.js').Controller<>} */ (
provideController(id)
);
return controller.value.then(value => {
// Release the value to the public only after ensuring
// we can reverse-lookup its nonce.
if (typeof value === 'object' && value !== null) {
idForRef.add(value, id);
}
return value;
});
};

/** @type {import('./types.js').DaemonCore['formulateReadableBlob']} */
const formulateReadableBlob = async (readerRef, deferredTasks) => {
const { formulaNumber, contentSha512 } = await formulaGraphJobs.enqueue(
Expand Down
12 changes: 6 additions & 6 deletions packages/daemon/src/mail.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const makeMailboxMaker = ({ provide }) => {
/**
* @param {import('./types.js').EnvelopedMessage} envelope
*/
const deliver = async envelope => {
const deliver = envelope => {
/** @type {import('@endo/promise-kit/src/types.js').PromiseKit<void>} */
const dismissal = makePromiseKit();
const messageNumber = nextMessageNumber;
Expand Down Expand Up @@ -119,13 +119,13 @@ export const makeMailboxMaker = ({ provide }) => {
* @param {import('./types.js').Handle} recipient
* @param {import('./types.js').EnvelopedMessage} message
*/
const post = (recipient, message) => {
const post = async (recipient, message) => {
/** @param {object} allegedRecipient */
const envelope = makeEnvelope();
outbox.set(envelope, message);
E.sendOnly(recipient).receive(envelope, selfId);
await E(recipient).receive(envelope, selfId);
// Send to own inbox.
if (message.from !== message.to) {
// echo to own mailbox
deliver(message);
}
};
Expand Down Expand Up @@ -212,7 +212,7 @@ export const makeMailboxMaker = ({ provide }) => {
});

// add to recipient mailbox
post(to, message);
await post(to, message);
};

/** @type {import('./types.js').Mail['dismiss']} */
Expand Down Expand Up @@ -290,7 +290,7 @@ export const makeMailboxMaker = ({ provide }) => {
);

// Note: consider sending to each mailbox with different powers.
post(to, req);
await post(to, req);

const responseId = await responseIdP;
const responseP = provide(responseId);
Expand Down
19 changes: 11 additions & 8 deletions packages/daemon/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,6 @@ type PeerFormula = {
addresses: Array<string>;
};

type WebBundleFormula = {
type: 'web-bundle';
bundle: string;
powers: string;
};

type HandleFormula = {
type: 'handle';
agent: string;
Expand Down Expand Up @@ -215,7 +209,6 @@ export type Formula =
| LookupFormula
| MakeUnconfinedFormula
| MakeBundleFormula
| WebBundleFormula
| HandleFormula
| PetInspectorFormula
| KnownPeersStoreFormula
Expand Down Expand Up @@ -347,6 +340,17 @@ export interface Controller<Value = unknown> {
context: Context;
}

export type FormulaMaker<F extends Formula> = (
formula: F,
context: Context,
id: string,
number: string,
) => unknown;

export type FormulaMakerTable = {
[T in Formula['type']]: FormulaMaker<{ type: T } & Formula>;
};

export interface Envelope {}

export interface Handle {
Expand Down Expand Up @@ -551,7 +555,6 @@ export type KnownEndoInspectors = {
'make-unconfined': EndoInspector<'host'>;
'make-bundle': EndoInspector<'bundle' | 'powers' | 'worker'>;
guest: EndoInspector<'bundle' | 'powers'>;
'web-bundle': EndoInspector<'powers' | 'specifier' | 'worker'>;
// This is an "empty" inspector, in that there is nothing to `lookup()` or `list()`.
[formulaType: string]: EndoInspector<string>;
};
Expand Down

0 comments on commit 1f6c0b4

Please sign in to comment.