I am writing a webassembly demo in C to match a similar demo I wrote in zig.
I am currently able to call C functions from JS, and interact with shared memory on either side. However, I can't seem to be able to expose JS variables and functions to the C program.
Is anybody familiar with writing C this way? I wonder if the undefined symbols are getting optimized out somehow.
I'm using zig cc
(i.e. clang
) to compile to wasm on zig version 0.13.0-dev.351+64ef45eb0, using clang 16.0.0 on macos Sequoia 15.0.1 (aarch64).
This is the code I've written thus far:
#include <stdlib.h>
void print_test(void); // the function I want to use
const unsigned char heap[4096];
int __attribute__((export_name("memstart")))
memstart() {
return (int)&heap;
}
int __attribute__((export_name("return5")))
return5(int p) {
return 5 * p;
}
int __attribute__((export_name("entryAt")))
entryAt(int p) {
print_test();
return heap[p];
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>For Stephen</title>
</head>
<body>
<input id="input" type="number" placeholder="int param..."></input>
<input id="bytes" placeholder="bytes..."></input>
<pre id="output">output goes gere</pre>
<button id="button_r5">Return 5</button>
<button id="button_nth">Get nth byte</button>
<canvas id="my_canvas"></canvas>
<script>
const c = {};
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const importObject = {
"env": {
"print_test": () => console.log("test print")
}
};
WebAssembly.instantiateStreaming(fetch("resource.wasm", importObject))
.then(result => {
const {memory, memstart, return5, entryAt} = result.instance.exports;
console.log(memstart);
c.buffer = new Uint8Array(memory.buffer, memstart());
c.return5 = return5;
c.entryAt = entryAt;
});
const button_r5 = document.getElementById("button_r5");
const button_nth = document.getElementById("button_nth");
const input = document.getElementById("input");
const bytes = document.getElementById("bytes");
const output = document.getElementById("output");
button_r5.addEventListener("click", ()=>{
output.textContent = c.return5(input.value);
});
button_nth.addEventListener("click", ()=>{
c.buffer.set(encoder.encode(bytes.value));
output.textContent = c.entryAt(input.value);
});
</script>
</body>
</html>
zig cc -target wasm32-freestanding -g resource.c -lc -Wl,--no-entry -o resource.wasm
You can use some attributes to import functions from JavaScript, that will depends on the compiler.
Using Emscripten you can set EM_IMPORT(NAME)
. That is equivalent of __attribute__((import_module("env"), import_name(#NAME)))
(source-code).
Considering that Zig also uses LLVM, you can also use the same attribute:
__attribute__((import_module("env"), import_name("print_test"))) void print_test();
However, if it doesn't work (maybe it's not using Zig compiler), you might have some other flavours:
void print_test() __attribute__((
__import_module__("env"),
__import_name__("print_test")
));
__attribute__((__import_name__("print_test"))) void print_test();
That are based on other languages/compilers, such as SwiftWasm and .NET-NativeLLVM (all based on LLVM).