WebAssembly

What Is WebAssembly?

Mostly, WebAssembly is a specification (here) for a bytecode format (i.e. a virtual instruction set architecture) for a virtual stack machine. See here for an intro to virtual stack machines.

Just like many other programming languages (Python, Ruby, every JVM language), the idea is that a virtual machine interprets the bytecode. These days browsers include such a VM, as does NodeJS.

A WebAssembly virtual machine has some important differences from other popular language virtual machines:

  • Host Environment.
  • Security Model.

WebAssmbly vs Javascript

Interpreters

There are a couple existing Javascript intepreters:

So, there's a V8 team at Google who created their own interpreter for Javascript. They invented a bytecode format and implemented a VM (incidentally, a register machine) that executes the bytecode.

V8 scans and parses Javascript code (into an AST) and then generates bytecode. The interpreter evaluates the bytecodes using C functions. The bytecode form of the program can't be shared for execution in other browsers, since it only works in V8; instead, sharing the program means sharing the Javascript (which is fine, just slightly slower to load and execute).

A WebAssembly VM could execute a program that has been pre-compiled into WebAssembly bytecode.

Since a whole new VM is required, WebAssembly is also an opportunity to revisit every single design decision about Javascript to make it better for running in browsers.

Bytcode as a Compilation Target

Bytecode is a nicer compilation target than a high-level textual language like Javascript. While there have been many tools to transpile various languages into Javascript, it is easier to create tools that compile those languages to WebAssembly bytecode.

wat, wasm, and wast

  • wat: is the textual format (lisp)
  • wasm: the binary format
  • wast: ??
  • wasi - An API spec for talking to an OS. Much like POSIX. Needed/used only when running outside of a browser, because browsers define their own complete API for what is possible. See: wasi.dev.

Also, "wasm" is sometimes used to abbreviate "WebAssembly", as in: "the wasm text format and the wasm binary format".

Tooling

  • wabt, the WebAssembly Binary Toolkit.

    brew install wabt
    
  • Emscripten: LLVM backend for wasm
  • WasmExplorer - Website where you can programs to WebAssembly and view them.
  • WasmFiddle

Runtimes

The roadmap lists three non-browser runtimes:

  • Node.js
  • Wasmtime
  • Wasmer

Hello World

(module
 (func (export "AddInt")
       (param $value_1 i32) (param $value_2 i32)
       (result i32)
       local.get $value_1
       local.get $value_2
       i32.add
       )
 )
wat2wasm webassembly/AddInt.wat \
         -o webassembly/AddInt.wasm
// Read wasm file
const fs = require('fs');
const bytes = fs.readFileSync(dir + "/AddInt.wasm");

WebAssembly.instantiate(new Uint8Array(bytes))
  .then(obj => {
    let result = obj.instance.exports.AddInt(3, 4);
    console.log(`3 + 4 = ${result}`);
  })
#+RESULTS:
undefined3 + 4 = 7

Text/Binary Conversion

From wat (text) to wasm (binary)

Tangle this to simple.wat.

(module
 (func $i (import "imports" "imported_func") (param i32))
 (func (export "exported_func")
       i32.const 42
       call $i))

Now run wat2wasm to produce a binary file called simple.wasm.

wat2wasm simple.wat -o simple.wasm

Why is this writing to stderr?

wat2wasm simple.wat -v
0000000: 0061 736d                                 ; WASM_BINARY_MAGIC
0000004: 0100 0000                                 ; WASM_BINARY_VERSION
; section "Type" (1)
0000008: 01                                        ; section code
0000009: 00                                        ; section size (guess)
000000a: 02                                        ; num types
; func type 0
000000b: 60                                        ; func
000000c: 01                                        ; num params
000000d: 7f                                        ; i32
000000e: 00                                        ; num results
; func type 1
000000f: 60                                        ; func
0000010: 00                                        ; num params
0000011: 00                                        ; num results
0000009: 08                                        ; FIXUP section size
; section "Import" (2)
0000012: 02                                        ; section code
0000013: 00                                        ; section size (guess)
0000014: 01                                        ; num imports
; import header 0
0000015: 07                                        ; string length
0000016: 696d 706f 7274 73                        imports  ; import module name
000001d: 0d                                        ; string length
000001e: 696d 706f 7274 6564 5f66 756e 63         imported_func  ; import field name
000002b: 00                                        ; import kind
000002c: 00                                        ; import signature index
0000013: 19                                        ; FIXUP section size
; section "Function" (3)
000002d: 03                                        ; section code
000002e: 00                                        ; section size (guess)
000002f: 01                                        ; num functions
0000030: 01                                        ; function 0 signature index
000002e: 02                                        ; FIXUP section size
; section "Export" (7)
0000031: 07                                        ; section code
0000032: 00                                        ; section size (guess)
0000033: 01                                        ; num exports
0000034: 0d                                        ; string length
0000035: 6578 706f 7274 6564 5f66 756e 63         exported_func  ; export name
0000042: 00                                        ; export kind
0000043: 01                                        ; export func index
0000032: 11                                        ; FIXUP section size
; section "Code" (10)
0000044: 0a                                        ; section code
0000045: 00                                        ; section size (guess)
0000046: 01                                        ; num functions
; function body 0
0000047: 00                                        ; func body size (guess)
0000048: 00                                        ; local decl count
0000049: 41                                        ; i32.const
000004a: 2a                                        ; i32 literal
000004b: 10                                        ; call
000004c: 00                                        ; function index
000004d: 0b                                        ; end
0000047: 06                                        ; FIXUP func body size
0000045: 08                                        ; FIXUP section size

From wasm to wat

We can disassemble! Ain't that peachy!

wasm2wat simple.wasm
#+RESULTS:
(module
  (type (;0;) (func (param i32)))
  (type (;1;) (func))
  (import "imports" "imported_func" (func (;0;) (type 0)))
  (func (;1;) (type 1)
    i32.const 42
    call 0)
  (export "exported_func" (func 1)))