run-emscripten.mjs is a Node.js script to quickly test the Wasm-compiled
QEMU. This runs directly from the terminal using the node command so the
HTTP server or the browser isn't needed.

Node.js 24 or newer is needed to run wasm64 binaries. Also note that QEMU's
"quit" monitor command doesn't work as of now because of the Emscripten's
atexit issue [1]. Send SIGINT or SIGTERM to the node process to exit QEMU.

[1] https://github.com/emscripten-core/emscripten/issues/26040

Signed-off-by: Kohei Tokunaga <[email protected]>
---
 scripts/run-emscripten.mjs | 66 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 scripts/run-emscripten.mjs

diff --git a/scripts/run-emscripten.mjs b/scripts/run-emscripten.mjs
new file mode 100644
index 0000000000..330e31cfe3
--- /dev/null
+++ b/scripts/run-emscripten.mjs
@@ -0,0 +1,66 @@
+#!/usr/bin/env node
+
+import path from 'node:path';
+import { parseArgs } from "node:util";
+
+function help() {
+  console.log(`Usage: run-emscripten.mjs [--preload FILE] [script.js] -- 
[arguments]
+
+Options:
+  --preload FILE: Load the package created by Emscripten's file_packager.py`);
+}
+
+const { values, positionals } = parseArgs({
+  allowPositionals: true,
+  options: {
+    'preload': { type: 'string' },
+    'help': { type: 'boolean' },
+  },
+})
+if (values.help) {
+  help();
+  process.exit(0);
+}
+const preload = values.preload;
+const moduleFile = positionals[0];
+const moduleArgs = positionals.slice(1);
+if ((!moduleFile) || (!moduleFile.match(/.*\.m?js$/))) {
+  console.error("module JS file must be specified as the first argument");
+  help();
+  process.exit(1);
+}
+
+const targetModule = await import(path.resolve(moduleFile));
+let preloadModule;
+if (preload) preloadModule = await import(path.resolve(preload));
+
+var Module = {
+  preRun: [],
+  arguments: moduleArgs,
+  mainScriptUrlOrBlob: path.resolve(moduleFile),
+};
+Module["preRun"].push((Module) => {
+  const decoder = new TextDecoder('utf-8');
+  Module.TTY.default_tty_ops.put_char = (tty, val) => {
+    process.stdout.write(decoder.decode(new Uint8Array([val])));
+  }
+  Module.TTY.default_tty1_ops.put_char = (tty, val) => {
+    process.stderr.write(decoder.decode(new Uint8Array([val])));
+  }
+});
+if (preloadModule) preloadModule.default(Module);
+
+if (process.stdin.isTTY) {
+  process.stdin.setRawMode(true);
+}
+function restoreTTY() {
+  if (process.stdin.isTTY) {
+    process.stdin.setRawMode(false);
+  }
+  process.exit(0);
+}
+process.on('exit', restoreTTY);
+process.on('SIGINT', restoreTTY);
+process.on('SIGTERM', restoreTTY);
+
+await targetModule.default(Module);
-- 
2.43.0


Reply via email to