diff --git a/utils/jsffi/dyld.mjs b/utils/jsffi/dyld.mjs index e84928c4031479cb0c80df1c5b3372ddf4136346..e068f54239a2642db7f838ecf2558a6116665ad2 100755 --- a/utils/jsffi/dyld.mjs +++ b/utils/jsffi/dyld.mjs @@ -370,8 +370,11 @@ export class DyLDRPC { #origin; #wsPipe; #wsSig; + #redirectWasiConsole; + #wsStdout; + #wsStderr; - constructor({ origin }) { + constructor({ origin, redirectWasiConsole }) { this.#origin = origin; const ws_url = this.#origin.replace("http://", "ws://"); @@ -399,8 +402,17 @@ export class DyLDRPC { this.#wsSig = new WebSocket(ws_url, "sig"); this.#wsSig.binaryType = "arraybuffer"; + this.#redirectWasiConsole = redirectWasiConsole; + if (redirectWasiConsole) { + this.#wsStdout = new WebSocket(ws_url, "stdout"); + this.#wsStderr = new WebSocket(ws_url, "stderr"); + } + this.opened = Promise.all( - [this.#wsPipe, this.#wsSig].map( + (redirectWasiConsole + ? [this.#wsPipe, this.#wsSig, this.#wsStdout, this.#wsStderr] + : [this.#wsPipe, this.#wsSig] + ).map( (ws) => new Promise((res, rej) => { ws.addEventListener("open", res); @@ -413,6 +425,10 @@ export class DyLDRPC { close() { this.#wsPipe.close(); this.#wsSig.close(); + if (this.#redirectWasiConsole) { + this.#wsStdout.close(); + this.#wsStderr.close(); + } } async #rpc(endpoint, ...args) { @@ -444,6 +460,22 @@ export class DyLDRPC { async fetchWasm(p) { return fetch(`${this.#origin}/fs${p}`); } + + stdout(msg) { + if (this.#redirectWasiConsole) { + this.#wsStdout.send(msg); + } else { + console.info(msg); + } + } + + stderr(msg) { + if (this.#redirectWasiConsole) { + this.#wsStderr.send(msg); + } else { + console.warn(msg); + } + } } // Actual implementation of endpoints used by DyLDRPC @@ -452,7 +484,15 @@ class DyLDRPCServer { #server; #wss; - constructor({ host, port, dyldPath, libdir, ghciSoPath, args }) { + constructor({ + host, + port, + dyldPath, + libdir, + ghciSoPath, + args, + redirectWasiConsole, + }) { this.#server = http.createServer(async (req, res) => { const origin = originFromServerAddress(await this.listening); @@ -487,7 +527,7 @@ class DyLDRPCServer { ` import { DyLDRPC, main } from "./fs${dyldPath}"; const args = ${JSON.stringify({ libdir, ghciSoPath, args })}; -args.rpc = new DyLDRPC({origin: "${origin}"}); +args.rpc = new DyLDRPC({origin: "${origin}", redirectWasiConsole: ${redirectWasiConsole}}); args.rpc.opened.then(() => main(args)); ` ); @@ -578,6 +618,16 @@ args.rpc.opened.then(() => main(args)); return; } + if (ws.protocol === "stdout") { + ws.addEventListener("message", (ev) => console.info(ev.data)); + return; + } + + if (ws.protocol === "stderr") { + ws.addEventListener("message", (ev) => console.warn(ev.data)); + return; + } + throw new Error(`unknown protocol ${ws.protocol}`); }); @@ -682,12 +732,8 @@ class DyLD { new wasi.OpenFile( new wasi.File(new Uint8Array(), { readonly: true }) ), - wasi.ConsoleStdout.lineBuffered((msg) => - console.log(`[WASI stdout] ${msg}`) - ), - wasi.ConsoleStdout.lineBuffered((msg) => - console.warn(`[WASI stderr] ${msg}`) - ), + wasi.ConsoleStdout.lineBuffered((msg) => this.#rpc.stdout(msg)), + wasi.ConsoleStdout.lineBuffered((msg) => this.#rpc.stderr(msg)), ], { debug: false } ); @@ -1126,6 +1172,11 @@ export async function main({ rpc, libdir, ghciSoPath, args }) { libdir, ghciSoPath, args, + redirectWasiConsole: + process.env.GHCI_BROWSER_PUPPETEER_LAUNCH_OPTS || + process.env.GHCI_BROWSER_PLAYWRIGHT_BROWSER_TYPE + ? false + : Boolean(process.env.GHCI_BROWSER_REDIRECT_WASI_CONSOLE), }); const origin = originFromServerAddress(await server.listening);