WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.
WebAssembly is neither Web nor Assembly. It is (memory) safe, fast, efficient, and sandboxed. Something we want from container technologies!
1. Portability
2. Security
POSIX provides source code portability. You can compile the same source code with different versions of libc to target different machines. Still, this is not good enough, we need portable binaries.
Wasm is sandboxed:
Just as WebAssembly is an assembly language for a conceptual machine, it needs a system interface for a conceptual OS - the WebAssembly System Interface (WASI)!
Rust, at 26%, is the most frequently used language, followed by C++ then AssemblyScript. (survey responses are available on demand as a CSV file)
Chart from https://blog.scottlogic.com
AssemblyScript is a new TypeScript-like language that has been designed specifically for WebAssembly.
Chart from https://blog.scottlogic.com
Chart from https://blog.scottlogic.com
Chart from https://blog.scottlogic.com
WebAssembly is expected to have a significant impact across quite a wide range of different application areas, and 56% seeing Serverless as a high impact application.
Chart from https://blog.scottlogic.com
More details at https://github.com/WebAssembly/proposals.
WASI is the WebAssembly System Interface, a modular system interface for Wasm - files, network connections, clocks, and random numbers (controlled by host for sandboxing).
There are two things that distinguish WASI modules from “ordinary” Wasm:
For example, when a WASI module calls __wasi_fd_write() there needs to be a host to pass that imported function so it is able to actually do something.
const go = new Go(); // Defined in wasm_exec.js. Don't forget to add this in your index.html.
const runWasm = async () => {
// Get the importObject from the go instance.
const importObject = go.importObject;
// Set our function we want to use from WebAssembly, on our importObject
// This should be set on the env key, of the import object.
// The function itself should be set on the env object, where the key is
// depending on the compiler, usually in the format similar to below,
// but you can check for the "LinkError: WebAssembly.instantiate()",
// which would show the name required.
importObject.env["./main.go.jsPrintInt"] = value => console.log(value);
// Instantiate our wasm module
const wasmModule = await wasmBrowserInstantiate("./main.wasm", importObject);
// Allow the wasm_exec go instance, bootstrap and execute our wasm module
go.run(wasmModule.instance);
// Call the export from wasm
wasmModule.instance.exports.printIntFromWasm(24);
};
runWasm();
WAGI allows developers to run WebAssembly WASI binaries as HTTP handlers. It's basically the old idea of CGI HTTP handlers applied to WASI, as a way of building a server implementation on top of WASI.
This is very useful to be able to go around some key architectural limitations in WASI, particularly its lack of a networking layer and its underlying single-threaded nature.
It is NOT considered production-grade by its developers, neither is it "supported" software.
Problem: Wasm lacks support for complex types (only numbers are supported). if a function takes or returns anything besides numbers, things get complicated.
The problem to solve here is translating values between different types when a module is talking to another module (or directly to a host, like the browser).
There are four places where a translation is needed.
For exported functions:
For imported functions:
With interface types, the two sides aren’t trying to share a representation. Instead, the default is to copy values or pointers between one side and the other.
Protobuffer is designed so that you can interact with a system that you don’t share memory with -- either because it’s running in a different process or because it’s on a totally different machine across the network.
This means that you have to be able to send the thing in the middle -- the "intermediate representation" of the objects -- across that boundary.
With interface types, this "IR" never needs to leave the engine. The modules only see the what the engine spits out for them at the end of the process -- what's been copied to their linear memory or given to them as a reference. There is no schema/layout that needs to be specified.