@metamask/ocap-kernel
Version:
OCap kernel core components
288 lines • 15.5 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _KernelRouter_instances, _KernelRouter_kernelStore, _KernelRouter_kernelQueue, _KernelRouter_getVat, _KernelRouter_invokeKernelService, _KernelRouter_logger, _KernelRouter_routeMessage, _KernelRouter_deliverSend, _KernelRouter_deliverKernelServiceMessage, _KernelRouter_deliverNotify, _KernelRouter_deliverGCAction, _KernelRouter_deliverBringOutYourDead;
import { Logger } from "@metamask/logger";
import { KernelQueue } from "./KernelQueue.mjs";
import { kser } from "./services/kernel-marshal.mjs";
import { extractSingleRef } from "./store/utils/extract-ref.mjs";
import { parseRef } from "./store/utils/parse-ref.mjs";
import { isPromiseRef } from "./store/utils/promise-ref.mjs";
import { insistVatId, insistMessage } from "./types.mjs";
import { assert, Fail } from "./utils/assert.mjs";
import { VatHandle } from "./VatHandle.mjs";
/**
* The KernelRouter is responsible for routing messages to the correct vat.
*
* This class is responsible for routing messages to the correct vat, including
* sending messages, resolving promises, and dropping imports.
*/
export class KernelRouter {
/**
* Construct a new KernelRouter.
*
* @param kernelStore - The kernel's store.
* @param kernelQueue - The kernel's queue.
* @param getVat - A function that returns a vat handle for a given vat id.
* @param invokeKernelService - A function that calls a method on a kernel service object.
* @param logger - The logger. If not provided, no logging will be done.
*/
constructor(kernelStore, kernelQueue, getVat, invokeKernelService, logger) {
_KernelRouter_instances.add(this);
/** The kernel's store. */
_KernelRouter_kernelStore.set(this, void 0);
/** The kernel's queue. */
_KernelRouter_kernelQueue.set(this, void 0);
/** A function that returns a vat handle for a given vat id. */
_KernelRouter_getVat.set(this, void 0);
/** A function that invokes a method on a kernel service. */
_KernelRouter_invokeKernelService.set(this, void 0);
/** The logger, if any. */
_KernelRouter_logger.set(this, void 0);
__classPrivateFieldSet(this, _KernelRouter_kernelStore, kernelStore, "f");
__classPrivateFieldSet(this, _KernelRouter_kernelQueue, kernelQueue, "f");
__classPrivateFieldSet(this, _KernelRouter_getVat, getVat, "f");
__classPrivateFieldSet(this, _KernelRouter_invokeKernelService, invokeKernelService, "f");
__classPrivateFieldSet(this, _KernelRouter_logger, logger, "f");
}
/**
* Deliver a run queue item to its target.
*
* If the item being delivered is message whose target is a promise, it is
* delivered based on the kernel's model of the promise's state:
* - unresolved: it is put onto the queue that the kernel maintains for that promise
* - fulfilled: it is forwarded to the promise resolution target
* - rejected: the result promise of the message is in turn rejected according
* to the kernel's model of the promise's rejection value
*
* If the item being delivered is a notification, the kernel's model of the
* state of the promise being notified is updated, and any queue items
* enqueued for that promise are placed onto the run queue. The notification
* is also forwarded to all of the promise's registered subscribers.
*
* @param item - The message/notification to deliver.
* @returns The crank outcome.
*/
async deliver(item) {
switch (item.type) {
case 'send':
return await __classPrivateFieldGet(this, _KernelRouter_instances, "m", _KernelRouter_deliverSend).call(this, item);
case 'notify':
return await __classPrivateFieldGet(this, _KernelRouter_instances, "m", _KernelRouter_deliverNotify).call(this, item);
case 'dropExports':
case 'retireExports':
case 'retireImports':
return await __classPrivateFieldGet(this, _KernelRouter_instances, "m", _KernelRouter_deliverGCAction).call(this, item);
case 'bringOutYourDead':
return await __classPrivateFieldGet(this, _KernelRouter_instances, "m", _KernelRouter_deliverBringOutYourDead).call(this, item);
default:
// @ts-expect-error Runtime does not respect "never".
Fail `unsupported or unknown run queue item type ${item.type}`;
}
return undefined;
}
}
_KernelRouter_kernelStore = new WeakMap(), _KernelRouter_kernelQueue = new WeakMap(), _KernelRouter_getVat = new WeakMap(), _KernelRouter_invokeKernelService = new WeakMap(), _KernelRouter_logger = new WeakMap(), _KernelRouter_instances = new WeakSet(), _KernelRouter_routeMessage = function _KernelRouter_routeMessage(item) {
const { target, message } = item;
insistMessage(message);
const routeAsSplat = (error) => {
if (message.result && error) {
__classPrivateFieldGet(this, _KernelRouter_kernelQueue, "f").resolvePromises(undefined, [
[message.result, true, error],
]);
}
return null;
};
const routeAsSend = (targetObject) => {
if (__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").isRevoked(targetObject)) {
return routeAsSplat(kser('revoked object'));
}
const vatId = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").getOwner(targetObject);
if (!vatId) {
return routeAsSplat(kser('no vat'));
}
return { vatId, target: targetObject };
};
const routeAsRequeue = (targetObject) => {
return { target: targetObject };
};
if (isPromiseRef(target)) {
const promise = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").getKernelPromise(target);
switch (promise.state) {
case 'fulfilled': {
if (promise.value) {
const targetObject = extractSingleRef(promise.value);
if (targetObject) {
if (isPromiseRef(targetObject)) {
return routeAsRequeue(targetObject);
}
return routeAsSend(targetObject);
}
}
return routeAsSplat(kser('no object'));
}
case 'rejected':
return routeAsSplat(promise.value);
case 'unresolved':
return routeAsRequeue(target);
default:
throw Fail `unknown promise state ${promise.state}`;
}
}
else {
return routeAsSend(target);
}
}, _KernelRouter_deliverSend =
/**
* Deliver a 'send' run queue item.
*
* @param item - The send item to deliver.
* @returns The crank outcome.
*/
async function _KernelRouter_deliverSend(item) {
const route = __classPrivateFieldGet(this, _KernelRouter_instances, "m", _KernelRouter_routeMessage).call(this, item);
let crankResults;
// Message went splat
if (!route) {
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").decrementRefCount(item.target, 'deliver|splat|target');
if (item.message.result) {
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").decrementRefCount(item.message.result, 'deliver|splat|result');
}
for (const slot of item.message.methargs.slots) {
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").decrementRefCount(slot, 'deliver|splat|slot');
}
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ message went splat ${item.target}<-${JSON.stringify(item.message)}`);
return crankResults;
}
const { vatId, target } = route;
const { message } = item;
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ deliver ${vatId} send ${target}<-${JSON.stringify(message)}`);
if (vatId) {
const isKernelServiceMessage = vatId === 'kernel';
const vat = isKernelServiceMessage ? null : __classPrivateFieldGet(this, _KernelRouter_getVat, "f").call(this, vatId);
if (vat || isKernelServiceMessage) {
if (message.result) {
if (typeof message.result !== 'string') {
throw TypeError('message result must be a string');
}
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").setPromiseDecider(message.result, vatId);
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").decrementRefCount(message.result, 'deliver|send|result');
}
}
if (vat) {
const vatTarget = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").translateRefKtoV(vatId, target, false);
const vatMessage = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").translateMessageKtoV(vatId, message);
crankResults = await vat.deliverMessage(vatTarget, vatMessage);
}
else if (isKernelServiceMessage) {
crankResults = await __classPrivateFieldGet(this, _KernelRouter_instances, "m", _KernelRouter_deliverKernelServiceMessage).call(this, target, message);
}
else {
Fail `no owner for kernel object ${target}`;
}
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").decrementRefCount(target, 'deliver|send|target');
for (const slot of message.methargs.slots) {
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").decrementRefCount(slot, 'deliver|send|slot');
}
}
else {
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").enqueuePromiseMessage(target, message);
}
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ done ${vatId} send ${target}<-${JSON.stringify(message)}`);
return crankResults;
}, _KernelRouter_deliverKernelServiceMessage = async function _KernelRouter_deliverKernelServiceMessage(target, message) {
await __classPrivateFieldGet(this, _KernelRouter_invokeKernelService, "f").call(this, target, message);
return { didDelivery: 'kernel' };
}, _KernelRouter_deliverNotify =
/**
* Deliver a 'notify' run queue item.
*
* @param item - The notify item to deliver.
* @returns The crank outcome.
*/
async function _KernelRouter_deliverNotify(item) {
const { vatId, kpid } = item;
insistVatId(vatId);
const { context, isPromise } = parseRef(kpid);
assert(context === 'kernel' && isPromise, `${kpid} is not a kernel promise`);
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ deliver ${vatId} notify ${vatId} ${kpid}`);
const promise = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").getKernelPromise(kpid);
const { state, value } = promise;
assert(value, `no value for promise ${kpid}`);
if (state === 'unresolved') {
Fail `notification on unresolved promise ${kpid}`;
}
if (!__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").krefToEref(vatId, kpid)) {
// no c-list entry, already done
return { didDelivery: vatId };
}
const targets = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").getKpidsToRetire(kpid, value);
if (targets.length === 0) {
// no kpids to retire, already done
return { didDelivery: vatId };
}
const resolutions = [];
for (const toResolve of targets) {
const tPromise = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").getKernelPromise(toResolve);
if (tPromise.state === 'unresolved') {
Fail `target promise ${toResolve} is unresolved`;
}
if (!tPromise.value) {
throw Fail `target promise ${toResolve} has no value`;
}
resolutions.push([
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").translateRefKtoV(vatId, toResolve, true),
tPromise.state === 'rejected',
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").translateCapDataKtoV(vatId, tPromise.value),
]);
// decrement refcount for the promise being notified
if (toResolve !== kpid) {
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").decrementRefCount(toResolve, 'deliver|notify|slot');
}
}
const vat = __classPrivateFieldGet(this, _KernelRouter_getVat, "f").call(this, vatId);
const crankResults = await vat.deliverNotify(resolutions);
// Decrement reference count for processed 'notify' item
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").decrementRefCount(kpid, 'deliver|notify');
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ done ${vatId} notify ${vatId} ${kpid}`);
return crankResults;
}, _KernelRouter_deliverGCAction =
/**
* Deliver a Garbage Collection action run queue item.
*
* @param item - The dropExports | retireExports | retireImports item to deliver.
* @returns The crank outcome.
*/
async function _KernelRouter_deliverGCAction(item) {
const { type, vatId, krefs } = item;
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ deliver ${vatId} ${type}`, krefs);
const vat = __classPrivateFieldGet(this, _KernelRouter_getVat, "f").call(this, vatId);
const vrefs = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").krefsToExistingErefs(vatId, krefs);
const method = `deliver${type[0].toUpperCase()}${type.slice(1)}`;
const crankResults = await vat[method](vrefs);
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ done ${vatId} ${type}`, krefs);
return crankResults;
}, _KernelRouter_deliverBringOutYourDead =
/**
* Deliver a 'bringOutYourDead' run queue item.
*
* @param item - The bringOutYourDead item to deliver.
* @returns The crank outcome.
*/
async function _KernelRouter_deliverBringOutYourDead(item) {
const { vatId } = item;
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ deliver ${vatId} bringOutYourDead`);
const vat = __classPrivateFieldGet(this, _KernelRouter_getVat, "f").call(this, vatId);
const crankResults = await vat.deliverBringOutYourDead();
__classPrivateFieldGet(this, _KernelRouter_logger, "f")?.log(`@@@@ done ${vatId} bringOutYourDead`);
return crankResults;
};
//# sourceMappingURL=KernelRouter.mjs.map