Support for Reference Types
WebAssembly recently has gained support for a new value type called externref
.
Proposed in the WebAssembly reference types
repo this feature of
WebAssembly is hoped to enable more efficient communication between the host
(JS) and the Wasm module. This feature removes the need for much of the JS glue
generated by wasm-bindgen
because it can natively call APIs with JS values.
For example, this Rust function:
#![allow(unused)] fn main() { #[wasm_bindgen] pub fn takes_js_value(a: &JsValue) { // ... } }
generates this JS glue without reference types support:
const heap = new Array(32).fill(undefined);
heap.push(undefined, null, true, false);
let stack_pointer = 32;
function addBorrowedObject(obj) {
if (stack_pointer == 1) throw new Error('out of js stack');
heap[--stack_pointer] = obj;
return stack_pointer;
}
export function takes_js_value(a) {
try {
wasm.takes_js_value(addBorrowedObject(a));
} finally {
heap[stack_pointer++] = undefined;
}
}
We can see here how under the hood the JS is managing a table of JS values which
are passed to the Wasm binary, so Wasm actually only works in indices. If we
compile with -Ctarget-feature=+reference-types
(by default since Rust v1.82),
however, the generated JS looks like:
export function takes_js_value(a) {
wasm.takes_js_value(a);
}
And that's it! The WebAssembly binary takes the JS value directly and manages it internally.
Currently this feature is supported in Firefox 79+ and Chrome. Support in other
browsers is likely coming soon! In Node.js this feature is behind the
--experimental-wasm-anyref
flag, although the support does not currently align
with the upstream specification as of 14.6.0.