My first take on WebAssembly
Tags: javascript, cpp, webassembly, beginners
Published: 2023-09-04 03:31:03

Here we go, I decided to try WebAssembly having a project in mind, but before dedicating myself and take on coding, there is always fun of fooling around with some code.

If you are new to WebAssembly, the first place to go after Wikipedia is probably [MDN Web Docs](https://developer.mozilla.org/en-US/docs/WebAssembly) that gives a decent introductory background. It explains that WebAssembly is binary format and the corresponding assembly language that runs in a Virtual Machine built into all modern browsers. It sounds like a blasphemy if you are an acolyte of [Unix Philosophy](https://en.wikipedia.org/wiki/Unix_philosophy), but otherwise it is a cool client-side portable technology to deal with high-performance numeric computations (no, no, I am not talking about crypto mining).

So where should we start?

# First comes a compiler

In order to use WebAssembly with our shiny cool numeric C/C++ libraries (you are not going to manually write assembly, aren't you?), we need a compiler that takes C/C++ source as an input and generates a `*.wasm` binary file along with some JavaScript glue code, which is needed to call binary code from a web-page or Node.js. Well, MDN recommends to use [Emscipten](https://emscripten.org/), which wraps around LLVM `clang` compiler, and I am not in a position to make a reasonable argument about other options...

I will skip the details of how to install Emscripten SDK. [It is pretty easy](https://emscripten.org/docs/getting_started/downloads.html). Just note that the most reasonable and sane way to use `emcc` compiler is, probably, from `bash`. But I decided to give it a try from PowerShell first without additional abstractions like WSL, and it worked... Nice surprise.

# Let's compile something

Well, let me skip a [hello world](https://developer.mozilla.org/en-US/docs/WebAssembly/C_to_Wasm) example, and start with some basic questions one needs to figure out before starting a project.

* How can I interface my C/C++ code with JavaScript? At minimum, I need to call some functions. It would be nice to have a full support of classes, threads, etc -- but maybe later, okay?

* How can I pass anything from JavaScript to C/C++. Passing a couple of `double`s would be nice, but I also need to pass `Float64` arrays and aggregate data like `struct`.

* How would my C/C++ library functions return anything meaningful rather than a single numeric value?

Let's move one step after another and take time to look around...

## Calling C-functions using `ccall()`

Let us prepare a very basic "numeric library".

```
// ccall_testing.cpp

#include <cmath>
#include <iostream>

#include <emscripten/emscripten.h>

extern "C" {

EMSCRIPTEN_KEEPALIVE double my_norm(double x, double y) {
    std::cout << "my_norm is called\n";
    return std::sqrt(x * x + y * y);
}

}
```
And yes, `ccall()` is to call C-functions, so here is `extern "C"` to deal with C++ [name mangling](https://en.wikipedia.org/wiki/Name_mangling). We also need `EMSCRIPTEN_KEEPALIVE` to prevent this code to be optimized out as [dead code](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html).

Now, don't forget to source all the environmental variables from your shell of choice. There is a collection of shell scripts for doing it in your Emscripten SDK source directory. In PowerShell, I just run `./emcmdprompt.bat`.

Then compile our source file using `emcc`:
```
$emcc ccall_testing.cpp -o ccall_testing.js -sEXPORTED_RUNTIME_METHODS=ccall
```
We have two files. The first is WebAssembly binary `ccall_testing.wasm` and the second is JavaScript glue code `ccall_testing.js`. It is possible to use `html` as the compiler's target, and it will generate a web-page, but I will stick to `*.js` to remain in control.

It is time to call our library function from a web page. Let's come up with something very basic:

```
<!DOCTYPE html>
<html lang="en">
<head>
    <title>My WASM experiments</title>
</head>
<body>

<button id="mybutton">Run</button>

<script>
    document.getElementById("mybutton").addEventListener("click", ()=>{
        const result = Module.ccall("my_norm", // yes, that's our function
		                    "number",				// return type
		                   ["number", "number"], 	// argument types
		                   [3.0, 4.0]			// yeah, Pythagorean triple
		               );
					   
	    console.log(`result = ${result}`);
    });
</script>

<script src="ccall_testing.js" type="text/javascript" async></script>

</body>
</html>
```
Save this as `ccall_testing.html` and then run in the browther. Does not work? There is additional step. In order to load `*.wasm` blob, we can serve the page from a simple http-server:

```
$python -m http.server
```
which will open a connection at `localhost:8000`. Now just open `html`-file in the directory of your choice:

```
http://localhost:8000/wasm_testing/ccall_testing.html
```
Press `Run`, open a console `Ctrl-Shift-I` and _Voila!_ (don't forget `Ctrl+R` to reload cache):
```
my_norm is called   ccall_testing.js:1559:16
result = 5
```

## Prefer wraps instead of calls?

No problem! It is possible to wrap C-function directly into JavaScript function using `cwrap()`. 

In this case, we don't need `EMSCRIPTEN_KEEPALIVE` any more because we will tell the compiler to export this function anyway so our `cpp`-file becomes a bit cleaner
```
// cwrap_testing.cpp

#include <cmath>
#include <iostream>

#include <emscripten/emscripten.h>

extern "C" {

double my_norm(double x, double y) {
    std::cout << "my_norm is called\n";
    return std::sqrt(x * x + y * y);
}

}
```
Call the compiler and add `-sEXPORTED_FUNCTIONS=_my_norm` with an underscore in front of the function name (that's an assembly symbol, like `_start`)
```
$emcc cwrap_testing.cpp -o cwrap_testing.js -sEXPORTED_FUNCTIONS=_my_norm -sEXPORTED_RUNTIME_METHODS=cwrap
```
Our `html` file also becomes slightly different. Here, I introduce a new JavaScript function `myNorm` that wraps around the C-library function call

```
<button id="mybutton">Run</button>

<script>
	document.getElementById("mybutton").addEventListener("click", ()=>{
		const myNorm = Module.cwrap("my_norm", // no underscore
									'number',  // return type 
									['number', 'number']); // param types;
		const result = myNorm(3.0, 4.0);
		console.log(`result = ${result}`);
	});
</script>

<script src="cwrap_testing.js" type="text/javascript" async></script>
```

The result is the same as for `ccall()`. That's nice. But how can we pass something more meaningful? 

## But I need to pass an array of `Float64`...

The naive question would be: can we use `'array'` instead of `'number'` in the function call? So our code would look like: `Module.ccall("my_norm", "number", "array", [3.0, 4.0])`?

The answer is "not so simple"... The `array` type here really means something like "raw byte buffer".

According to the [API documentation](https://emscripten.org/docs/api_reference/preamble.js.html#preamble-js): " 'array' [is] for JavaScript arrays and typed arrays, containing 8-bit integer data - that is, the data is written into a C array of 8-bit integers". 

So if we need to pass an array of `double`s, it is either a manual allocation and passing a pointer (as a `'number'`) or [Embind](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html).

Yeah, Embind is cool! Let's come back to that...