WASM and Rust
By Casey Allred
@sbditto85
Who am i?
- Wrote ~20k LOC targeting WASM in Rust
- UVU MCS project ~12k LOC currently at https://programwith.us
- Work for LiveView Technology on the Web Development team
- Have a wife, three beautiful little girls, and love playing Minecraft
Agenda
- What is WASM?
- WAT
- WASM Bindgen
- Rust frontend frameworks
What is WASM?
“WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.”
- WebAssembly Working Group (https://www.w3.org/wasm/)
WAT - WebAssembly Text format
- S-Expressions
- Types
- Functions
- Local/Global
- Imports/Exports
- Memory
- Table
WAT - S-Expressions
(module)
“Lisp like” expressions where everything is surrounded in ()
Most simple WASM module is:
which is a module that has nothing and does nothing.
WAT - Types
WebAssembly has the following value types:
- i32: 32-bit integer
- i64: 64-bit integer
- f32: 32-bit floating point
- f64: 64-bit floating point
The value types i32 and i64 are not inherently signed or unsigned. The interpretation of these types is determined by individual operators.
WAT - Types
- Each parameter and local variable has exactly one value type.
- Function signatures consist of a sequence of zero or more parameter types and a sequence of zero or more return types
- In the MVP, a function can have at most one return type.
WAT - Functions
- The signature declares what the function takes (parameters) and returns (return values).
- The locals are like vars in JavaScript, but with explicit types declared.
- The body is just a linear list of low-level instructions.
( func <signature> <locals> <body> )
WAT - Functions
(func $add (param $lhs i32) (param $rhs i32) (result i32)
get_local $lhs
get_local $rhs
i32.add
)
(func $add (param i32) (param i32) (result i32)
get_local 0
get_local 1
i32.add
)
WAT - Local/Global
(module
(global $glob (mut i32) (i32.const 100))
(func $add (param $lhs i32) (param $rhs i32) (result i32) (local $loc i32)
i32.const 10
set_local $loc
get_local $lhs
get_local $rhs
i32.add
get_local $loc
i32.add
get_global $glob
i32.add
)
(export "add" (func $add))
)
WAT - Imports/Exports
(module
(global $glob (import "js" "global") i32)
(import "console" "log" (func $log (param i32)))
(func (export "add") (param $lhs i32) (param $rhs i32) (result i32)
i32.const 100
call $log
get_local $lhs
get_local $rhs
i32.add
)
)
var importObject = {
console: {
log: function(arg) {
console.log(arg);
}
}
};
WebAssembly.instantiateStreaming(fetch('logger.wasm'), importObject)
.then(obj => {
console.log(obj.instance.exports.add(1,1));
});
WAT - Memory
- Memory is just a large array of bytes that can grow over time.
- WebAssembly contains instructions for reading and writing from linear memory.
- i32.load
- i32.store
- ...
WAT - Memory
(module
(import "console" "log" (func $log (param i32 i32)))
(import "js" "mem" (memory 1))
(data (i32.const 0) "Hi")
(func (export "writeHi")
i32.const 0 ;; pass offset 0 to log
i32.const 2 ;; pass length 2 to log
call $log))
function consoleLogString(offset, length) {
var bytes = new Uint8Array(memory.buffer, offset, length);
var string = new TextDecoder('utf8').decode(bytes);
console.log(string);
}
var memory = new WebAssembly.Memory({initial:1});
var importObject = { console: { log: consoleLogString }, js: { mem: memory } };
WebAssembly.instantiateStreaming(fetch('logger2.wasm'), importObject)
.then(obj => {
obj.instance.exports.writeHi();
});
WAT - Table
(module
(table 2 funcref)
(func $f1 (result i32)
i32.const 42)
(func $f2 (result i32)
i32.const 13)
(elem (i32.const 0) $f1 $f2)
(type $return_i32 (func (result i32)))
(func (export "callByIndex") (param $i i32) (result i32)
local.get $i
call_indirect (type $return_i32))
)
WebAssembly.instantiateStreaming(fetch('wasm-table.wasm'))
.then(obj => {
// returns 42
console.log(obj.instance.exports.callByIndex(0));
// returns 13
console.log(obj.instance.exports.callByIndex(1));
// returns an error, because there is no index position 2 in the table
console.log(obj.instance.exports.callByIndex(2));
});
WASM-Bindgen
Let's get rust to do all that for us!
Demo - webassembly.studio
Useful Resources
- Programming WebAssembly with Rust
- https://pragprog.com/book/khrust/programming-webassembly-with-rust
- crates.io
- web-sys
- js-sys
- wasm-bindgen
Rust Frameworks
WASM and Rust
By sbditto85
WASM and Rust
- 765