Expand description
Low-cost reference type shims for WASM modules.
Reference type (aka externref
or anyref
) is an opaque reference made available to
a WASM module by the host environment. Such references cannot be forged in the WASM code
and can be associated with arbitrary host data, thus making them a good alternative to
ad-hoc handles (e.g., numeric ones). References cannot be stored in WASM linear memory;
they are confined to the stack and tables with externref
elements.
Rust does not support reference types natively; there is no way to produce an import / export
that has externref
as an argument or a return type. wasm-bindgen
patches WASM if
externref
s are enabled. This library strives to accomplish the same goal for generic
low-level WASM ABIs (wasm-bindgen
is specialized for browser hosts).
§externref
use cases
Since externref
s are completely opaque from the module perspective, the only way to use
them is to send an externref
back to the host as an argument of an imported function.
(Depending on the function semantics, the call may or may not consume the externref
and may or may not modify the underlying data; this is not reflected
by the WASM function signature.) An externref
cannot be dereferenced by the module,
thus, the module cannot directly access or modify the data behind the reference. Indeed,
the module cannot even be sure which kind of data is being referenced.
It may seem that this limits externref
utility significantly,
but externref
s can still be useful, e.g. to model capability-based security tokens
or resource handles in the host environment. Another potential use case is encapsulating
complex data that would be impractical to transfer across the WASM API boundary
(especially if the data shape may evolve over time), and/or if interactions with data
must be restricted from the module side.
§Usage
- Use
Resource
s as arguments / return results for imported and/or exported functions in a WASM module in place ofexternref
s . Reference args (including mutable references) and theOption<_>
wrapper are supported as well. - Add the
#[externref]
proc macro on the imported / exported functions. - Post-process the generated WASM module with the
processor
.
Resource
s support primitive downcasting and upcasting with Resource<()>
signalling
a generic resource. Downcasting is unchecked; it is up to the Resource
users to
define a way to check the resource kind dynamically if necessary. One possible approach
for this is defining a WASM import fn(&Resource<()>) -> Kind
, where Kind
is the encoded
kind of the supplied resource, such as i32
.
§How it works
The externref
macro detects Resource
args / return types
for imported and exported functions. All Resource
args or return types are replaced
with usize
s and a wrapper function is added that performs the necessary transform
from / to usize
.
Additionally, a function signature describing where Resource
args are located
is recorded in a WASM custom section.
To handle usize
(~i32
in WASM) <-> externref
conversions, managing resources is performed
using 3 function imports from a surrogate module:
- Creating a
Resource
(“real” signaturefn(externref) -> usize
) stores a reference into anexternref
table and returns the table index. The index is what is actually stored within theResource
, meaning thatResource
s can be easily placed on heap. - Getting a reference from a
Resource
(“real” signaturefn(usize) -> externref
) is an indexing operation for theexternref
table. Resource::drop()
(“real” signaturefn(usize)
) removes the reference from the table.
Real externref
s are patched back to the imported / exported functions
by the WASM module post-processor:
- Imports from a surrogate module referenced by
Resource
methods are replaced with local WASM functions. Functions for getting anexternref
from the table and dropping anexternref
are more or less trivial. Storing anexternref
is less so; we don’t want to excessively grow theexternref
s table, thus we search for null refs among its existing elements first, and only grow the table if all existing table elements are occupied. - Patching changes function types, and as a result types of some locals.
This is OK because the post-processor also changes the signatures of affected
imported / exported functions. The success relies on the fact that
a reference is only stored immediately after receiving it from the host;
likewise, a reference is only obtained immediately before passing it to the host.
Resource
s can be dropped anywhere, but the correspondingexternref
removal function does not need its type changed.
§Crate features
§processor
(Off by default)
Enables WASM module processing via the processor
module.
§tracing
(Off by default)
Enables tracing during module processing with the tracing
facade.
Tracing events / spans mostly use INFO
and DEBUG
levels.
§Examples
Using the #[externref]
macro and Resource
s in WASM-targeting code:
use externref::{externref, Resource};
// Two marker types for different resources.
pub struct Sender(());
pub struct Bytes(());
#[externref]
#[link(wasm_import_module = "test")]
extern "C" {
// This import will have signature `(externref, i32, i32) -> externref`
// on host.
fn send_message(
sender: &Resource<Sender>,
message_ptr: *const u8,
message_len: usize,
) -> Resource<Bytes>;
// `Option`s are used to deal with null references. This function will have
// `(externref) -> i32` signature.
fn message_len(bytes: Option<&Resource<Bytes>>) -> usize;
// This one has `() -> externref` signature.
fn last_sender() -> Option<Resource<Sender>>;
}
// This export will have signature `(externref)` on host.
#[externref]
#[export_name = "test_export"]
pub extern "C" fn test_export(sender: Resource<Sender>) {
let messages: Vec<_> = ["test", "42", "some other string"]
.into_iter()
.map(|msg| {
unsafe { send_message(&sender, msg.as_ptr(), msg.len()) }
})
.collect();
// ...
// All 4 resources are dropped when exiting the function.
}
Modules§
- processor
processor
- WASM module processor for
externref
s.
Structs§
- BitSlice
- Slice of bits. This type is used to mark
Resource
args in imported / exported functions. - Function
- Information about a function with
Resource
args or return type. - Read
Error - Errors that can occur when reading declarations of functions manipulating
Resource
s from a WASM module. - Resource
- Host resource exposed to WASM.
Enums§
- Function
Kind - Kind of a function with
Resource
args or return type. - Read
Error Kind - Kind of a
ReadError
.
Attribute Macros§
- externref
macro
- Prepares imported functions or an exported function with
Resource
args and/or return type.