@metamask/ocap-kernel
Version:
OCap kernel core components
201 lines • 12.2 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 _VatSyscall_instances, _VatSyscall_kernelQueue, _VatSyscall_kernelStore, _VatSyscall_logger, _VatSyscall_handleSyscallSend, _VatSyscall_handleSyscallResolve, _VatSyscall_handleSyscallSubscribe, _VatSyscall_handleSyscallDropImports, _VatSyscall_handleSyscallRetireImports, _VatSyscall_handleSyscallExportCleanup, _VatSyscall_recordVatFatalSyscall;
import { Logger } from "@metamask/logger";
import { makeError } from "./services/kernel-marshal.mjs";
import { parseRef } from "./store/utils/parse-ref.mjs";
import { coerceMessage } from "./types.mjs";
/**
* A VatSyscall is a class that handles syscalls from a vat.
*
* This class is responsible for handling syscalls from a vat, including
* sending messages, resolving promises, and dropping imports.
*/
export class VatSyscall {
/**
* Construct a new VatSyscall instance.
*
* @param props - The properties for the VatSyscall.
* @param props.vatId - The ID of the vat.
* @param props.kernelQueue - The kernel's run queue.
* @param props.kernelStore - The kernel's store.
* @param props.logger - The logger for the VatSyscall.
*/
constructor({ vatId, kernelQueue, kernelStore, logger }) {
_VatSyscall_instances.add(this);
/** The kernel's run queue */
_VatSyscall_kernelQueue.set(this, void 0);
/** The kernel's store */
_VatSyscall_kernelStore.set(this, void 0);
/** Logger for outputting messages (such as errors) to the console */
_VatSyscall_logger.set(this, void 0);
this.vatId = vatId;
__classPrivateFieldSet(this, _VatSyscall_kernelQueue, kernelQueue, "f");
__classPrivateFieldSet(this, _VatSyscall_kernelStore, kernelStore, "f");
__classPrivateFieldSet(this, _VatSyscall_logger, logger, "f");
}
/**
* Handle a syscall from the vat.
*
* @param vso - The syscall that was received.
* @returns The result of the syscall.
*/
handleSyscall(vso) {
try {
this.illegalSyscall = undefined;
this.vatRequestedTermination = undefined;
// This is a safety check - this case should never happen
if (!__classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").isVatActive(this.vatId)) {
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_recordVatFatalSyscall).call(this, 'vat not found');
return harden(['error', 'vat not found']);
}
const kso = __classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").translateSyscallVtoK(this.vatId, vso);
const [op] = kso;
const { vatId } = this;
switch (op) {
case 'send': {
// [KRef, Message];
const [, target, message] = kso;
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.log(`@@@@ ${vatId} syscall send ${target}<-${JSON.stringify(message)}`);
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_handleSyscallSend).call(this, target, coerceMessage(message));
break;
}
case 'subscribe': {
// [KRef];
const [, promise] = kso;
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.log(`@@@@ ${vatId} syscall subscribe ${promise}`);
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_handleSyscallSubscribe).call(this, promise);
break;
}
case 'resolve': {
// [VatOneResolution[]];
const [, resolutions] = kso;
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.log(`@@@@ ${vatId} syscall resolve ${JSON.stringify(resolutions)}`);
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_handleSyscallResolve).call(this, resolutions);
break;
}
case 'exit': {
// [boolean, SwingSetCapData];
const [, isFailure, info] = kso;
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.log(`@@@@ ${vatId} syscall exit fail=${isFailure} ${JSON.stringify(info)}`);
this.vatRequestedTermination = { reject: isFailure, info };
break;
}
case 'dropImports': {
// [KRef[]];
const [, refs] = kso;
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.log(`@@@@ ${vatId} syscall dropImports ${JSON.stringify(refs)}`);
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_handleSyscallDropImports).call(this, refs);
break;
}
case 'retireImports': {
// [KRef[]];
const [, refs] = kso;
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.log(`@@@@ ${vatId} syscall retireImports ${JSON.stringify(refs)}`);
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_handleSyscallRetireImports).call(this, refs);
break;
}
case 'retireExports': {
// [KRef[]];
const [, refs] = kso;
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.log(`@@@@ ${vatId} syscall retireExports ${JSON.stringify(refs)}`);
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_handleSyscallExportCleanup).call(this, refs, true);
break;
}
case 'abandonExports': {
// [KRef[]];
const [, refs] = kso;
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.log(`@@@@ ${vatId} syscall abandonExports ${JSON.stringify(refs)}`);
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_handleSyscallExportCleanup).call(this, refs, false);
break;
}
case 'callNow':
case 'vatstoreGet':
case 'vatstoreGetNextKey':
case 'vatstoreSet':
case 'vatstoreDelete': {
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.warn(`vat ${vatId} issued invalid syscall ${op} `, vso);
break;
}
default:
// Compile-time exhaustiveness check
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.warn(`vat ${vatId} issued unknown syscall ${op} `, vso);
break;
}
return harden(['ok', null]);
}
catch (error) {
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.error(`Fatal syscall error in vat ${this.vatId}`, error);
__classPrivateFieldGet(this, _VatSyscall_instances, "m", _VatSyscall_recordVatFatalSyscall).call(this, 'syscall translation error: prepare to die');
return harden([
'error',
error instanceof Error ? error.message : String(error),
]);
}
}
}
_VatSyscall_kernelQueue = new WeakMap(), _VatSyscall_kernelStore = new WeakMap(), _VatSyscall_logger = new WeakMap(), _VatSyscall_instances = new WeakSet(), _VatSyscall_handleSyscallSend = function _VatSyscall_handleSyscallSend(target, message) {
__classPrivateFieldGet(this, _VatSyscall_kernelQueue, "f").enqueueSend(target, message);
}, _VatSyscall_handleSyscallResolve = function _VatSyscall_handleSyscallResolve(resolutions) {
__classPrivateFieldGet(this, _VatSyscall_kernelQueue, "f").resolvePromises(this.vatId, resolutions);
}, _VatSyscall_handleSyscallSubscribe = function _VatSyscall_handleSyscallSubscribe(kpid) {
const kp = __classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").getKernelPromise(kpid);
if (kp.state === 'unresolved') {
__classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").addPromiseSubscriber(this.vatId, kpid);
}
else {
__classPrivateFieldGet(this, _VatSyscall_kernelQueue, "f").enqueueNotify(this.vatId, kpid);
}
}, _VatSyscall_handleSyscallDropImports = function _VatSyscall_handleSyscallDropImports(krefs) {
for (const kref of krefs) {
const { direction, isPromise } = parseRef(kref);
// We validate it's an import - meaning this vat received this object from somewhere else
if (direction === 'export' || isPromise) {
throw Error(`vat ${this.vatId} issued invalid syscall dropImports for ${kref}`);
}
__classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").clearReachableFlag(this.vatId, kref);
}
}, _VatSyscall_handleSyscallRetireImports = function _VatSyscall_handleSyscallRetireImports(krefs) {
for (const kref of krefs) {
const { direction, isPromise } = parseRef(kref);
// We validate it's an import - meaning this vat received this object from somewhere else
if (direction === 'export' || isPromise) {
throw Error(`vat ${this.vatId} issued invalid syscall retireImports for ${kref}`);
}
if (__classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").getReachableFlag(this.vatId, kref)) {
throw Error(`syscall.retireImports but ${kref} is still reachable`);
}
// deleting the clist entry will decrement the recognizable count, but
// not the reachable count (because it was unreachable, as we asserted)
__classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").forgetKref(this.vatId, kref);
}
}, _VatSyscall_handleSyscallExportCleanup = function _VatSyscall_handleSyscallExportCleanup(krefs, checkReachable) {
const action = checkReachable ? 'retire' : 'abandon';
for (const kref of krefs) {
const { direction, isPromise } = parseRef(kref);
// We validate it's an export - meaning this vat created/owns this object
if (direction === 'import' || isPromise) {
throw Error(`vat ${this.vatId} issued invalid syscall ${action}Exports for ${kref}`);
}
if (checkReachable) {
if (__classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").getReachableFlag(this.vatId, kref)) {
throw Error(`syscall.${action}Exports but ${kref} is still reachable`);
}
}
__classPrivateFieldGet(this, _VatSyscall_kernelStore, "f").forgetKref(this.vatId, kref);
__classPrivateFieldGet(this, _VatSyscall_logger, "f")?.debug(`${action}Exports: deleted object ${kref}`);
}
}, _VatSyscall_recordVatFatalSyscall = function _VatSyscall_recordVatFatalSyscall(error) {
this.illegalSyscall = { vatId: this.vatId, info: makeError(error) };
};
//# sourceMappingURL=VatSyscall.mjs.map