Skip to content

wasm2c example #864

Open
Open
@guest271314

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?

See WebAssembly/wabt#2521.

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions