Description
What is the idea?
Work with wabt's wasm2c
to provide a minimal, complete, example of JavaScript => WebAssembly => C => native executable.
What problem does it solve?
...
JavaScript <=> WASM <=> C <=> native executable.
I've been experimenting with passing code generated by Javy to wabt's wasm2c
. There is a basic hello example of creating a main function to compile the .h
and .c
files generated by wasm2c
here https://github.com/WebAssembly/wabt/tree/main/wasm2c#using-the-generated-module. However, there's no complete example of creating a main function and compiling the C produced by wasm2c
for Javy code.
Javy source code
function main() {
const stdin = 0;
const stdout = 1;
const stderr = 2;
const decoder = new TextDecoder;
const encoder = new TextEncoder;
let offset = 0;
const message = new Uint8Array(65536);
// https://stackoverflow.com/a/34238979
function array_nth_permutation(a, n) {
let lex = n;
let b = [];
for (let x = 0;x < a.length; x++) {
b[x] = a[x];
}
let len = a.length;
const res = [];
let i = 1;
let f = 1;
for (;i <= len; i++) {
f *= i;
}
let fac = f;
if (n >= 0 && n < f) {
for (;len > 0; len--) {
f /= len;
i = (n - n % f) / f;
res.push(b.splice(i, 1)[0]);
n %= f;
}
return `${lex} of ${fac - 1} (0-indexed, factorial ${fac}) => ${JSON.stringify(res)}
`;
} else {
if (n === 0) {
return `${JSON.stringify(res)}
`;
}
return `${n} >= 0 && ${n} < ${f}: ${n >= 0 && n < f}
`;
}
}
while (true) {
const buffer = new Uint8Array(1);
const bytesRead = Javy.IO.readSync(stdin, buffer);
message.set(buffer, offset);
offset += bytesRead;
if (bytesRead === 0) {
break;
}
}
const data = decoder.decode(message.subarray(0, offset));
const [input, lex] = data.split(" ").map(Math.floor);
if (input < 2 || lex < 0) {
Javy.IO.writeSync(stderr, encoder.encode(`Expected n > 2, m >= 0, got ${input}, ${lex}
`));
return 1;
} else {
Javy.IO.writeSync(stdout, encoder.encode(array_nth_permutation([...new Array(input).keys()].map(Number), lex)));
return 0;
}
}
main();
Building with javy
javy build -C dynamic -C plugin=plugin.wasm -o nm_javy_permutations.wasm nm_javy_test.js
How this can be executed in JavaScript world
echo '9 362879' | deno -A run-wasi.js -
362879 of 362879 (0-indexed, factorial 362880) => [8,7,6,5,4,3,2,1,0]
import { readFile } from "node:fs/promises";
import process from "node:process";
import { WASI } from "./wasi-minimal.js";
import * as fs from "node:fs";
try {
const [embeddedModule, pluginModule] = await Promise.all([
compileModule("./nm_javy_permutations.wasm"),
compileModule("./plugin.wasm"),
]);
const result = await runJavy(embeddedModule, pluginModule);
} catch (e) {
process.stdout.write(e.message, "utf8");
} finally {
process.exit();
}
async function compileModule(wasmPath) {
const bytes = await readFile(new URL(wasmPath, import.meta.url));
return WebAssembly.compile(bytes);
}
async function runJavy(embeddedModule, pluginModule) {
try {
let wasi = new WASI({
env: {},
args: [],
fds: [
{
type: 2,
handle: fs
},
{
type: 2,
handle: fs
},
{
type: 2,
handle: fs
}
]
});
const pluginInstance = await WebAssembly.instantiate(
pluginModule,
{ "wasi_snapshot_preview1": wasi.exports },
);
const instance = await WebAssembly.instantiate(embeddedModule,
{ "javy_quickjs_provider_v3": pluginInstance.exports },
);
wasi.memory = pluginInstance.exports.memory;
instance.exports._start();
return;
} catch (e) {
if (e instanceof WebAssembly.RuntimeError) {
if (e) {
throw new Error(e);
}
}
throw e;
}
}
wasm2c
wabt/bin/wasm2c plugin.wasm -o plugin.c
That write around a 16 MB plugin.c
and 3.2 KB plugin.h
.
How to create a main.c
and main
function for that generated C to be compiled to a standalone executable by gcc
or clang
, just like the fac
example in wabt's repository?