Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News WebAssembly Reference Types Implemented in wasmtime, Lets Wasm Modules Handle Complex Types

WebAssembly Reference Types Implemented in wasmtime, Lets Wasm Modules Handle Complex Types

This item in japanese

Nick Fitzgerald recently announced the implementation of the WebAssembly reference types proposal in wasmtime. With reference types, a WebAssembly runtime can handle references to complex host objects (e.g. DOM nodes, or file handles) instead of being limited to integer and floating-point values. Reference types pave the way for more WebAssembly features – interface types, type imports, garbage collection, module linking, and more.

The first version of WebAssembly (now a web standard) shipped with the minimum viable features (MVP) for usage in a browser context. In this version, WebAssembly only understands numbers expressed as integer and float types (numtype ::= i32 | i64 | f32 | f64).

Glue code thus has to be authored which transforms the complex types of the host language into those base types. This may be done by the compiler through user-provided annotations (e.g. #[wasm_bindgen] with Rust). This glue code must be created for each host environment (e.g. Rust, or C++) that the module is expected to run in. This may entail significant efforts for the Wasm module’s author. On the other hand, the Wasm module’s public API would expose a non-intuitive interface that solely consists of the base types. This also creates pain points for the module’s users.

The WebAssembly interface types proposal seeks to remove the previous pain points for both Wasm module’s users and authors by letting Wasm modules use complex types to interact with their host environment and other modules. With interface types, Wasm modules would provide a mapping of the types they use to an intermediate representation (abstract types, as opposed to the current concrete base types). Wasm runtimes would use those abstract types to generate the code targeting a given host environment.

The WebAssembly reference types proposal addresses a simpler use case of interoperation with the host environment. In her didactic presentation of interface types, Mozilla’s Lin Clark described such a use case:

You want a JavaScript function to pass a string to a WebAssembly function, and then have WebAssembly pass it to another JavaScript function. Here’s what needs to happen for that to work:

  1. The first JavaScript function passes the string to the JS glue code
  2. The JS glue code turns that string object into numbers and then puts those numbers into linear memory
  3. Then passes a number (a pointer to the start of the string) to WebAssembly
  4. The WebAssembly function passes that number over to the JS glue code on the other side
  5. The second JavaScript function pulls all of those numbers out of linear memory and then decodes them back into a string object
  6. Which it gives to the second JS function

So the JS glue code on one side is just reversing the work it did on the other side. That’s a lot of work to recreate what’s basically the same object.

With reference types, host references to complex objects (e.g. strings in Clark’s example) can be passed as an externref opaque type to a WebAssembly module, which can then pass it identically back to a host function — without having to go through the complex process described previously.

Fitzgerald provided the following example of a Wasm module handling a reference to an open file:

;; hello.wat

  ;; Import the write syscall from a hypothetical (and
  ;; simplified) future version of WASI.
  ;; It takes a host reference to an open file, an address
  ;; in memory, and byte length and then writes
  ;; `memory[address..(address+length)]` to the file.
  (import "future-wasi" "write"
    (func $write (param externref i32 i32) (result i32)))

  ;; Define a memory that is one page in size (64KiB).
  (memory (export "memory") 1)

  ;; At offset 0x42 in the memory, define our data string.
  (data (i32.const 0x42) "Hello, Reference Types!\n")

  ;; Define a function that writes our hello string to a
  ;; given open file handle.
  (func (export "hello") (param externref)
    (call $write
      ;; The open file handle we were given.
      (local.get 0)
      ;; The address of our string in memory.
      (i32.const 0x42)
      ;; The length of our string in memory.
      (i32.const 24))
    ;; Ignore the return code.

The aforedescribed Wasm module exports a hello function that calls a host-provided write function with a host-provided file handle. The first parameter of the hello function is described as param externref, and retrieved with local.get 0. The Wasm module imports the $write function, describing the first parameter of the function (the file handle) as an externref. The $write function is then called with the externref host reference and two parameters (address of the string to write in the Wasm module memory, and length of the string). Because the externref is both provided and consumed as is by the host environment, no complex manipulation is necessary by the Wasm module.

Fitzgerald further provided examples of using the previous Wasm module without glue code in Python, Rust, or Web environments.

wasmtime is a standalone runtime for WebAssembly that executes Wasm code after transpiling it (JIT-style) into machine code. Wasmtime can be used from a variety of host languages (e.g. Rust, C, Python, .NET, or Go). The wasmtime guide provides a starting point to learn about wasmtime.

Rate this Article