Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasm2c example #864

Open
guest271314 opened this issue Dec 20, 2024 · 2 comments
Open

wasm2c example #864

guest271314 opened this issue Dec 20, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@guest271314
Copy link
Contributor

guest271314 commented Dec 20, 2024

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.

@guest271314 guest271314 added the enhancement New feature or request label Dec 20, 2024
@jeffcharles
Copy link
Collaborator

We're open to an example being contributed but we aren't going to create one for wasm2c.

@guest271314
Copy link
Contributor Author

No idea where to start. You are the expert in this domain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants