pub struct Linker<T> { /* private fields */ }
Expand description
Structure used to link wasm modules/instances together.
This structure is used to assist in instantiating a Module
. A Linker
is a way of performing name resolution to make instantiating a module easier
than specifying positional imports to Instance::new
. Linker
is a
name-based resolver where names are dynamically defined and then used to
instantiate a Module
.
An important method is Linker::instantiate
which takes a module to
instantiate into the provided store. This method will automatically select
all the right imports for the Module
to be instantiated, and will
otherwise return an error if an import isn’t satisfied.
Name Resolution
As mentioned previously, Linker
is a form of name resolver. It will be
using the string-based names of imports on a module to attempt to select a
matching item to hook up to it. This name resolution has two-levels of
namespaces, a module level and a name level. Each item is defined within a
module and then has its own name. This basically follows the wasm standard
for modularization.
Names in a Linker
cannot be defined twice, but allowing duplicates by
shadowing the previous definition can be controlled with the
Linker::allow_shadowing
method.
Commands and Reactors
The Linker
type provides conveniences for working with WASI Commands and
Reactors through the Linker::module
method. This will automatically
handle instantiation and calling _start
and such as appropriate
depending on the inferred type of module.
Type parameter T
It’s worth pointing out that the type parameter T
on Linker<T>
does
not represent that T
is stored within a Linker
. Rather the T
is used
to ensure that linker-defined functions and stores instantiated into all use
the same matching T
as host state.
Multiple Store
s
The Linker
type is designed to be compatible, in some scenarios, with
instantiation in multiple Store
s. Specifically host-defined functions
created in Linker
with Linker::func_new
, Linker::func_wrap
, and
their async versions are compatible to instantiate into any Store
. This
enables programs which want to instantiate lots of modules to create one
Linker
value at program start up and use that continuously for each
Store
created over the lifetime of the program.
Note that once Store
-owned items, such as Global
, are defined witin
a Linker
then it is no longer compatible with any Store
. At that
point only the Store
that owns the Global
can be used to instantiate
modules.
Multiple Engine
s
The Linker
type is not compatible with usage between multiple Engine
values. An Engine
is provided when a Linker
is created and only
stores and items which originate from that Engine
can be used with this
Linker
. If more than one Engine
is used with a Linker
then that
may cause a panic at runtime, similar to how if a Func
is used with the
wrong Store
that can also panic at runtime.
Implementations§
source§impl<T> Linker<T>
impl<T> Linker<T>
sourcepub fn allow_shadowing(&mut self, allow: bool) -> &mut Self
pub fn allow_shadowing(&mut self, allow: bool) -> &mut Self
Configures whether this Linker
will shadow previous duplicate
definitions of the same signature.
By default a Linker
will disallow duplicate definitions of the same
signature. This method, however, can be used to instead allow duplicates
and have the latest definition take precedence when linking modules.
Examples
let mut linker = Linker::<()>::new(&engine);
linker.func_wrap("", "", || {})?;
// by default, duplicates are disallowed
assert!(linker.func_wrap("", "", || {}).is_err());
// but shadowing can be configured to be allowed as well
linker.allow_shadowing(true);
linker.func_wrap("", "", || {})?;
sourcepub fn allow_unknown_exports(&mut self, allow: bool) -> &mut Self
pub fn allow_unknown_exports(&mut self, allow: bool) -> &mut Self
Configures whether this Linker
will allow unknown exports from
command modules.
By default a Linker
will error when unknown exports are encountered
in a command module while using Linker::module
.
This method can be used to allow unknown exports from command modules.
Examples
let mut linker = Linker::new(&engine);
linker.allow_unknown_exports(true);
linker.module(&mut store, "mod", &module)?;
sourcepub fn define_unknown_imports_as_traps(&mut self, module: &Module) -> Result<()>
pub fn define_unknown_imports_as_traps(&mut self, module: &Module) -> Result<()>
Implement any imports of the given Module
with a function which traps.
By default a Linker
will error when unknown imports are encountered
in a command module while using Linker::module
. Use this function
when
This method can be used to allow unknown imports from command modules.
Examples
let mut linker = Linker::new(&engine);
linker.define_unknown_imports_as_traps(&module)?;
linker.instantiate(&mut store, &module)?;
sourcepub fn define(
&mut self,
module: &str,
name: &str,
item: impl Into<Extern>
) -> Result<&mut Self>
pub fn define( &mut self, module: &str, name: &str, item: impl Into<Extern> ) -> Result<&mut Self>
Defines a new item in this Linker
.
This method will add a new definition, by name, to this instance of
Linker
. The module
and name
provided are what to name the
item
.
Errors
Returns an error if the module
and name
already identify an item
of the same type as the item
provided and if shadowing is disallowed.
For more information see the documentation on Linker
.
Examples
let mut linker = Linker::new(&engine);
let ty = GlobalType::new(ValType::I32, Mutability::Const);
let global = Global::new(&mut store, ty, Val::I32(0x1234))?;
linker.define("host", "offset", global)?;
let wat = r#"
(module
(import "host" "offset" (global i32))
(memory 1)
(data (global.get 0) "foo")
)
"#;
let module = Module::new(&engine, wat)?;
linker.instantiate(&mut store, &module)?;
sourcepub fn define_name(
&mut self,
name: &str,
item: impl Into<Extern>
) -> Result<&mut Self>
pub fn define_name( &mut self, name: &str, item: impl Into<Extern> ) -> Result<&mut Self>
Same as Linker::define
, except only the name of the import is
provided, not a module name as well.
This is only relevant when working with the module linking proposal where one-level names are allowed (in addition to two-level names). Otherwise this method need not be used.
sourcepub fn func_new(
&mut self,
module: &str,
name: &str,
ty: FuncType,
func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static
) -> Result<&mut Self>
pub fn func_new( &mut self, module: &str, name: &str, ty: FuncType, func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static ) -> Result<&mut Self>
Creates a Func::new
-style function named in this linker.
For more information see Linker::func_wrap
.
sourcepub unsafe fn func_new_unchecked(
&mut self,
module: &str,
name: &str,
ty: FuncType,
func: impl Fn(Caller<'_, T>, &mut [ValRaw]) -> Result<(), Trap> + Send + Sync + 'static
) -> Result<&mut Self>
pub unsafe fn func_new_unchecked( &mut self, module: &str, name: &str, ty: FuncType, func: impl Fn(Caller<'_, T>, &mut [ValRaw]) -> Result<(), Trap> + Send + Sync + 'static ) -> Result<&mut Self>
Creates a Func::new_unchecked
-style function named in this linker.
For more information see Linker::func_wrap
.
sourcepub fn func_wrap<Params, Args>(
&mut self,
module: &str,
name: &str,
func: impl IntoFunc<T, Params, Args>
) -> Result<&mut Self>
pub fn func_wrap<Params, Args>( &mut self, module: &str, name: &str, func: impl IntoFunc<T, Params, Args> ) -> Result<&mut Self>
Define a host function within this linker.
For information about how the host function operates, see
Func::wrap
. That includes information about translating Rust types
to WebAssembly native types.
This method creates a host-provided function in this linker under the
provided name. This method is distinct in its capability to create a
Store
-independent function. This means that the
function defined here can be used to instantiate instances in multiple
different stores, or in other words the function can be loaded into
different stores.
Note that the capability mentioned here applies to all other
host-function-defining-methods on Linker
as well. All of them can be
used to create instances of Func
within multiple stores. In a
multithreaded program, for example, this means that the host functions
could be called concurrently if different stores are executing on
different threads.
Errors
Returns an error if the module
and name
already identify an item
of the same type as the item
provided and if shadowing is disallowed.
For more information see the documentation on Linker
.
Examples
let mut linker = Linker::new(&engine);
linker.func_wrap("host", "double", |x: i32| x * 2)?;
linker.func_wrap("host", "log_i32", |x: i32| println!("{}", x))?;
linker.func_wrap("host", "log_str", |caller: Caller<'_, ()>, ptr: i32, len: i32| {
// ...
})?;
let wat = r#"
(module
(import "host" "double" (func (param i32) (result i32)))
(import "host" "log_i32" (func (param i32)))
(import "host" "log_str" (func (param i32 i32)))
)
"#;
let module = Module::new(&engine, wat)?;
// instantiate in multiple different stores
for _ in 0..10 {
let mut store = Store::new(&engine, ());
linker.instantiate(&mut store, &module)?;
}
sourcepub fn instance(
&mut self,
store: impl AsContextMut<Data = T>,
module_name: &str,
instance: Instance
) -> Result<&mut Self>
pub fn instance( &mut self, store: impl AsContextMut<Data = T>, module_name: &str, instance: Instance ) -> Result<&mut Self>
Convenience wrapper to define an entire Instance
in this linker.
This function is a convenience wrapper around Linker::define
which
will define all exports on instance
into this linker. The module name
for each export is module_name
, and the name for each export is the
name in the instance itself.
Note that when this API is used the Linker
is no longer compatible
with multi-Store
instantiation because the items
defined within this store will belong to the store
provided, and only
the store
provided.
Errors
Returns an error if the any item is redefined twice in this linker (for
example the same module_name
was already defined) and shadowing is
disallowed, or if instance
comes from a different
Store
than this Linker
originally was created
with.
Panics
Panics if instance
does not belong to store
.
Examples
let mut linker = Linker::new(&engine);
// Instantiate a small instance...
let wat = r#"(module (func (export "run") ))"#;
let module = Module::new(&engine, wat)?;
let instance = linker.instantiate(&mut store, &module)?;
// ... and inform the linker that the name of this instance is
// `instance1`. This defines the `instance1::run` name for our next
// module to use.
linker.instance(&mut store, "instance1", instance)?;
let wat = r#"
(module
(import "instance1" "run" (func $instance1_run))
(func (export "run")
call $instance1_run
)
)
"#;
let module = Module::new(&engine, wat)?;
let instance = linker.instantiate(&mut store, &module)?;
sourcepub fn module(
&mut self,
store: impl AsContextMut<Data = T>,
module_name: &str,
module: &Module
) -> Result<&mut Self>where
T: 'static,
pub fn module( &mut self, store: impl AsContextMut<Data = T>, module_name: &str, module: &Module ) -> Result<&mut Self>where T: 'static,
Define automatic instantiations of a Module
in this linker.
This automatically handles Commands and Reactors instantiation and initialization.
Exported functions of a Command module may be called directly, however
instead of having a single instance which is reused for each call,
each call creates a new instance, which lives for the duration of the
call. The imports of the Command are resolved once, and reused for
each instantiation, so all dependencies need to be present at the time
when Linker::module
is called.
For Reactors, a single instance is created, and an initialization function is called, and then its exports may be called.
Ordinary modules which don’t declare themselves to be either Commands or Reactors are treated as Reactors without any initialization calls.
Errors
Returns an error if the any item is redefined twice in this linker (for
example the same module_name
was already defined) and shadowing is
disallowed, if instance
comes from a different
Store
than this Linker
originally was created
with, or if a Reactor initialization function traps.
Panics
Panics if any item used to instantiate the provided Module
is not
owned by store
, or if the store
provided comes from a different
Engine
than this Linker
.
Examples
let mut linker = Linker::new(&engine);
// Instantiate a small instance and inform the linker that the name of
// this instance is `instance1`. This defines the `instance1::run` name
// for our next module to use.
let wat = r#"(module (func (export "run") ))"#;
let module = Module::new(&engine, wat)?;
linker.module(&mut store, "instance1", &module)?;
let wat = r#"
(module
(import "instance1" "run" (func $instance1_run))
(func (export "run")
call $instance1_run
)
)
"#;
let module = Module::new(&engine, wat)?;
let instance = linker.instantiate(&mut store, &module)?;
For a Command, a new instance is created for each call.
let mut linker = Linker::new(&engine);
// Create a Command that attempts to count the number of times it is run, but is
// foiled by each call getting a new instance.
let wat = r#"
(module
(global $counter (mut i32) (i32.const 0))
(func (export "_start")
(global.set $counter (i32.add (global.get $counter) (i32.const 1)))
)
(func (export "read_counter") (result i32)
(global.get $counter)
)
)
"#;
let module = Module::new(&engine, wat)?;
linker.module(&mut store, "commander", &module)?;
let run = linker.get_default(&mut store, "")?
.typed::<(), (), _>(&store)?
.clone();
run.call(&mut store, ())?;
run.call(&mut store, ())?;
run.call(&mut store, ())?;
let wat = r#"
(module
(import "commander" "_start" (func $commander_start))
(import "commander" "read_counter" (func $commander_read_counter (result i32)))
(func (export "run") (result i32)
call $commander_start
call $commander_start
call $commander_start
call $commander_read_counter
)
)
"#;
let module = Module::new(&engine, wat)?;
linker.module(&mut store, "", &module)?;
let run = linker.get(&mut store, "", "run").unwrap().into_func().unwrap();
let count = run.typed::<(), i32, _>(&store)?.call(&mut store, ())?;
assert_eq!(count, 0, "a Command should get a fresh instance on each invocation");
sourcepub fn alias(
&mut self,
module: &str,
name: &str,
as_module: &str,
as_name: &str
) -> Result<&mut Self>
pub fn alias( &mut self, module: &str, name: &str, as_module: &str, as_name: &str ) -> Result<&mut Self>
Aliases one item’s name as another.
This method will alias an item with the specified module
and name
under a new name of as_module
and as_name
.
Errors
Returns an error if any shadowing violations happen while defining new items, or if the original item wasn’t defined.
sourcepub fn alias_module(&mut self, module: &str, as_module: &str) -> Result<()>
pub fn alias_module(&mut self, module: &str, as_module: &str) -> Result<()>
Aliases one module’s name as another.
This method will alias all currently defined under module
to also be
defined under the name as_module
too.
Errors
Returns an error if any shadowing violations happen while defining new items.
sourcepub fn instantiate(
&self,
store: impl AsContextMut<Data = T>,
module: &Module
) -> Result<Instance>
pub fn instantiate( &self, store: impl AsContextMut<Data = T>, module: &Module ) -> Result<Instance>
Attempts to instantiate the module
provided.
This method will attempt to assemble a list of imports that correspond
to the imports required by the Module
provided. This list
of imports is then passed to Instance::new
to continue the
instantiation process.
Each import of module
will be looked up in this Linker
and must
have previously been defined. If it was previously defined with an
incorrect signature or if it was not previously defined then an error
will be returned because the import can not be satisfied.
Per the WebAssembly spec, instantiation includes running the module’s
start function, if it has one (not to be confused with the _start
function, which is not run).
Errors
This method can fail because an import may not be found, or because
instantiation itself may fail. For information on instantiation
failures see Instance::new
.
Panics
Panics if any item used to instantiate module
is not owned by
store
. Additionally this will panic if the Engine
that the store
belongs to is different than this Linker
.
Examples
let mut linker = Linker::new(&engine);
linker.func_wrap("host", "double", |x: i32| x * 2)?;
let wat = r#"
(module
(import "host" "double" (func (param i32) (result i32)))
)
"#;
let module = Module::new(&engine, wat)?;
linker.instantiate(&mut store, &module)?;
sourcepub fn instantiate_pre(
&self,
store: impl AsContextMut<Data = T>,
module: &Module
) -> Result<InstancePre<T>>
pub fn instantiate_pre( &self, store: impl AsContextMut<Data = T>, module: &Module ) -> Result<InstancePre<T>>
Performs all checks necessary for instantiating module
with this
linker within store
, except that instantiation doesn’t actually
finish.
This method is used for front-loading type-checking information as well
as collecting the imports to use to instantiate a module with. The
returned InstancePre
represents a ready-to-be-instantiated module,
which can also be instantiated multiple times if desired.
Panics
This method will panic if any item defined in this linker used by
module
is not owned by store
. Additionally this will panic if the
Engine
that the store
belongs to is different than this
Linker
.
Examples
let mut linker = Linker::new(&engine);
linker.func_wrap("host", "double", |x: i32| x * 2)?;
let wat = r#"
(module
(import "host" "double" (func (param i32) (result i32)))
)
"#;
let module = Module::new(&engine, wat)?;
let instance_pre = linker.instantiate_pre(&mut store, &module)?;
// Finish instantiation after the type-checking has all completed...
let instance = instance_pre.instantiate(&mut store)?;
// ... and we can even continue to keep instantiating if desired!
instance_pre.instantiate(&mut store)?;
instance_pre.instantiate(&mut store)?;
// Note that functions defined in a linker with `func_wrap` and similar
// constructors are not owned by any particular `Store`, so we can also
// instantiate our `instance_pre` in other stores because no imports
// belong to the original store.
let mut new_store = Store::new(&engine, ());
instance_pre.instantiate(&mut new_store)?;
sourcepub fn iter<'a: 'p, 'p>(
&'a self,
store: impl AsContextMut<Data = T> + 'p
) -> impl Iterator<Item = (&str, &str, Extern)> + 'p
pub fn iter<'a: 'p, 'p>( &'a self, store: impl AsContextMut<Data = T> + 'p ) -> impl Iterator<Item = (&str, &str, Extern)> + 'p
Returns an iterator over all items defined in this Linker
, in
arbitrary order.
The iterator returned will yield 3-tuples where the first two elements are the module name and item name for the external item, and the third item is the item itself that is defined.
Note that multiple Extern
items may be defined for the same
module/name pair.
Panics
This function will panic if the store
provided does not come from the
same Engine
that this linker was created with.
sourcepub fn get(
&self,
store: impl AsContextMut<Data = T>,
module: &str,
name: &str
) -> Option<Extern>
pub fn get( &self, store: impl AsContextMut<Data = T>, module: &str, name: &str ) -> Option<Extern>
sourcepub fn get_by_import(
&self,
store: impl AsContextMut<Data = T>,
import: &ImportType<'_>
) -> Option<Extern>
pub fn get_by_import( &self, store: impl AsContextMut<Data = T>, import: &ImportType<'_> ) -> Option<Extern>
sourcepub fn get_default(
&self,
store: impl AsContextMut<Data = T>,
module: &str
) -> Result<Func>
pub fn get_default( &self, store: impl AsContextMut<Data = T>, module: &str ) -> Result<Func>
Returns the “default export” of a module.
An export with an empty string is considered to be a “default export”. “_start” is also recognized for compatibility.
Panics
Panics if the default function found is not owned by store
. This
function will also panic if the store
provided does not come from the
same Engine
that this linker was created with.