From 1cb735f2a58f82659766cbdfeead743fda7d686b Mon Sep 17 00:00:00 2001 From: Cheng Shao <terrorjack@type.dance> Date: Sun, 16 Mar 2025 22:14:20 +0000 Subject: [PATCH] wasm: isolate nodejs-specific logic with the isNode flag in dyld As we move towards supporting running the dyld script in the browser, this commit implements the isNode module-level binding which is true if dyld is running in nodejs. The nodejs-specific bits are gated under isNode. For the browser case, this commit introduces @bjorn3/browser_wasi_shim as the wasi implementation; we already use it in quite a few projects and it simply works. (cherry picked from commit 7003a39901d180d13723e1cec5f2972068fe5e26) (cherry picked from commit 9156318fab8723c6bae562d2b431fe3d533078c9) --- utils/jsffi/dyld.mjs | 63 +++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/utils/jsffi/dyld.mjs b/utils/jsffi/dyld.mjs index 3820d78a555..caa699ee1cb 100755 --- a/utils/jsffi/dyld.mjs +++ b/utils/jsffi/dyld.mjs @@ -87,10 +87,6 @@ // -opti GHC flag to pass process arguments, like RTS flags or -opti-v // to dump the iserv messages. -import fs from "node:fs"; -import path from "node:path"; -import stream from "node:stream"; -import { WASI } from "node:wasi"; import { JSValManager, setImmediate } from "./prelude.mjs"; import { parseRecord, parseSections } from "./post-link.mjs"; @@ -265,6 +261,28 @@ async function parseDyLink0(reader) { return r; } +// Browser/node portable code stays above this watermark. +const isNode = Boolean(globalThis?.process?.versions?.node); + +// Too cumbersome to only import at use sites. Too troublesome to +// factor out browser-only/node-only logic into different modules. For +// now, just make these global let bindings optionally initialized if +// isNode and be careful to not use them in browser-only logic. +let fs, path, require, stream, wasi; + +if (isNode) { + require = (await import("node:module")).createRequire(import.meta.url); + + fs = require("fs"); + path = require("path"); + stream = require("stream"); + wasi = require("wasi"); +} else { + wasi = await import( + "https://cdn.jsdelivr.net/npm/@bjorn3/browser_wasi_shim@0.4.1/dist/index.js" + ); +} + // The real stuff class DyLD { // Wasm page size. @@ -342,12 +360,31 @@ class DyLD { #regs = {}; constructor({ args }) { - this.#wasi = new WASI({ - version: "preview1", - args, - env: { PATH: "", PWD: process.cwd() }, - preopens: { "/": "/" }, - }); + if (isNode) { + this.#wasi = new wasi.WASI({ + version: "preview1", + args, + env: { PATH: "", PWD: process.cwd() }, + preopens: { "/": "/" }, + }); + } else { + this.#wasi = new wasi.WASI( + args, + [], + [ + 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}`) + ), + ], + { debug: false } + ); + } // Keep this in sync with rts/wasm/Wasm.S! for (let i = 1; i <= 10; ++i) { @@ -740,11 +777,7 @@ class DyLD { } } -function isMain() { - return import.meta.filename === process.argv[1]; -} - -if (isMain()) { +if (isNode) { // sysroot libdir that contains libc.so etc const libdir = process.argv[2], ghci_so_path = process.argv[3]; -- GitLab