Wasmex. Components. HostResource
(wasmex v0.15.0)
Copy Markdown
Defines a host-owned WebAssembly component resource implemented in Elixir.
Given this imported WIT resource:
interface counters {
resource counter {
constructor(initial: u32);
with-value: static func(value: u32) -> counter;
increment: func() -> u32;
take-other: func(other: counter) -> u32;
}
}implement its callbacks and pass the generated imports to the component:
defmodule CounterHost do
use Wasmex.Components.HostResource,
wit_path: "wit",
resource: "counter"
def new(initial), do: start_counter(initial)
def with_value(value), do: start_counter(value)
def increment(counter), do: Agent.get_and_update(counter, &{&1 + 1, &1 + 1})
# `other` is an owned argument, so this callback is now responsible for it.
def take_other(counter, other) do
value = Agent.get(counter, & &1) + Agent.get(other, & &1)
Agent.stop(other)
value
end
def drop(counter), do: Agent.stop(counter)
defp start_counter(initial) do
{:ok, counter} = Agent.start_link(fn -> initial end)
counter
end
end
{:ok, component} =
Wasmex.Components.start_link(
bytes: File.read!("component.wasm"),
imports: CounterHost.imports()
)Callback mapping
A resource is represented by any opaque Elixir term. A PID, reference, ETS key, or struct containing those values works well when the resource has mutable state.
- A WIT constructor maps to
new/arityand returns the opaque term. - A method maps to its snake-case name and receives the opaque term first.
- A static function maps to its snake-case name without a resource argument.
- A resource returned directly or inside a tuple, option, result, record, variant, or list is represented by an opaque term in the matching Elixir position.
drop/1is called when the guest destroys an owned handle. It defaults to a no-op and should be overridden when the term owns external state.
Passing borrow<T> to a callback leaves the guest handle live. Passing
own<T> transfers ownership to the callback and removes the guest handle;
drop/1 is not subsequently called for that handle. The callback must either
release the state or return it in a resource-typed result.
Callback exceptions and incompatible return values trap the active component call. The component server itself remains alive. Wasmex releases any partially lowered Wasmtime handles, but a callback that allocated external state before returning an invalid value remains responsible for that state.
Multiple resources and worlds
Merge resources imported from the same or different interfaces with:
imports =
Wasmex.Components.HostResource.merge_imports([
CounterHost,
LabelHost,
%{
"package:namespace/interface@version" => %{
"freestanding-function" => {:fn, &MyHost.function/1}
}
}
])Maps can therefore be included for freestanding imports that share an interface with a generated resource.
Set :world when the WIT package contains multiple worlds. If multiple
imported interfaces contain the same resource name, select one with
interface: "package:namespace/interface@version".
Use wit: source for a self-contained WIT package. Use wit_path: path for
a WIT file or directory. A directory may contain the standard deps
subdirectory, making wit_path: the best choice for packages that import
WASI or other dependency packages. Files used beneath the path are registered
as external compile resources, so changing them recompiles the host module.
User imports are linked after Wasmtime's built-in WASI interfaces and may
intentionally override them. For example, a module selecting
interface: "wasi:io/error@0.2.12" can implement that standard resource in
Elixir. The component and host WIT versions must match exactly.
Summary
Functions
Deep-merges generated host-resource modules and ordinary import maps.