Chapter 4. Upcoming Features and Proposals

The initial release of WebAssembly in 2017 was intended to be a Minimum Viable Product (MVP). The team that worked on this project wanted to get something released quickly in order to gain traction with the community. As a result, a number of features that the team was interested in didn’t make it into the MVP “cut.”

After the initial release, the specification and process that governs WebAssembly was moved into the World Wide Web Consortium (W3C) standards organization, with future WebAssembly development adopting a similar approach to JavaScript and other web standards. There isn’t going to be a WebAssembly v2.0 with an eye-catching and tantalizing list of features; instead, new features, in the form of specifications, are developed by both community groups and vendors. These features are eventually implemented by browsers after they become sufficiently mature.

The WebAssembly standardization process adopts a phased approach, comprising phases from 1 (immature) up to 5 (standardized), with browsers typically starting to implement features in phase 3 and above. These features differ significantly in their scope and goal, and as a result they will progress through these phases at quite different rates.

This chapter does not provide an exhaustive analysis of all the currently proposed features, a number of them are really only of interest to people who are writing WebAssembly compilers. Instead, we take a look at the proposals that significantly affect the capability of WebAssembly—those that will make it more powerful, easier to work with, and ultimately drive adoption.

Reference Types

The current WebAssembly type system is minimal, with just four numeric types. As previously mentioned, the only way to interact with more-complex types (e.g., strings, objects, arrays, structs) is to serialize them into linear memory and pass a reference to their location. This proposal extends the type system, adding a new anyref type that allows modules to hold references to objects provided by the host environment; in other words, you can pass a JavaScript object to your .wasm module.

The .wasm module can’t really do much with the object via the anyref type. What’s more important is that the module is holding a reference to a garbage collected object on the JavaScript heap, meaning they need to be traced during .wasm execution. This proposal is seen as a stepping-stone toward the more significant garbage-collection proposal.

Garbage Collection

Most modern programming languages (excluding system-level languages) use a garbage collector (GC) for memory management. In brief, the GC frees developers from thinking too deeply about memory management; they can create objects, pass them around, share them between functions/variables, and rely on the GC to clean things up when an object is no longer used.

WebAssembly doesn’t have a garbage collector. In fact, it doesn’t have any tools for memory management, it simply provides you with a block of linear memory. Languages that don’t use a GC still need some sort of mechanism for managing allocation of memory. As an example, Rust uses a small WebAssembly-optimized allocator.

Currently, languages that require a GC have no other option than to compile the GC to .wasm and ship that as part of the binary, such as the Blazor runtime, which includes the .Net CLR garbage collector. This comes at a considerable cost, both in binary size and efficiency of the GC algorithm itself. The lack of a GC is the reason why certain languages like Scala and Elm have yet to compile to WebAssembly.

Although this proposal adds GC capabilities to WebAssembly, it will not have its own GC. Instead, it will integrate with the GC provided by the host environment. This choice makes a lot of sense because this and various other proposals (host bindings, reference types) are designed to improve the interoperability with the host, making it easier to share state and call APIs. Having a single GC to manage memory makes this much easier.

Looking at the state of the current proposal, this is a significant change, leading to many additions to the .wasm type system, including simple tuples, structs, and arrays. There have also been discussions around adding a string type. The complexity of this proposal is why we’ve seen the problem broken down into smaller pieces; for example, the reference types proposal, which is an important building block for GC implementation. This allows different components of the problem to be solved, and specifications published independently.

The use of a GC will be optional in WebAssembly, allowing languages like Rust and C++ to use a memory allocator and linear memory. However, the option of integrating with the host’s GC will not only make it easier to compile numerous high-level languages to WebAssembly (Java, C#, Elm, Scala), it will also make it easier to interoperate with objects created by the host, which are often garbage collected, as well.

Threads

JavaScript has had multithreading capabilities for a number of years via Web Workers; however, the mechanisms for transferring data between workers is limited to relatively slow message passing via postMessage. More recently, shared memory was finalized in TC39, and as of February 2017 is part of ECMAScript. This adds shared array buffers and atomics (thread-safe operations), making it much easier to share data between threads.

This proposal for WebAssembly closely mirrors the additions to TC39, allowing shared access to linear memory and providing atomic operations. Notably, this proposal does not introduce a mechanism for creating threads; instead, this functionality is supplied by the host. For WebAssembly modules running within the browser, thread construction will be provided by Web Workers. Native threading might be added in future, but as a separate proposal.

The WebAssembly threads proposal is making good progress, with a number of browsers already implementing the (non-final) specification. When completed, this will make WebAssembly even more powerful for various classes of number-crunching tasks and will make it easier to move preexisting multithreaded applications to the web.

Exception Handling

The WebAssembly MVP doesn’t have anything that resembles exception handling within its current control flow instructions. As a result, tools like Emscripten must emulate this functionality, which has an inherent cost—currently, catching C++ exceptions is turned off by default. Other languages face similar challenges.

The exception-handling proposal outlines a lot of the complexity involved in constructing a suitable concept that plays nicely with the host environment. Interestingly, this is the second attempt at creating a suitable proposal, which is likely an indicator of the challenges they face!

Rather than just adding exceptions to WebAssembly, this proposal looks to introduce a more generic concept of events, that look a lot like interrupts. When an event occurs, execution is suspended, and a suitable handler is located within a new events section.

Although events are a generic concept, it looks like the standard try/catch instructions will also be added. This addition will reduce some of the complexity in WebAssembly compilers.

ECMAScript Module Integration

For a WebAssembly module to be compiled and executed within the browser (or Node), it needs to be fetched, the imports constructed, and the module instantiated, as shown here:

let req = fetch("./myModule.wasm");

let imports = {
  aModule: {
    anImport
  }
};

WebAssembly
  .instantiateStreaming(req, imports)
  .then(
    obj => obj.instance.exports.foo()
  );

The goal of this specification is to allow modules to be loaded directly by import statements:

import {foo} from "./myModule.wasm";
foo();

This proposal also seeks to make it possible to load WebAssembly modules directly via script tags, using the module type:

<script type='module' src="./myModule.wasm"></script>

Various JavaScript module bundlers (Rollup, Webpack) are already implementing similar behaviors, allowing developers to bundle (i.e., combine) JavaScript and WebAssembly into a single file for download. This proposal seeks to standardize this process.

Interface Types

The WebAssembly virtual machine (VM) supports only numeric types, and there are no immediate plans to add more complex types (strings, arrays, objects). For a WebAssembly module to exchange complex types with the host, it needs to do so via linear memory as we saw in Chapter 2. Unfortunately, this requires glue code to perform the necessary encoding and decoding, which adds bloat and affects performance.

Since the release of the WebAssembly MVP, there have been a few different proposals that tackle this issue, including host bindings, which made it easier to generate the required glue code, and Web IDL bindings that allowed direct invocation of browser APIs. However, both have been replaced with a more far-reaching proposal, interface types. There has also been significant interest in using WebAssembly outside of the browser, both as a standalone runtime, and within a wide variety of hosts. As a result, any solution to the interoperability problem needs to be sufficiently generic. Ideally it should be possible to write a single WebAssembly module that can be used anywhere.

The interface types proposal adds a new set of interface types to WebAssembly that describe high-level values (like strings, sequences, records, and variants) but not their memory representation.

In this simple example, a .wasm module returns a “hello world” string:

(module
  (memory (export "mem") 1)
  (data (i32.const 0) "hello world")
  (func (export "greetingImpl") (result i32 i32)
    i32.const 0    ;; offset of string in memory
    i32.const 11   ;; length
  )
  @interface func (export "greeting") (result string)
    call-export "greetingImpl"
    memory-to-string "mem"
  )
)

In Chapter 2, we saw the additional glue that was required to locate and decode the string in linear memory. In the preceding code, the @interface statement declares an adapter function that is exported with the name greeting. The body of this adapter calls the greetingImpl function, which leaves two i32 values on the stack. The memory-to-string instruction pops the two i32 values, reads and decodes UTF-8 bytes from memory, and pushes the resulting string value.

When the above module is instantiated by the host, the exported greeting function returns a string type, where the representation of this string is specific to the host.

The interface types proposal will significantly reduce the friction that currently exists when interoperating with WebAssembly modules, making it easier to share native code across platforms and runtimes.

WebAssembly System Interface

The final feature we look at is the WebAssembly System Interface (WASI). Unlike the other features discussed in this chapter, WASI isn’t a proposed enhancement of the WebAssembly specification; instead, it is being developed as a separate standard that complements the current specification.

Since its initial release, WebAssembly has gained a lot of traction and support in nonbrowser applications (as you’ll see in the next chapter). However, one significant obstacle to adoption, and specifically portability, is the lack of a widely supported system interface.

A system interface is an API that allows an application to perform I/O tasks and access system resources. For example, if you write an application that reads data from a file, the call is marshalled via a system interface, allowing the operating system to perform various security and permissioning checks. However, there exists a large number of operating systems, including macOS, Windows, and a whole family of Unix-like systems. To avoid the need to program against various different APIs, a system interface is defined (e.g., POSIX) which allows interoperability across a range of operating systems.

With WebAssembly gaining traction for nonbrowser applications, it is important to define a system interface in order to allow portability of modules (notably the goal here is runtime portability rather than compile-time).

WASI currently defines a system interface called wasi-core, which each host can implement to provide network and filesystem access. As the WASI standard expands, additional interfaces will be added as separate modules based around capabilities. These interfaces will significantly improve portability of WebAssembly modules and will likely further fuel the use of WebAssembly for a growing range of nonbrowser applications.

There are a number of exciting new features in store for WebAssembly, both enhancing the power of the core runtime and through initiatives like WASI, increasing the overall capabilities of the platform. In Chapter 5, we look at some of the interesting applications of WebAssembly beyond its original home in the browser.

Get What Is WebAssembly? now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.