A Taste Of WASM

The Browser as a Virtual Machine

@m4d_z
alwaysdata

Remember

  • 2001: Prototype, Scriptaculo·us, Mootools
  • 2005: jQuery
  • 2010: SPA / Backbone
  • 2012: Angular
  • 2015: virtual-dom, React, Vue.js
  • 2018: PWA
  • 2019: How is your RAM today? Good? 🤮

We are developping apps!

We need:

  • Secured Environment
  • Faster Execution
  • Shared Domains Logics

Back to Basics: It’s not that easy

WASM Binary Format

0200 20 00
0202 42 00
0204 51
0205 04 7e
0207 42 01
020A 05
020B 20 00
020D 20 00
0210 42 01
0211 7d
0212 10 00
0214 7e
0217 0b

Is this a… language?

WASM Text Format

get_local 0
i64.const 0
i64.eq
if i64
    i64.const 1
else
    get_local 0
    get_local 0
    i64.const 1
    i64.sub
    call 0
    i64.mul
end
So you want...?

We need to
compile for the Web

WASM Rust Hello World, Web version

pub fn greet(name: &str) {
    alert(&format!("Hello {}!", name));
}
module.greet("World")

🍀 static types

  • 2 Integers
  • 2 Floats

Problem:
I need more than numbers!

I need Strings
💩 UTF-8

A number could be…
a memory pointer!

Linear Memory

const pointer = 0

const LM = new WebAssembly.Memory({ initial: 1 })
const sharedMem = new Uint8Array(LM.buffer)

const str = "Hello World"

[].forEach.call(
  btoa(str),
  (char, idx) => sharedMem[pointer + idx] = char.charCodeAt(0)
)
// Uint8Array(65536) [ 83, 71, 86, 115, 98, 71, 56, 103, 86, 50, … ]

We need
High-level tools

Instanciate: Fetch!

fetch('my-module.wasm')
  .then(res => res.arrayBuffer())
  .then(instance => WebAssembly.instantiate(instance))
  .then(module => {
    // module.instance.exports[...]
    module.instance.exports.greet('Folks')
  })

Execution timeline

  1. Fetch
  2. Load / Compile (×2)
  3. Instantiate
  4. Access fn

We need
a packer

We do need

  • Static types ready to compile
  • Good-memory management
  • Bindings to JS
  • Packing toolchain

We do not need

  • a new VM
Is it secured enough?

Security Model

  • Sandboxed
  • Deterministic Execution w/ Limited Exceptions
  • Control-flow Integrity
  • Protected Call-stack in Linear Memory
  • Embedding Security Policies

Hands-on!

Let’s focus
on Rust

Remember?

pub fn greet(name: &str) {
    alert(&format!("Hello {}!", name));
}

Why Rust?

  • C++, thanks, but I only have one brain
  • No data-race by design
  • Static types
  • Iterators
  • Excellent Memory Management
  • Community 😍
  • Toolchain (Rustup, Cargo, Rustc…)
  • And much more…

wasm-bindgen!

Facilitating high-level interactions between wasm modules and JavaScript.

  • High-level library
  • Exposes:
    • Web sys (DOM, window, document, fetch, canvas…)
    • JS sys (passing values, types, boxing…)
    • Promises
  • Generate TypeScript bindings
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen(module = "/bindings.js")]
extern "C" {
    fn updateState(state: &JsValue);
}

pub fn greet(name: &str) {
    alert(&format!("Hello {}!", name));

    let state = State { name }
    updateState(JsValue::from_serde(&state).unwrap());
}

wasm-pack!

📦✨ your favorite rust -> wasm workflow tool!

Easy to install

$ curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

Easy to use!

$ cd my-module
$ wasm-pack build --target web --release

Ready to publish!

$ tree
├── bridge.js
├── Cargo.toml
├── pkg
│   ├── my-module_bg.d.ts
│   ├── my-module_bg.wasm
│   ├── my-module.d.ts
│   ├── my-module.js
│   ├── package.json
│   └── snippets
│       └── my-module-91b01578bc1ce6c0
│           └── bridge.js
└── src
    └── lib.rs

A State of WASM

interface Record {
  id: Sha256,
  payload: object
}

interface Node {
  id: Sha256,
  parent: Sha256,
  nonce: number,
  records: Array<Record>
}

let blockchain: Array<Nodes>
while (true) {
  node.nonce = nonce()
  node.id = await crypto.subtle.digest('SHA-256', encoder.encode(`
    ${node.parent};${node.nonce};
    ${node.records.map(record => Object.values(record).join(';')).join(';')}
  `))
  .then(value => tohex(value))

  if (node.id.substr(0, limit) == prefix) {
    break
  } else {
    node.records = shuffle(node.records)
  }
}
loop {
    if node.mine(&limit, &prefix) {
        break
    }
}

pub struct Node {
    ...
}
impl Node {
    fn mine(&mut self, limit: &usize, prefix: &str) -> bool {
        let mut rng = thread_rng();
        self.records.shuffle(&mut rng);
        self.generate_nonce();
        self.generate_id();
        self.id[..*limit].eq(prefix)
    }
}

Let’s Try It!

What’s under the Hood?

Fantastic Four!

Open Standard

Current Status

  • Compile
  • Fast
  • Lightweight
  • Linear Memory w/ TypedArray

Comming Soon

  • Multi-threading
  • Streaming compilation/Tiered Compiler
  • JS Modules exchange/Garbage Collector
  • Portability/Runtime/IoT
  • WASI/Interface Types
  • Binding Modules for any languages

Know The Web!

  • HTML/DOM → Interfaces
  • CSS → Layouts/Style
  • JS → UI Thread
  • WASM → Background Processes
m4dz's avatar
m4dz

Paranoïd Web Dino · Tech Evangelist

alwaysdata logo
https://www.alwaysdata.com

Questions?

Thank You!


Available under licence CC BY-SA 4.0

Illustrations

m4dz, CC BY-SA 4.0

Interleaf images

Courtesy of Unsplash and Pexels contributors

Icons

  • Layout icons are from Entypo+
  • Content icons are from FontAwesome

Fonts

  • Cover Title: Sinzano
  • Titles: Argentoratum
  • Body: Mohave
  • Code: Fira Code

Tools

Powered by Reveal.js

Source code available at
https://git.madslab.net/talks