@metamask/ocap-kernel
Version:
OCap kernel core components
274 lines • 14.1 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_routeMessage, _KernelRouter_deliverSend, _KernelRouter_deliverNotify, _KernelRouter_deliverGCAction, _KernelRouter_deliverBringOutYourDead;
Object.defineProperty(exports, "__esModule", { value: true });
exports.KernelRouter = void 0;
const KernelQueue_ts_1 = require("./KernelQueue.cjs");
const kernel_marshal_ts_1 = require("./services/kernel-marshal.cjs");
const extract_ref_ts_1 = require("./store/utils/extract-ref.cjs");
const parse_ref_ts_1 = require("./store/utils/parse-ref.cjs");
const promise_ref_ts_1 = require("./store/utils/promise-ref.cjs");
const types_ts_1 = require("./types.cjs");
const assert_ts_1 = require("./utils/assert.cjs");
const VatHandle_ts_1 = require("./VatHandle.cjs");
/**
* 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.
*/
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.
*/
constructor(kernelStore, kernelQueue, getVat) {
_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);
__classPrivateFieldSet(this, _KernelRouter_kernelStore, kernelStore, "f");
__classPrivateFieldSet(this, _KernelRouter_kernelQueue, kernelQueue, "f");
__classPrivateFieldSet(this, _KernelRouter_getVat, getVat, "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".
(0, assert_ts_1.Fail) `unsupported or unknown run queue item type ${item.type}`;
}
return undefined;
}
}
exports.KernelRouter = KernelRouter;
_KernelRouter_kernelStore = new WeakMap(), _KernelRouter_kernelQueue = new WeakMap(), _KernelRouter_getVat = new WeakMap(), _KernelRouter_instances = new WeakSet(), _KernelRouter_routeMessage = function _KernelRouter_routeMessage(item) {
const { target, message } = item;
(0, types_ts_1.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((0, kernel_marshal_ts_1.kser)('revoked object'));
}
const vatId = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").getOwner(targetObject);
if (!vatId) {
return routeAsSplat((0, kernel_marshal_ts_1.kser)('no vat'));
}
return { vatId, target: targetObject };
};
const routeAsRequeue = (targetObject) => {
return { target: targetObject };
};
if ((0, promise_ref_ts_1.isPromiseRef)(target)) {
const promise = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").getKernelPromise(target);
switch (promise.state) {
case 'fulfilled': {
if (promise.value) {
const targetObject = (0, extract_ref_ts_1.extractSingleRef)(promise.value);
if (targetObject) {
if ((0, promise_ref_ts_1.isPromiseRef)(targetObject)) {
return routeAsRequeue(targetObject);
}
return routeAsSend(targetObject);
}
}
return routeAsSplat((0, kernel_marshal_ts_1.kser)('no object'));
}
case 'rejected':
return routeAsSplat(promise.value);
case 'unresolved':
return routeAsRequeue(target);
default:
throw (0, assert_ts_1.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');
}
console.log(`@@@@ message went splat ${item.target}<-${JSON.stringify(item.message)}`);
return crankResults;
}
const { vatId, target } = route;
const { message } = item;
console.log(`@@@@ deliver ${vatId} send ${target}<-${JSON.stringify(message)}`);
if (vatId) {
const vat = __classPrivateFieldGet(this, _KernelRouter_getVat, "f").call(this, vatId);
if (vat) {
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');
}
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);
__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 {
(0, assert_ts_1.Fail) `no owner for kernel object ${target}`;
}
}
else {
__classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").enqueuePromiseMessage(target, message);
}
console.log(`@@@@ done ${vatId} send ${target}<-${JSON.stringify(message)}`);
return crankResults;
}, _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;
(0, types_ts_1.insistVatId)(vatId);
const { context, isPromise } = (0, parse_ref_ts_1.parseRef)(kpid);
(0, assert_ts_1.assert)(context === 'kernel' && isPromise, `${kpid} is not a kernel promise`);
console.log(`@@@@ deliver ${vatId} notify ${vatId} ${kpid}`);
const promise = __classPrivateFieldGet(this, _KernelRouter_kernelStore, "f").getKernelPromise(kpid);
const { state, value } = promise;
(0, assert_ts_1.assert)(value, `no value for promise ${kpid}`);
if (state === 'unresolved') {
(0, assert_ts_1.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') {
(0, assert_ts_1.Fail) `target promise ${toResolve} is unresolved`;
}
if (!tPromise.value) {
throw (0, assert_ts_1.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');
console.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;
console.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);
console.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;
console.log(`@@@@ deliver ${vatId} bringOutYourDead`);
const vat = __classPrivateFieldGet(this, _KernelRouter_getVat, "f").call(this, vatId);
const crankResults = await vat.deliverBringOutYourDead();
console.log(`@@@@ done ${vatId} bringOutYourDead`);
return crankResults;
};
//# sourceMappingURL=KernelRouter.cjs.map