A brief introduction to the future: Web Assembly

Gustavo Sequeira
August 24, 2017

What's Web Assembly?

Web Assembly, one of the most important new technologies that has come to the web environment in maybe a decade, maybe since the JIT's. You can read more about JIT's (Just in time compilers) in this article here. Just like the JIT's, Web Assembly comes with the guarantee to make some serious improvements on web apps.

In a nutshell, Web Assembly lets you execute code on the browser other than your javascript code. Or, in Mozilla's words: "an emerging standard whose goal is to define a safe, portable, size and load time efficient binary compiler target which offers near-native performance, a virtual CPU for the web."

Okay, Web Assembly lets me run my (rust/c/c++) code on the browser, that's cool. But, how it does it work? In order to answer that, we have to know a little more about machine code, compilers design, and everything surrounding it. So, Let's dig in.

How does Web Assembly work?

When a program that was written in a high-level language gets compiled, it has to go through a series of transformations all the way down to assembler code that is going to be interpreted by the machine.

Since each machine architecture supports its own assembly dialect, for each high-level language we would need to create a specific transformation for each specific assembly dialect. Let's say we have n languages and m dialects, we would need n \* m ways of transformation.

As this isn't at all optimum, the compiler has an intermediate representation of the source code of a program, on whichever is the high-level language that we use, the IR it's going to be the same for all of them. This saves us having to deal with all the other n \* m transformations, and leaves us with just one. The compiler has what is called a frontend, which is responsible for transforming the source code into the IR , and a backend for transforming the IR into a specific dialect which the machine speaks.

Web Assembly Compilation
Web Assembly Compilation

We could say that Web Assembly is one of those dialects, but this would not be entirely accurate because each of these dialects are unique and determined by the architecture of the machine (a physical machine).

But when we send code to a user's machine over the internet we don&'t know anything about his or her machine or architecture. For this reason, Web Assembly is somewhat different from the others assembly dialects, it is a virtual dialect that is going to be executed on a virtual CPU.

What sets Web Assembly apart from other assembly dialects is that it's a virtual dialect that is executed on a virtual CPU.

The browser downloads this Web Assembly dialect, and then the code has a shortcut to the specific dialect from the machine, without any kind of compiling time, unlike the javascript code and the JIT's.

Web Assembly Compilation WA
Web Assembly Compilation WA

Currently, WebAssembly is in the minimal viable product or MVP phase, (you can read more about this phase here. It is supported by the four major browsers( Chrome, Firefox, Safari and Opera), and aims to substitute the asm.js which is what is being/has been? used to port code from languages like c/c++ to the web.

The compiler that has the most WebAssembly support is LLVM, and there are a lot of frontends and backends that can be plugged into it as well; it is alsoa battle tested compiler tool chain.

The LLVM compiler setup takes time but you can find it here.

For the next example we are going to use the WebAssebly fiddle, you can try it here. It will allow us to download the WebAssembly modules produced by the c programs we write.

Currently WebAssembly is unable to talk with any web api's, and the browser is unable to download web assembly modules. This will be implemented in the future, however in the meantime, we'll use javascript to load the web assembly modules.

Fibonacci

Just to warm up, let's make fibonacci on C. To achieve this, we'll need python (to make a simple serve) WebAssembly fiddle.

Let's get started by making the fib.c file, which is a simple and recursive implementation of fibonacci.

int fib (int number) {
  if (number <= 1) return 1;
  return fib(number - 1) + fib(number - 2);
}

Put this code on the Web Assembly fiddle and hit the Build button. You will get the text representation of our Web Assembly module in s-expression syntax.

Fibonacci WA
Fibonacci WA

For now we're not going to get into this Web Assembly representation and its format, but you can check it out right here if you want.

Download the Web Assembly file (not the wast file 😅) and put it next to your fib.c file.

Second, let's make the javascript that acts like glue code for loading our C program.

We are going to use the Web Assembly API for javascript to compile and instantiate the Web Assembly module.

You can check this API here.

Lets implement an auxiliar javascript function to fetch the WebAssembly file and instantiate it, we can do so like this:

(async (global) =&gt;  {

        async function fetchAndInstantiateWasm (url) {
          let binary = await fetch(url)
          let bytes = await binary.arrayBuffer()
          let module = await WebAssembly.compile(bytes)
          let instance = await WebAssembly.instantiate(module)
          return instance
        }

        let module = await fetchAndInstantiateWasm("./fib.wasm")
        global.fib = module.exports.fib;

})(window);

Looking at the first line of fetchAndInstantiateWasm , we fetch the WebAssembly file, this is the binary representation of the module. We save this binary on an array buffer in the second line of the function, this is just an array with binary numbers like [1111010, 10001111, 10000101, 00001111, …, 10001111], and on the following lines of the function we return the compiled and instantiated WebAssembly module.

Then we call the fetchAndInstantiateWasm and save it in the module variable, and then we put it on the global scope.

A module instance always has your compiled functions (in our case from C ) under the exports key. The shape of our module in this case will be like so:

{
  memory: {…}
  exports: {
          fib: function () { [native code]}
         }
} 

The memory key contains information about the memory that the WebAssembly has in runTime and this memory is shared with javascript.

Now we need to deliver this on the browser, in order to do this we create a fib.html and load the js module that will load the web assembly.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <script type="text/javascript" src="./fib.js"></script>
  </body>
</html>

We save this file near the other ones. The structure of our little web assembly project should look like this:

Web assembly project
Web assembly project

And, finally, we serve all these files with python, run python -m SimpleHTTPServer 8080 or, if you have python 3, python -m http.server 8080.

Now open the browser and go to localhost:8080, enter the fib.html, open the console and… ta da! you should have a global fib native function.

Browser console output
Browser console output

A nice repo to continue further readings about Web Assembly is this, it has a lot of information about it and pretty updated.

"A brief introduction to the future: Web Assembly" by Gustavo Sequeira is licensed under CC BY SA. Source code examples are licensed under MIT.

Photo by Luca Bravo.

Categorized under javascript / webassembly.

Book a free consultation.

We'd love to hear about your team's challenges and help you improve your agility.