I compiled JavaScript to Web Assembly using Bytecode Alliance's Javy. I then compiled the `.wasm` to C using WABT's `wasm2c`. Now I am trying to compile the resulting C to a standalone executable.
```
./javy emit-plugin -o plugin.wasm
./javy build -C dynamic -C plugin=plugin.wasm -o javy-permutations.wasm permutations.js
```
The code can be run using
```
wasmtime run --preload javy_quickjs_provider_v3=plugin.wasm javy-permutations.wasm
```
Compiling WASM to C
```
./wabt/bin/wasm2c javy-permutations.wasm -n array_nth_permutation -o javy-permutations.c
```
```
./wabt/bin/wasm2c plugin.wasm -n w2c_javy__quickjs__provider__v3 -o plugin.c
```
which writes `plugin.c` and `plugin.h` to the filesystem. The Javy implementation of WebAssembly toolchain.
I also, in my attempts to replicate a working JavaScript implementation of this using Node.js
https://github.com/bytecodealliance/javy/blob/main/docs/docs-using-nodejs.md, compiled `wasi_snapshot_preview1.reactor.wasm` to C using `wasm2c`. I have not been able to reproduce what I do using JavaScript in C.
I was able to achieve the result for the factorial example in `wasm2c`
https://github.com/WebAssembly/wabt/blob/main/wasm2c/README.md#tutorial-wat---wasm---c.
There's no examples in `wasm2c` for compiling WASI to a standalone executable.
Here's one version of the working JavaScript version that I'm trying to reproduce in C
```
import { readFile } from "node:fs/promises";
import { WASI } from "node:wasi";
try {
const [embeddedModule, pluginModule] = await Promise.all([
compileModule("./javy-permutations.wasm"),
compileModule("./plugin.wasm"),
]);
const result = await runJavy(pluginModule, embeddedModule);
// console.log("Success!", JSON.stringify(result, null, 2));
} catch (e) {
console.log(e);
}
async function compileModule(wasmPath) {
const bytes = await readFile(new URL(wasmPath, import.meta.url));
return WebAssembly.compile(bytes);
}
async function runJavy(pluginModule, embeddedModule) {
/*
// Use stdin/stdout/stderr to communicate with Wasm instance
// See
https://k33g.hashnode.dev/wasi-communication-between-nodejs-and-wasm-modules-another-way-with-stdin-and-stdout */
try {
const wasi = new WASI({
version: "preview1",
args: [],
env: {},
returnOnExit: true,
});
const pluginInstance = await WebAssembly.instantiate(
pluginModule,
{ "wasi_snapshot_preview1": wasi.wasiImport },
);
const instance = await WebAssembly.instantiate(
embeddedModule,
{ "javy_quickjs_provider_v3": pluginInstance.exports },
);
console.log(pluginInstance.exports);
// Javy plugin is a WASI reactor see
https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md?plain=1 wasi.initialize(pluginInstance);
instance.exports._start();
return "Done";
} catch (e) {
console.log(e);
if (e instanceof WebAssembly.RuntimeError) {
if (errorMessage) {
throw new Error(errorMessage);
}
}
throw e;
} finally {
// console.log("Finally");
}
}
```
Here's `javy-permutations.h`
```
/* Automatically generated by wasm2c */
#ifndef JAVY_PERMUTATIONS_H_GENERATED_
#define JAVY_PERMUTATIONS_H_GENERATED_
#include "wasm-rt.h"
#include <stdint.h>
#ifndef WASM_RT_CORE_TYPES_DEFINED
#define WASM_RT_CORE_TYPES_DEFINED
typedef uint8_t u8;
typedef int8_t s8;
typedef uint16_t u16;
typedef int16_t s16;
typedef uint32_t u32;
typedef int32_t s32;
typedef uint64_t u64;
typedef int64_t s64;
typedef float f32;
typedef double f64;
#endif
#ifdef __cplusplus
extern "C" {//
#endif
struct w2c_javy__quickjs__provider__v3;
extern wasm_rt_memory_t* w2c_javy__quickjs__provider__v3_memory(struct w2c_javy__quickjs__provider__v3*);
typedef struct w2c_array__nth__permutation {
struct w2c_javy__quickjs__provider__v3* w2c_javy__quickjs__provider__v3_instance;
/* import: 'javy_quickjs_provider_v3' 'memory' */
wasm_rt_memory_t *w2c_javy__quickjs__provider__v3_memory;
bool data_segment_dropped_w2c_array__nth__permutation_d0 : 1;
} w2c_array__nth__permutation;
void wasm2c_array__nth__permutation_instantiate(w2c_array__nth__permutation*, struct w2c_javy__quickjs__provider__v3*);
void wasm2c_array__nth__permutation_free(w2c_array__nth__permutation*);
wasm_rt_func_type_t wasm2c_array__nth__permutation_get_func_type(uint32_t param_count, uint32_t result_count, ...);
/* import: 'javy_quickjs_provider_v3' 'canonical_abi_realloc' */
u32 w2c_javy__quickjs__provider__v3_canonical_abi_realloc(struct w2c_javy__quickjs__provider__v3*, u32, u32, u32, u32);
/* import: 'javy_quickjs_provider_v3' 'invoke' */
void w2c_javy__quickjs__provider__v3_invoke(struct w2c_javy__quickjs__provider__v3*, u32, u32, u32, u32);
extern const u64 wasm2c_array__nth__permutation_min_javy__quickjs__provider__v3_memory;
extern const u64 wasm2c_array__nth__permutation_max_javy__quickjs__provider__v3_memory;
extern const u8 wasm2c_array__nth__permutation_is64_javy__quickjs__provider__v3_memory;
/* export: '_start' */
void w2c_array__nth__permutation_0x5Fstart(w2c_array__nth__permutation*);
#ifdef __cplusplus
}
#endif
#endif /* JAVY_PERMUTATIONS_H_GENERATED_ */
```
Here's what I tried so far in `main.javy.c`
```
#include <stdio.h>
#include <stdlib.h>
#include "javy-permutations.h"
#include "plugin.h"
int main(int argc, char** argv) {
/* Make sure there is at least one command-line argument. */
if (argc < 2) {
printf("Invalid argument. Expected '%s NUMBER'\n", argv[0]);
return 1;
}
/* Convert the argument from a string to an int. We'll implicitly cast the int
to a `u32`, which is what `fac` expects. */
// u32 x = atoi(argv[1]);
/* Initialize the Wasm runtime. */
wasm_rt_init();
/* Declare an instance of the `fac` module. */
w2c_array__nth__permutation array__nth__permutation;
w2c_javy__quickjs__provider__v3 provider;
/* Construct the module instance. */
wasm2c_array__nth__permutation_instantiate(&array__nth__permutation, &provider);
/* Call `fac`, using the mangled name. */
// u32 result = w2c_fac_fac(&fac, x);
/* Print the result. */
// printf("fac(%u) -> %u\n", x, result);
w2c_array__nth__permutation_0x5Fstart(array__nth__permutation*)
/* Free the fac module. */
w2c_w2c__javy__0x5Fquickjs__0x5Fprovider__0x5Fv3_canonical_abi_free(w2c_w2c__javy__0x5Fquickjs__0x5Fprovider__0x5Fv3*, u32, u32, u32);
/* Free the Wasm runtime state. */
wasm_rt_free();
return 0;
}
```
which throws errors when trying to compile
```
cc -o javy-permutations main.javy.c javy-permutations.c wasm2c/wasm-rt-impl.c wasm2c/wasm-rt-mem-impl.c -Iwasm2c -lm
main.javy.c: In function ‘main’:
main.javy.c:24:3: error: unknown type name ‘w2c_javy__quickjs__provider__v3’; use ‘struct’ keyword to refer to the type
24 | w2c_javy__quickjs__provider__v3 provider;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| struct
main.javy.c:27:72: warning: passing argument 2 of ‘wasm2c_array__nth__permutation_instantiate’ from incompatible pointer type [-Wincompatible-pointer-types]
27 | ray__nth__permutation_instantiate(&array__nth__permutation, &provider);
| ^~~~~~~~~
| |
| int *
In file included from main.javy.c:4:
javy-permutations.h:37:79: note: expected ‘struct w2c_javy__quickjs__provider__v3 *’ but argument is of type ‘int *’
37 | _nth__permutation_instantiate(w2c_array__nth__permutation*, struct w2c_javy__quickjs__provider__v3*);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.javy.c:34:65: error: expected expression before ‘)’ token
34 | w2c_array__nth__permutation_0x5Fstart(array__nth__permutation*)
|
```
I suspect I need to initialize import `wasi_snapshot_preview` into the plugin in C, though I am not sure how to do that.