128 lines
3.3 KiB
TypeScript
128 lines
3.3 KiB
TypeScript
import { safeRun } from "../util";
|
|
import { ConsoleLogger } from "./custom_logger";
|
|
import { ControllerMessage, WorkerMessage } from "./worker";
|
|
|
|
let loadedFunctions = new Map<string, Function>();
|
|
let pendingRequests = new Map<
|
|
number,
|
|
{
|
|
resolve: (result: unknown) => void;
|
|
reject: (e: any) => void;
|
|
}
|
|
>();
|
|
|
|
function workerPostMessage(msg: ControllerMessage) {
|
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
window.parent.postMessage(msg, "*");
|
|
} else {
|
|
self.postMessage(msg);
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
function syscall(name: string, ...args: any[]): Promise<any>;
|
|
// function require(moduleName: string): any;
|
|
}
|
|
|
|
let syscallReqId = 0;
|
|
|
|
self.syscall = async (name: string, ...args: any[]) => {
|
|
return await new Promise((resolve, reject) => {
|
|
syscallReqId++;
|
|
pendingRequests.set(syscallReqId, { resolve, reject });
|
|
workerPostMessage({
|
|
type: "syscall",
|
|
id: syscallReqId,
|
|
name,
|
|
args,
|
|
});
|
|
});
|
|
};
|
|
|
|
let loadedModules = new Map<string, any>();
|
|
|
|
// @ts-ignore
|
|
self.require = (moduleName: string): any => {
|
|
// console.log("Loading", moduleName, loadedModules.get(moduleName));
|
|
return loadedModules.get(moduleName);
|
|
};
|
|
|
|
// @ts-ignore
|
|
self.console = new ConsoleLogger((level, message) => {
|
|
workerPostMessage({ type: "log", level, message });
|
|
}, false);
|
|
|
|
function wrapScript(code: string) {
|
|
return `return (${code})["default"]`;
|
|
}
|
|
|
|
self.addEventListener("message", (event: { data: WorkerMessage }) => {
|
|
safeRun(async () => {
|
|
let data = event.data;
|
|
switch (data.type) {
|
|
case "load":
|
|
let fn2 = new Function(wrapScript(data.code!));
|
|
loadedFunctions.set(data.name!, fn2());
|
|
workerPostMessage({
|
|
type: "inited",
|
|
name: data.name,
|
|
});
|
|
break;
|
|
case "load-dependency":
|
|
// console.log("Received dep", data.name);
|
|
let fn3 = new Function(`return ${data.code!}`);
|
|
let v = fn3();
|
|
loadedModules.set(data.name!, v);
|
|
// console.log("Dep val", v);
|
|
workerPostMessage({
|
|
type: "dependency-inited",
|
|
name: data.name,
|
|
});
|
|
break;
|
|
case "invoke":
|
|
let fn = loadedFunctions.get(data.name!);
|
|
if (!fn) {
|
|
throw new Error(`Function not loaded: ${data.name}`);
|
|
}
|
|
try {
|
|
let result = await Promise.resolve(fn(...(data.args || [])));
|
|
workerPostMessage({
|
|
type: "result",
|
|
id: data.id,
|
|
result: result,
|
|
} as ControllerMessage);
|
|
} catch (e: any) {
|
|
workerPostMessage({
|
|
type: "result",
|
|
id: data.id,
|
|
error: e.message,
|
|
stack: e.stack,
|
|
});
|
|
// console.error("Error invoking function", data.name, e.message);
|
|
// throw e;
|
|
}
|
|
|
|
break;
|
|
case "syscall-response":
|
|
let syscallId = data.id!;
|
|
const lookup = pendingRequests.get(syscallId);
|
|
if (!lookup) {
|
|
console.log(
|
|
"Current outstanding requests",
|
|
pendingRequests,
|
|
"looking up",
|
|
syscallId
|
|
);
|
|
throw Error("Invalid request id");
|
|
}
|
|
pendingRequests.delete(syscallId);
|
|
if (data.error) {
|
|
lookup.reject(new Error(data.error));
|
|
} else {
|
|
lookup.resolve(data.result);
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
});
|