0

I wrote the below file:

const std = @import("std");

pub export fn add(a: i32, b: i32) i32 {
    std.debug.print("add({}, {}) = {}\n", .{ a, b, a + b });
    return a + b;
}

pub fn main() void {
    std.debug.print("Hello there from Zig!\n", .{});
    const x = add(2, 2);
    std.debug.print("The returned result is: {}\n", .{x});
}

And compiled it as:

zig build-exe -O ReleaseSmall -target wasm32-wasi main.zig

Tried to execute it using wasmedge I got the below error:

PS C:\Users\hasan> wasmedge --reactor main.wasm add 2 2
[2023-07-14 16:04:54.494] [error] wasmedge runtime failed: wasm function not found, Code: 0x05
[2023-07-14 16:04:54.495] [error]     When executing function name: "add"

I tried another approach through nodejs, but looks the same, only the main AKA _start() function is only exported, I wrote:

import { readFile } from 'node:fs/promises';
import { WASI } from 'wasi';
import { argv, env } from 'node:process';

const wasi = new WASI({
  version: 'preview1',
  args: argv,
  env,
  preopens: {
    '/sandbox': './',
  },
});

const wasm = await WebAssembly.compile(
  await readFile(new URL('./main.wasm', import.meta.url)),
);

const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject());

wasi.start(instance);
console.log(instance.exports);

const result = instance.exports.add(3, 4);
console.log(result); // 7

and got:

Hello there from Zig!
add(2, 2) = 4
The returned result is: 4
[Object: null prototype] {
  memory: Memory [WebAssembly.Memory] {},
  _start: [Function: 2]
}
file:///C:/Users/hasan/wa-gpt/callwasm.js:23
const result = instance.exports.add(3, 4);
                                ^

TypeError: instance.exports.add is not a function
    at file:///C:/Users/hasan/wa-gpt/callwasm.js:23:33

Node.js v20.4.0
Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203

1 Answers1

0

Thanks to the comments recieved, now my function is:

// main.zig
const std = @import("std");
//const wasm = @import("std").os.wasm;
extern fn print(a: i32) void;

export fn add(a: i32, b: i32) i32 {
    print(a + b); // calling the JavaScript function
    return a + b;
}

pub fn main() void {
    std.debug.print("Hello there from Zig!\n", .{});
    const x = add(2, 2);
    std.debug.print("The returned result is: {}\n", .{x});
}

And I compiled it to wasm as:

zig build-lib -O ReleaseSmall -target wasm32-freestanding main.zig -dynamic

And was able to call it from nodjs as:

//index.js
import fs from "fs"
const source = fs.readFileSync("main.wasm")
const tyoedArray = new Uint8Array(source)

const importObject = {
    env: {
        print: function(x) { console.log(x); } // map the zig function print to the JS function console.log(x)
    },
  };
  
const resuult = await WebAssembly.instantiate(tyoedArray, importObject);
const fn = resuult.instance.exports;
console.log(fn);
console.log(`a + b = ${fn.add(2,3)}`);

And was able to call it from the browser as:

//index.html
<!doctype html>
<html>
  <head>
    <title>WASM Demo</title>
  </head>
  <body>
    <h3>WASM Demo</h3>
  </body>
  <script src="main.js"></script>
</html>

And

//main.js
request = new XMLHttpRequest();
request.open('GET', 'main.wasm');
request.responseType = 'arraybuffer';
request.send();

const importObject = {
    env: {
        print: function(x) { console.log(x); } // map the zig function print to the JS function console.log(x)
    },
  };

request.onload = async function() {
  var bytes = request.response;
  const tyoedArray = new Uint8Array(bytes)
  const resuult = await WebAssembly.instantiate(tyoedArray, importObject);
  const fn = resuult.instance.exports;
  console.log(fn);
  console.log(`a + b = ${fn.add(2,3)}`);
};

In this case build-lib and freestanding the main function is not required as is not not seen by the JS, and if tried to define it as export will get a compilation error.

If the main function is required, then it should be compiled as build-exe and wasi as below:

zig build-exe -O ReleaseSmall -target wasm32-wasi main.zig

And this can be called from nodejs as below:

//index.js
import { readFile } from 'node:fs/promises';
import { WASI } from 'wasi';
import { argv, env } from 'node:process';

const wasi = new WASI({
  version: 'preview1',
  args: argv,
  env: env,
  preopens: {
    '/sandbox': './',
  },
});

const wasm = await WebAssembly.compile(
  await readFile(new URL('./main.wasm', import.meta.url)),
);

const importObject = {
  ...wasi.getImportObject(),
  env: {
    print: function(x) { console.log(x); }, // map the zig function print to the JS function console.log(x)
  },
};

const instance = await WebAssembly.instantiate(wasm, importObject);

wasi.start(instance);
const fn = instance.exports;
console.log(fn);
console.log(`a + b = ${fn.add(2,3)}`);
Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203