Search code examples
javascriptnode.jswebassemblyemscripten

Get rid of generated JavaScript and execute WebAssembly directly


I have plans to create a library for NodeJS that uses native code, and this time I want to experiment with WebAssembly. Currently, I can compile C code to WebAssembly, but Emscripten generates a .wasm and a .js file. I understand that there is a runtime involved behind the scenes, but I would like to know if there isn't a way to load the WebAssembly (as listed below) and call the function.

My main.c

#include <stdio.h>
#include <emscripten/emscripten.h>

EMSCRIPTEN_KEEPALIVE
extern void hello() {
  printf("Hello world!\n");
}

My index.js

var a = require("./a.out.js");

a().then((instance) => {
  instance.ccall("hello"); // Prints "Hello World!"
});

Compile

emcc -s NO_EXIT_RUNTIME=1 -sMODULARIZE -s "EXPORTED_RUNTIME_METHODS=['ccall']" main.c

Run

node index.js 

The above works. The code below no

const fs = require("node:fs");

const wasmBuffer = fs.readFileSync("a.out.wasm");
WebAssembly.instantiate(wasmBuffer).then((wasmModule) => {
  const { hello } = wasmModule.instance.exports;
  hello();
});

Error

node index.js
node:internal/process/promises:288
            triggerUncaughtException(err, true /* fromPromise */);
            ^

[TypeError: WebAssembly.instantiate(): Imports argument must be present and must be an object]

Node.js v18.18.2

Solution

  • If you are targeting Node.js (as opposed to browser JavaScript), I strongly recommend you to go the native Node-API route. It is much faster, has much better support and it is a very mature environment. WASM is still a slightly experimental technology with many rough edges. If you do not intend to run your project in a browser, you can only lose with WASM.

    emscripten relies on a special loader because when it was created, WASM did not have a specified environment. This means that there is lots of glue code between JavaScript and WASM that emscripten does its own way.

    Today there is WASI which will be the future standard, but it is still in its infancy. It will solve those kinds of problems by specifying a standard environment and ABI for WASI software.

    You can't load the WASM directly unless you are willing to do everything the emscripten loader does manually - which I guarantee you that it is not easy - especially if you want good cross-platform compatibility.