Files
wasmtime/crates/wasmtime/src/linker.rs
Adam Bratschi-Kaye afd10646c9 List exports of an instance in linking error (#3456)
When there is a linking error caused by an undefined instance, list all
the instances exports in the error message. This will clarify errors for
undefined two-level imports that get desugared to one-level instance
imports under the module-linking proposal.
2021-10-20 16:31:53 -05:00

1282 lines
49 KiB
Rust

use crate::func::HostFunc;
use crate::instance::{InstanceData, InstancePre};
use crate::store::StoreOpaque;
use crate::{
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
IntoFunc, Module, StoreContextMut, Trap, Val, ValRaw,
};
use anyhow::{anyhow, bail, Context, Result};
use log::warn;
use std::collections::hash_map::{Entry, HashMap};
#[cfg(feature = "async")]
use std::future::Future;
use std::marker;
#[cfg(feature = "async")]
use std::pin::Pin;
use std::sync::Arc;
/// 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.
///
/// [`Store`]: crate::Store
/// [`Global`]: crate::Global
pub struct Linker<T> {
engine: Engine,
string2idx: HashMap<Arc<str>, usize>,
strings: Vec<Arc<str>>,
map: HashMap<ImportKey, Definition>,
allow_shadowing: bool,
allow_unknown_exports: bool,
_marker: marker::PhantomData<fn() -> T>,
}
impl<T> Clone for Linker<T> {
fn clone(&self) -> Linker<T> {
Linker {
engine: self.engine.clone(),
string2idx: self.string2idx.clone(),
strings: self.strings.clone(),
map: self.map.clone(),
allow_shadowing: self.allow_shadowing,
allow_unknown_exports: self.allow_unknown_exports,
_marker: self._marker,
}
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
struct ImportKey {
name: usize,
module: usize,
}
#[derive(Clone)]
pub(crate) enum Definition {
Extern(Extern),
HostFunc(Arc<HostFunc>),
Instance(Arc<indexmap::IndexMap<String, Definition>>),
}
macro_rules! generate_wrap_async_func {
($num:tt $($args:ident)*) => (paste::paste!{
/// Asynchronous analog of [`Linker::func_wrap`].
///
/// For more information also see
/// [`Func::wrapN_async`](crate::Func::wrap1_async).
#[allow(non_snake_case)]
#[cfg(feature = "async")]
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
pub fn [<func_wrap $num _async>]<$($args,)* R>(
&mut self,
module: &str,
name: &str,
func: impl for<'a> Fn(Caller<'a, T>, $($args),*) -> Box<dyn Future<Output = R> + Send + 'a> + Send + Sync + 'static,
) -> Result<&mut Self>
where
$($args: crate::WasmTy,)*
R: crate::WasmRet,
{
assert!(
self.engine.config().async_support,
concat!(
"cannot use `func_wrap",
$num,
"_async` without enabling async support on the config",
),
);
self.func_wrap(module, name, move |mut caller: Caller<'_, T>, $($args: $args),*| {
let async_cx = caller.store.as_context_mut().0.async_cx();
let mut future = Pin::from(func(caller, $($args),*));
match unsafe { async_cx.block_on(future.as_mut()) } {
Ok(ret) => ret.into_fallible(),
Err(e) => R::fallible_from_trap(e),
}
})
}
})
}
impl<T> Linker<T> {
/// Creates a new [`Linker`].
///
/// The linker will define functions within the context of the `engine`
/// provided and can only instantiate modules for a [`Store`][crate::Store]
/// that is also defined within the same [`Engine`]. Usage of stores with
/// different [`Engine`]s may cause a panic when used with this [`Linker`].
pub fn new(engine: &Engine) -> Linker<T> {
Linker {
engine: engine.clone(),
map: HashMap::new(),
string2idx: HashMap::new(),
strings: Vec::new(),
allow_shadowing: false,
allow_unknown_exports: false,
_marker: marker::PhantomData,
}
}
/// Returns the [`Engine`] this is connected to.
pub fn engine(&self) -> &Engine {
&self.engine
}
/// 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
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// 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("", "", || {})?;
/// # Ok(())
/// # }
/// ```
pub fn allow_shadowing(&mut self, allow: bool) -> &mut Self {
self.allow_shadowing = allow;
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
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// # let module = Module::new(&engine, "(module)")?;
/// # let mut store = Store::new(&engine, ());
/// let mut linker = Linker::new(&engine);
/// linker.allow_unknown_exports(true);
/// linker.module(&mut store, "mod", &module)?;
/// # Ok(())
/// # }
/// ```
pub fn allow_unknown_exports(&mut self, allow: bool) -> &mut Self {
self.allow_unknown_exports = allow;
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
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// # let mut store = Store::new(&engine, ());
/// 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)?;
/// # Ok(())
/// # }
/// ```
pub fn define(
&mut self,
module: &str,
name: &str,
item: impl Into<Extern>,
) -> Result<&mut Self> {
let key = self.import_key(module, Some(name));
self.insert(key, Definition::Extern(item.into()))?;
Ok(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.
pub fn define_name(&mut self, name: &str, item: impl Into<Extern>) -> Result<&mut Self> {
let key = self.import_key(name, None);
self.insert(key, Definition::Extern(item.into()))?;
Ok(self)
}
/// Creates a [`Func::new`]-style function named in this linker.
///
/// For more information see [`Linker::func_wrap`].
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
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> {
let func = HostFunc::new(&self.engine, ty, func);
let key = self.import_key(module, Some(name));
self.insert(key, Definition::HostFunc(Arc::new(func)))?;
Ok(self)
}
/// Creates a [`Func::new_unchecked`]-style function named in this linker.
///
/// For more information see [`Linker::func_wrap`].
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
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> {
let func = HostFunc::new_unchecked(&self.engine, ty, func);
let key = self.import_key(module, Some(name));
self.insert(key, Definition::HostFunc(Arc::new(func)))?;
Ok(self)
}
/// Creates a [`Func::new_async`]-style function named in this linker.
///
/// For more information see [`Linker::func_wrap`].
#[cfg(all(feature = "async", feature = "cranelift"))]
#[cfg_attr(nightlydoc, doc(cfg(all(feature = "async", feature = "cranelift"))))]
pub fn func_new_async<F>(
&mut self,
module: &str,
name: &str,
ty: FuncType,
func: F,
) -> Result<&mut Self>
where
F: for<'a> Fn(
Caller<'a, T>,
&'a [Val],
&'a mut [Val],
) -> Box<dyn Future<Output = Result<(), Trap>> + Send + 'a>
+ Send
+ Sync
+ 'static,
{
assert!(
self.engine.config().async_support,
"cannot use `func_new_async` without enabling async support in the config"
);
self.func_new(module, name, ty, move |mut caller, params, results| {
let async_cx = caller.store.as_context_mut().0.async_cx();
let mut future = Pin::from(func(caller, params, results));
match unsafe { async_cx.block_on(future.as_mut()) } {
Ok(Ok(())) => Ok(()),
Ok(Err(trap)) | Err(trap) => Err(trap),
}
})
}
/// 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`](crate::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
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// 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)?;
/// }
/// # Ok(())
/// # }
/// ```
pub fn func_wrap<Params, Args>(
&mut self,
module: &str,
name: &str,
func: impl IntoFunc<T, Params, Args>,
) -> Result<&mut Self> {
let func = HostFunc::wrap(&self.engine, func);
let key = self.import_key(module, Some(name));
self.insert(key, Definition::HostFunc(Arc::new(func)))?;
Ok(self)
}
for_each_function_signature!(generate_wrap_async_func);
/// 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`][crate::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`](crate::Store) than this [`Linker`] originally was created
/// with.
///
/// # Panics
///
/// Panics if `instance` does not belong to `store`.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// # let mut store = Store::new(&engine, ());
/// 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)?;
/// # Ok(())
/// # }
/// ```
pub fn instance(
&mut self,
mut store: impl AsContextMut<Data = T>,
module_name: &str,
instance: Instance,
) -> Result<&mut Self> {
for export in instance.exports(store.as_context_mut()) {
let key = self.import_key(module_name, Some(export.name()));
self.insert(key, Definition::Extern(export.into_extern()))?;
}
Ok(self)
}
/// 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.
///
/// [Commands and Reactors]: https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi
///
/// # 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`](crate::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
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// # let mut store = Store::new(&engine, ());
/// 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)?;
/// # Ok(())
/// # }
/// ```
///
/// For a Command, a new instance is created for each call.
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// # let mut store = Store::new(&engine, ());
/// 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, "", Some("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");
///
/// # Ok(())
/// # }
/// ```
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn module(
&mut self,
mut store: impl AsContextMut<Data = T>,
module_name: &str,
module: &Module,
) -> Result<&mut Self>
where
T: 'static,
{
// NB: this is intended to function the same as `Linker::module_async`,
// they should be kept in sync.
// This assert isn't strictly necessary since it'll bottom out in the
// `HostFunc::to_func` method anyway. This is placed earlier for this
// function though to prevent the functions created here from delaying
// the panic until they're called.
assert!(
Engine::same(&self.engine, store.as_context().engine()),
"different engines for this linker and the store provided"
);
match ModuleKind::categorize(module)? {
ModuleKind::Command => {
self.command(
store,
module_name,
module,
|store, func_ty, export_name, instance_pre| {
Func::new(
store,
func_ty.clone(),
move |mut caller, params, results| {
// Create a new instance for this command execution.
let instance = instance_pre.instantiate(&mut caller)?;
// `unwrap()` everything here because we know the instance contains a
// function export with the given name and signature because we're
// iterating over the module it was instantiated from.
instance
.get_export(&mut caller, &export_name)
.unwrap()
.into_func()
.unwrap()
.call(&mut caller, params, results)
.map_err(|error| error.downcast::<Trap>().unwrap())?;
Ok(())
},
)
},
)
}
ModuleKind::Reactor => {
let instance = self.instantiate(&mut store, &module)?;
if let Some(export) = instance.get_export(&mut store, "_initialize") {
if let Extern::Func(func) = export {
func.typed::<(), (), _>(&store)
.and_then(|f| f.call(&mut store, ()).map_err(Into::into))
.context("calling the Reactor initialization function")?;
}
}
self.instance(store, module_name, instance)
}
}
}
/// Define automatic instantiations of a [`Module`] in this linker.
///
/// This is the same as [`Linker::module`], except for async `Store`s.
#[cfg(all(feature = "async", feature = "cranelift"))]
#[cfg_attr(nightlydoc, doc(cfg(all(feature = "async", feature = "cranelift"))))]
pub async fn module_async(
&mut self,
mut store: impl AsContextMut<Data = T>,
module_name: &str,
module: &Module,
) -> Result<&mut Self>
where
T: Send + 'static,
{
// NB: this is intended to function the same as `Linker::module`, they
// should be kept in sync.
assert!(
Engine::same(&self.engine, store.as_context().engine()),
"different engines for this linker and the store provided"
);
match ModuleKind::categorize(module)? {
ModuleKind::Command => self.command(
store,
module_name,
module,
|store, func_ty, export_name, instance_pre| {
let upvars = Arc::new((instance_pre, export_name));
Func::new_async(
store,
func_ty.clone(),
move |mut caller, params, results| {
let upvars = upvars.clone();
Box::new(async move {
let (instance_pre, export_name) = &*upvars;
let instance = instance_pre.instantiate_async(&mut caller).await?;
instance
.get_export(&mut caller, &export_name)
.unwrap()
.into_func()
.unwrap()
.call_async(&mut caller, params, results)
.await
.map_err(|error| error.downcast::<Trap>().unwrap())?;
Ok(())
})
},
)
},
),
ModuleKind::Reactor => {
let instance = self.instantiate_async(&mut store, &module).await?;
if let Some(export) = instance.get_export(&mut store, "_initialize") {
if let Extern::Func(func) = export {
let func = func
.typed::<(), (), _>(&store)
.context("loading the Reactor initialization function")?;
func.call_async(&mut store, ())
.await
.context("calling the Reactor initialization function")?;
}
}
self.instance(store, module_name, instance)
}
}
}
fn command(
&mut self,
mut store: impl AsContextMut<Data = T>,
module_name: &str,
module: &Module,
mk_func: impl Fn(&mut StoreContextMut<T>, &FuncType, String, InstancePre<T>) -> Func,
) -> Result<&mut Self>
where
T: 'static,
{
let mut store = store.as_context_mut();
for export in module.exports() {
if let Some(func_ty) = export.ty().func() {
let instance_pre = self.instantiate_pre(&mut store, module)?;
let export_name = export.name().to_owned();
let func = mk_func(&mut store, func_ty, export_name, instance_pre);
let key = self.import_key(module_name, Some(export.name()));
self.insert(key, Definition::Extern(func.into()))?;
} else if export.name() == "memory" && export.ty().memory().is_some() {
// Allow an exported "memory" memory for now.
} else if export.name() == "__indirect_function_table" && export.ty().table().is_some()
{
// Allow an exported "__indirect_function_table" table for now.
} else if export.name() == "table" && export.ty().table().is_some() {
// Allow an exported "table" table for now.
} else if export.name() == "__data_end" && export.ty().global().is_some() {
// Allow an exported "__data_end" memory for compatibility with toolchains
// which use --export-dynamic, which unfortunately doesn't work the way
// we want it to.
warn!("command module exporting '__data_end' is deprecated");
} else if export.name() == "__heap_base" && export.ty().global().is_some() {
// Allow an exported "__data_end" memory for compatibility with toolchains
// which use --export-dynamic, which unfortunately doesn't work the way
// we want it to.
warn!("command module exporting '__heap_base' is deprecated");
} else if export.name() == "__dso_handle" && export.ty().global().is_some() {
// Allow an exported "__dso_handle" memory for compatibility with toolchains
// which use --export-dynamic, which unfortunately doesn't work the way
// we want it to.
warn!("command module exporting '__dso_handle' is deprecated")
} else if export.name() == "__rtti_base" && export.ty().global().is_some() {
// Allow an exported "__rtti_base" memory for compatibility with
// AssemblyScript.
warn!("command module exporting '__rtti_base' is deprecated; pass `--runtime half` to the AssemblyScript compiler");
} else if !self.allow_unknown_exports {
bail!("command export '{}' is not a function", export.name());
}
}
Ok(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.
pub fn alias(
&mut self,
module: &str,
name: &str,
as_module: &str,
as_name: &str,
) -> Result<&mut Self> {
let src = self.import_key(module, Some(name));
let dst = self.import_key(as_module, Some(as_name));
match self.map.get(&src).cloned() {
Some(item) => self.insert(dst, item)?,
None => bail!("no item named `{}::{}` defined", module, name),
}
Ok(self)
}
/// 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.
pub fn alias_module(&mut self, module: &str, as_module: &str) -> Result<()> {
let module = self.intern_str(module);
let as_module = self.intern_str(as_module);
let items = self
.map
.iter()
.filter(|(key, _def)| key.module == module)
.map(|(key, def)| (key.name, def.clone()))
.collect::<Vec<_>>();
for (name, item) in items {
self.insert(
ImportKey {
module: as_module,
name,
},
item,
)?;
}
Ok(())
}
fn insert(&mut self, key: ImportKey, item: Definition) -> Result<()> {
match self.map.entry(key) {
Entry::Occupied(_) if !self.allow_shadowing => {
let module = &self.strings[key.module];
let desc = match self.strings.get(key.name) {
Some(name) => format!("{}::{}", module, name),
None => module.to_string(),
};
bail!("import of `{}` defined twice", desc)
}
Entry::Occupied(mut o) => {
o.insert(item);
}
Entry::Vacant(v) => {
v.insert(item);
}
}
Ok(())
}
fn import_key(&mut self, module: &str, name: Option<&str>) -> ImportKey {
ImportKey {
module: self.intern_str(module),
name: name
.map(|name| self.intern_str(name))
.unwrap_or(usize::max_value()),
}
}
fn intern_str(&mut self, string: &str) -> usize {
if let Some(idx) = self.string2idx.get(string) {
return *idx;
}
let string: Arc<str> = string.into();
let idx = self.strings.len();
self.strings.push(string.clone());
self.string2idx.insert(string, idx);
idx
}
/// 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
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// # let mut store = Store::new(&engine, ());
/// 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)?;
/// # Ok(())
/// # }
/// ```
pub fn instantiate(
&self,
mut store: impl AsContextMut<Data = T>,
module: &Module,
) -> Result<Instance> {
self.instantiate_pre(&mut store, module)?.instantiate(store)
}
/// Attempts to instantiate the `module` provided. This is the same as
/// [`Linker::instantiate`], except for async `Store`s.
#[cfg(feature = "async")]
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
pub async fn instantiate_async(
&self,
mut store: impl AsContextMut<Data = T>,
module: &Module,
) -> Result<Instance>
where
T: Send,
{
self.instantiate_pre(&mut store, module)?
.instantiate_async(store)
.await
}
/// 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
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// # let mut store = Store::new(&engine, ());
/// 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)?;
/// # Ok(())
/// # }
/// ```
pub fn instantiate_pre(
&self,
mut store: impl AsContextMut<Data = T>,
module: &Module,
) -> Result<InstancePre<T>> {
let store = store.as_context_mut().0;
let imports = module
.imports()
.map(|import| self._get_by_import(&import))
.collect::<Result<_>>()?;
unsafe { InstancePre::new(store, module, imports) }
}
/// 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.
pub fn iter<'a: 'p, 'p>(
&'a self,
mut store: impl AsContextMut<Data = T> + 'p,
) -> impl Iterator<Item = (&str, &str, Extern)> + 'p {
self.map.iter().map(move |(key, item)| {
let store = store.as_context_mut();
(
&*self.strings[key.module],
&*self.strings[key.name],
// Should be safe since `T` is connecting the linker and store
unsafe { item.to_extern(store.0) },
)
})
}
/// Looks up a previously defined value in this [`Linker`], identified by
/// the names provided.
///
/// Returns `None` if this name was not previously defined in this
/// [`Linker`].
///
/// # Panics
///
/// This function will panic if the `store` provided does not come from the
/// same [`Engine`] that this linker was created with.
pub fn get(
&self,
mut store: impl AsContextMut<Data = T>,
module: &str,
name: Option<&str>,
) -> Option<Extern> {
let store = store.as_context_mut().0;
// Should be safe since `T` is connecting the linker and store
Some(unsafe { self._get(module, name)?.to_extern(store) })
}
fn _get(&self, module: &str, name: Option<&str>) -> Option<&Definition> {
let key = ImportKey {
module: *self.string2idx.get(module)?,
name: match name {
Some(name) => *self.string2idx.get(name)?,
None => usize::max_value(),
},
};
self.map.get(&key)
}
/// Looks up a value in this `Linker` which matches the `import` type
/// provided.
///
/// Returns `None` if no match was found.
///
/// # Panics
///
/// This function will panic if the `store` provided does not come from the
/// same [`Engine`] that this linker was created with.
pub fn get_by_import(
&self,
mut store: impl AsContextMut<Data = T>,
import: &ImportType,
) -> Option<Extern> {
let store = store.as_context_mut().0;
// Should be safe since `T` is connecting the linker and store
Some(unsafe { self._get_by_import(import).ok()?.to_extern(store) })
}
fn _get_by_import(&self, import: &ImportType) -> anyhow::Result<Definition> {
fn undef_err(missing_import: &str) -> anyhow::Error {
anyhow!("unknown import: `{}` has not been defined", missing_import)
}
if let Some(item) = self._get(import.module(), import.name()) {
return Ok(item.clone());
}
if let Some(name) = import.name() {
return Err(undef_err(&format!("{}::{}", import.module(), name)));
}
if let ExternType::Instance(t) = import.ty() {
// This is a key location where the module linking proposal is
// implemented. This logic allows single-level imports of an instance to
// get satisfied by multiple definitions of items within this `Linker`.
//
// The instance being import is iterated over to load the names from
// this `Linker` (recursively calling `get`). If anything isn't defined
// we return `None` since the entire value isn't defined. Otherwise when
// all values are loaded it's assembled into an `Instance` and
// returned`.
//
// Note that this isn't exactly the speediest implementation in the
// world. Ideally we would pre-create the `Instance` instead of creating
// it each time a module is instantiated. For now though while the
// module linking proposal is under development this should hopefully
// suffice.
let mut map = indexmap::IndexMap::new();
for export in t.exports() {
let item = self
._get(import.module(), Some(export.name()))
.ok_or_else(|| undef_err(&format!("{}::{}", import.module(), export.name())))?;
map.insert(export.name().to_string(), item.clone());
}
return Ok(Definition::Instance(Arc::new(map)));
}
Err(undef_err(&import.module()))
}
/// 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.
pub fn get_default(
&self,
mut store: impl AsContextMut<Data = T>,
module: &str,
) -> Result<Func> {
if let Some(external) = self.get(&mut store, module, Some("")) {
if let Extern::Func(func) = external {
return Ok(func.clone());
}
bail!("default export in '{}' is not a function", module);
}
// For compatibility, also recognize "_start".
if let Some(external) = self.get(&mut store, module, Some("_start")) {
if let Extern::Func(func) = external {
return Ok(func.clone());
}
bail!("`_start` in '{}' is not a function", module);
}
// Otherwise return a no-op function.
Ok(Func::wrap(store, || {}))
}
}
impl<T> Default for Linker<T> {
fn default() -> Linker<T> {
Linker::new(&Engine::default())
}
}
impl Definition {
/// Note the unsafety here is due to calling `HostFunc::to_func`. The
/// requirement here is that the `T` that was originally used to create the
/// `HostFunc` matches the `T` on the store.
pub(crate) unsafe fn to_extern(&self, store: &mut StoreOpaque) -> Extern {
match self {
Definition::Extern(e) => e.clone(),
Definition::HostFunc(func) => func.to_func(store).into(),
Definition::Instance(i) => {
let items = Arc::new(
i.iter()
.map(|(name, item)| (name.clone(), item.to_extern(store)))
.collect(),
);
Instance::from_wasmtime(InstanceData::Synthetic(items), store).into()
}
}
}
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
match self {
Definition::Extern(e) => e.comes_from_same_store(store),
Definition::HostFunc(_func) => true,
Definition::Instance(i) => i.values().all(|e| e.comes_from_same_store(store)),
}
}
}
/// Modules can be interpreted either as Commands or Reactors.
enum ModuleKind {
/// The instance is a Command, meaning an instance is created for each
/// exported function and lives for the duration of the function call.
Command,
/// The instance is a Reactor, meaning one instance is created which
/// may live across multiple calls.
Reactor,
}
impl ModuleKind {
/// Determine whether the given module is a Command or a Reactor.
fn categorize(module: &Module) -> Result<ModuleKind> {
let command_start = module.get_export("_start");
let reactor_start = module.get_export("_initialize");
match (command_start, reactor_start) {
(Some(command_start), None) => {
if let Some(_) = command_start.func() {
Ok(ModuleKind::Command)
} else {
bail!("`_start` must be a function")
}
}
(None, Some(reactor_start)) => {
if let Some(_) = reactor_start.func() {
Ok(ModuleKind::Reactor)
} else {
bail!("`_initialize` must be a function")
}
}
(None, None) => {
// Module declares neither of the recognized functions, so treat
// it as a reactor with no initialization function.
Ok(ModuleKind::Reactor)
}
(Some(_), Some(_)) => {
// Module declares itself to be both a Command and a Reactor.
bail!("Program cannot be both a Command and a Reactor")
}
}
}
}