1
0

Refactoring

This commit is contained in:
Zef Hemel 2022-03-24 10:48:56 +01:00
parent 09d07d587f
commit b51730d33f
8 changed files with 34 additions and 34 deletions

View File

@ -10,6 +10,7 @@ class IFrameWrapper implements WorkerLike {
private iframe: HTMLIFrameElement; private iframe: HTMLIFrameElement;
onMessage?: (message: any) => Promise<void>; onMessage?: (message: any) => Promise<void>;
ready: Promise<void>; ready: Promise<void>;
private messageListener: (evt: any) => void;
constructor() { constructor() {
const iframe = document.createElement("iframe", {}); const iframe = document.createElement("iframe", {});
@ -18,7 +19,7 @@ class IFrameWrapper implements WorkerLike {
// Let's lock this down significantly // Let's lock this down significantly
iframe.setAttribute("sandbox", "allow-scripts"); iframe.setAttribute("sandbox", "allow-scripts");
iframe.srcdoc = sandboxHtml; iframe.srcdoc = sandboxHtml;
window.addEventListener("message", (evt: any) => { this.messageListener = (evt: any) => {
if (evt.source !== iframe.contentWindow) { if (evt.source !== iframe.contentWindow) {
return; return;
} }
@ -27,7 +28,8 @@ class IFrameWrapper implements WorkerLike {
safeRun(async () => { safeRun(async () => {
await this.onMessage!(data); await this.onMessage!(data);
}); });
}); };
window.addEventListener("message", this.messageListener);
document.body.appendChild(iframe); document.body.appendChild(iframe);
this.ready = new Promise((resolve) => { this.ready = new Promise((resolve) => {
iframe.onload = () => { iframe.onload = () => {
@ -42,6 +44,7 @@ class IFrameWrapper implements WorkerLike {
} }
terminate() { terminate() {
window.removeEventListener("message", this.messageListener);
return this.iframe.remove(); return this.iframe.remove();
} }
} }

View File

@ -10,16 +10,19 @@ let pendingRequests = new Map<
} }
>(); >();
let syscallReqId = 0;
let vm = new VM({ let vm = new VM({
sandbox: { sandbox: {
console: console, console: console,
self: { self: {
syscall: (reqId: number, name: string, args: any[]) => { syscall: (name: string, ...args: any[]) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
pendingRequests.set(reqId, { resolve, reject }); syscallReqId++;
pendingRequests.set(syscallReqId, { resolve, reject });
parentPort.postMessage({ parentPort.postMessage({
type: "syscall", type: "syscall",
id: reqId, id: syscallReqId,
name, name,
// TODO: Figure out why this is necessary (to avoide a CloneError) // TODO: Figure out why this is necessary (to avoide a CloneError)
args: JSON.parse(JSON.stringify(args)), args: JSON.parse(JSON.stringify(args)),

View File

@ -9,24 +9,26 @@ let pendingRequests = new Map<
reject: (e: any) => void; reject: (e: any) => void;
} }
>(); >();
declare global {
function syscall(id: number, name: string, args: any[]): Promise<any>;
}
let postMessage = self.postMessage.bind(self); let postMessage = self.postMessage.bind(self);
if (window.parent !== window) { if (window.parent !== window) {
postMessage = window.parent.postMessage.bind(window.parent); postMessage = window.parent.postMessage.bind(window.parent);
} }
self.syscall = async (id: number, name: string, args: any[]) => { declare global {
function syscall(name: string, ...args: any[]): Promise<any>;
}
let syscallReqId = 0;
self.syscall = async (name: string, ...args: any[]) => {
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
pendingRequests.set(id, { resolve, reject }); syscallReqId++;
pendingRequests.set(syscallReqId, { resolve, reject });
postMessage( postMessage(
{ {
type: "syscall", type: "syscall",
id, id: syscallReqId,
name, name,
args, args,
}, },

View File

@ -19,6 +19,7 @@ export interface WorkerLike {
} }
export type WorkerMessageType = "load" | "invoke" | "syscall-response"; export type WorkerMessageType = "load" | "invoke" | "syscall-response";
export type WorkerMessage = { export type WorkerMessage = {
type: WorkerMessageType; type: WorkerMessageType;
id?: number; id?: number;

View File

@ -29,7 +29,7 @@ test("Run a Node sandbox", async () => {
code: `(() => { code: `(() => {
return { return {
default: async (a, b) => { default: async (a, b) => {
return await self.syscall(1, "addNumbers", [a, b]); return await self.syscall("addNumbers", a, b);
} }
}; };
})()`, })()`,
@ -47,7 +47,7 @@ test("Run a Node sandbox", async () => {
code: `(() => { code: `(() => {
return { return {
default: async () => { default: async () => {
await self.syscall(2, "failingSyscall", []); await self.syscall("failingSyscall");
} }
}; };
})()`, })()`,

View File

@ -3,11 +3,12 @@ import { EventEmitter } from "../common/event";
import { Sandbox } from "./sandbox"; import { Sandbox } from "./sandbox";
import { Plug } from "./plug"; import { Plug } from "./plug";
interface SysCallMapping { export interface SysCallMapping {
[key: string]: (...args: any) => Promise<any> | any; [key: string]: (...args: any) => Promise<any> | any;
} }
export type SystemJSON<HookT> = { [key: string]: Manifest<HookT> }; export type SystemJSON<HookT> = { [key: string]: Manifest<HookT> };
export type SystemEvents<HookT> = { export type SystemEvents<HookT> = {
plugLoaded: (name: string, plug: Plug<HookT>) => void; plugLoaded: (name: string, plug: Plug<HookT>) => void;
plugUnloaded: (name: string, plug: Plug<HookT>) => void; plugUnloaded: (name: string, plug: Plug<HookT>) => void;
@ -32,13 +33,13 @@ export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
registerSyscalls(...registrationObjects: SysCallMapping[]) { registerSyscalls(...registrationObjects: SysCallMapping[]) {
for (const registrationObject of registrationObjects) { for (const registrationObject of registrationObjects) {
for (let p in registrationObject) { for (let [name, def] of Object.entries(registrationObject)) {
this.registeredSyscalls[p] = registrationObject[p]; this.registeredSyscalls[name] = def;
} }
} }
} }
async syscall(name: string, args: Array<any>): Promise<any> { async syscall(name: string, args: any[]): Promise<any> {
const callback = this.registeredSyscalls[name]; const callback = this.registeredSyscalls[name];
if (!name) { if (!name) {
throw Error(`Unregistered syscall ${name}`); throw Error(`Unregistered syscall ${name}`);

View File

@ -1,15 +1,5 @@
declare global { declare global {
function syscall(id: number, name: string, args: any[]): Promise<any>; function syscall(name: string, ...args: any[]): Promise<any>;
var reqId: number;
} }
// This needs to be global, because this will be shared with all other functions in the same environment (worker-like) export const syscall = self.syscall;
if (typeof self.reqId === "undefined") {
self.reqId = 0;
}
export async function syscall(name: string, ...args: any[]): Promise<any> {
self.reqId++;
// console.log("Syscall", name, reqId);
return await self.syscall(self.reqId, name, args);
}

View File

@ -54,9 +54,9 @@ export class PageApi implements ApiProvider {
broadcastCursors(page: Page) { broadcastCursors(page: Page) {
page.clientStates.forEach((client) => { page.clientStates.forEach((client) => {
client.socket.emit( client.socket.emit(
"cursorSnapshot", "cursorSnapshot",
page.name, page.name,
Object.fromEntries(page.cursors.entries()) Object.fromEntries(page.cursors.entries())
); );
}); });
} }