Files
wasmtime/crates/api/src/instance.rs
Dan Gohman 9364eb1d98 Refactor (#1524)
* Compute instance exports on demand.

Instead having instances eagerly compute a Vec of Externs, and bumping
the refcount for each Extern, compute Externs on demand.

This also enables `Instance::get_export` to avoid doing a linear search.

This also means that the closure returned by `get0` and friends now
holds an `InstanceHandle` to dynamically hold the instance live rather
than being scoped to a lifetime.

* Compute module imports and exports on demand too.

And compute Extern::ty on demand too.

* Add a utility function for computing an ExternType.

* Add a utility function for looking up a function's signature.

* Add a utility function for computing the ValType of a Global.

* Rename wasmtime_environ::Export to EntityIndex.

This helps differentiate it from other Export types in the tree, and
describes what it is.

* Fix a typo in a comment.

* Simplify module imports and exports.

* Make `Instance::exports` return the export names.

This significantly simplifies the public API, as it's relatively common
to need the names, and this avoids the need to do a zip with
`Module::exports`.

This also changes `ImportType` and `ExportType` to have public members
instead of private members and accessors, as I find that simplifies the
usage particularly in cases where there are temporary instances.

* Remove `Instance::module`.

This doesn't quite remove `Instance`'s `module` member, it gets a step
closer.

* Use a InstanceHandle utility function.

* Don't consume self in the `Func::get*` methods.

Instead, just create a closure containing the instance handle and the
export for them to call.

* Use `ExactSizeIterator` to avoid needing separate `num_*` methods.

* Rename `Extern::func()` etc. to `into_func()` etc.

* Revise examples to avoid using `nth`.

* Add convenience methods to instance for getting specific extern types.

* Use the convenience functions in more tests and examples.

* Avoid cloning strings for `ImportType` and `ExportType`.

* Remove more obviated clone() calls.

* Simplify `Func`'s closure state.

* Make wasmtime::Export's fields private.

This makes them more consistent with ExportType.

* Fix compilation error.

* Make a lifetime parameter explicit, and use better lifetime names.

Instead of 'me, use 'instance and 'module to make it clear what the
lifetime is.

* More lifetime cleanups.
2020-04-20 15:55:33 -05:00

229 lines
8.4 KiB
Rust

use crate::externals::{Export, Extern, Global, Memory, Table};
use crate::func::Func;
use crate::module::Module;
use crate::runtime::{Config, Store};
use crate::trap::Trap;
use anyhow::{bail, Error, Result};
use std::any::Any;
use wasmtime_jit::{CompiledModule, Resolver};
use wasmtime_runtime::{InstanceHandle, InstantiationError, SignatureRegistry};
struct SimpleResolver<'a> {
imports: &'a [Extern],
}
impl Resolver for SimpleResolver<'_> {
fn resolve(&mut self, idx: u32, _name: &str, _field: &str) -> Option<wasmtime_runtime::Export> {
self.imports
.get(idx as usize)
.map(|i| i.get_wasmtime_export())
}
}
fn instantiate(
config: &Config,
compiled_module: &CompiledModule,
imports: &[Extern],
sig_registry: &SignatureRegistry,
host: Box<dyn Any>,
) -> Result<InstanceHandle, Error> {
let mut resolver = SimpleResolver { imports };
unsafe {
let instance = compiled_module
.instantiate(
config.validating_config.operator_config.enable_bulk_memory,
&mut resolver,
sig_registry,
config.memory_creator.as_ref().map(|a| a as _),
host,
)
.map_err(|e| -> Error {
match e {
InstantiationError::StartTrap(trap) | InstantiationError::Trap(trap) => {
Trap::from_jit(trap).into()
}
other => other.into(),
}
})?;
Ok(instance)
}
}
/// An instantiated WebAssembly module.
///
/// This type represents the instantiation of a [`Module`]. Once instantiated
/// you can access the [`exports`](Instance::exports) which are of type
/// [`Extern`] and provide the ability to call functions, set globals, read
/// memory, etc. This is where all the fun stuff happens!
///
/// An [`Instance`] is created from two inputs, a [`Module`] and a list of
/// imports, provided as a list of [`Extern`] values. The [`Module`] is the wasm
/// code that was compiled and we're instantiating, and the [`Extern`] imports
/// are how we're satisfying the imports of the module provided. On successful
/// instantiation an [`Instance`] will automatically invoke the wasm `start`
/// function.
///
/// When interacting with any wasm code you'll want to make an [`Instance`] to
/// call any code or execute anything!
#[derive(Clone)]
pub struct Instance {
pub(crate) instance_handle: InstanceHandle,
module: Module,
}
impl Instance {
/// Creates a new [`Instance`] from the previously compiled [`Module`] and
/// list of `imports` specified.
///
/// This method instantiates the `module` provided with the `imports`,
/// following the procedure in the [core specification][inst] to
/// instantiate. Instantiation can fail for a number of reasons (many
/// specified below), but if successful the `start` function will be
/// automatically run (if provided) and then the [`Instance`] will be
/// returned.
///
/// ## Providing Imports
///
/// The `imports` array here is a bit tricky. The entries in the list of
/// `imports` are intended to correspond 1:1 with the list of imports
/// returned by [`Module::imports`]. Before calling [`Instance::new`] you'll
/// want to inspect the return value of [`Module::imports`] and, for each
/// import type, create an [`Extern`] which corresponds to that type.
/// These [`Extern`] values are all then collected into a list and passed to
/// this function.
///
/// Note that this function is intentionally relatively low level. It is the
/// intention that we'll soon provide a [higher level API][issue] which will
/// be much more ergonomic for instantiating modules. If you need the full
/// power of customization of imports, though, this is the method for you!
///
/// ## Errors
///
/// This function can fail for a number of reasons, including, but not
/// limited to:
///
/// * The number of `imports` provided doesn't match the number of imports
/// returned by the `module`'s [`Module::imports`] method.
/// * The type of any [`Extern`] doesn't match the corresponding
/// [`ExternType`] entry that it maps to.
/// * The `start` function in the instance, if present, traps.
/// * Module/instance resource limits are exceeded.
///
/// When instantiation fails it's recommended to inspect the return value to
/// see why it failed, or bubble it upwards. If you'd like to specifically
/// check for trap errors, you can use `error.downcast::<Trap>()`.
///
/// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation
/// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727
/// [`ExternType`]: crate::ExternType
pub fn new(module: &Module, imports: &[Extern]) -> Result<Instance, Error> {
let store = module.store();
// For now we have a restriction that the `Store` that we're working
// with is the same for everything involved here.
for import in imports {
if !import.comes_from_same_store(store) {
bail!("cross-`Store` instantiation is not currently supported");
}
}
if imports.len() != module.imports().len() {
bail!(
"wrong number of imports provided, {} != {}",
imports.len(),
module.imports().len()
);
}
let info = module.register_frame_info();
let config = store.engine().config();
let instance_handle = instantiate(
config,
module.compiled_module(),
imports,
store.compiler().signatures(),
Box::new(info),
)?;
Ok(Instance {
instance_handle,
module: module.clone(),
})
}
/// Returns the associated [`Store`] that this `Instance` is compiled into.
///
/// This is the [`Store`] that generally serves as a sort of global cache
/// for various instance-related things.
pub fn store(&self) -> &Store {
self.module.store()
}
/// Returns the list of exported items from this [`Instance`].
pub fn exports<'instance>(
&'instance self,
) -> impl ExactSizeIterator<Item = Export<'instance>> + 'instance {
let instance_handle = &self.instance_handle;
let store = self.module.store();
self.instance_handle
.exports()
.map(move |(name, entity_index)| {
let export = instance_handle.lookup_by_declaration(entity_index);
let extern_ = Extern::from_wasmtime_export(export, store, instance_handle.clone());
Export::new(name, extern_)
})
}
/// Looks up an exported [`Extern`] value by name.
///
/// This method will search the module for an export named `name` and return
/// the value, if found.
///
/// Returns `None` if there was no export named `name`.
pub fn get_export(&self, name: &str) -> Option<Extern> {
let export = self.instance_handle.lookup(&name)?;
Some(Extern::from_wasmtime_export(
export,
self.module.store(),
self.instance_handle.clone(),
))
}
/// Looks up an exported [`Func`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a function.
pub fn get_func(&self, name: &str) -> Option<Func> {
self.get_export(name)?.into_func()
}
/// Looks up an exported [`Table`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a table.
pub fn get_table(&self, name: &str) -> Option<Table> {
self.get_export(name)?.into_table()
}
/// Looks up an exported [`Memory`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a memory.
pub fn get_memory(&self, name: &str) -> Option<Memory> {
self.get_export(name)?.into_memory()
}
/// Looks up an exported [`Global`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a global.
pub fn get_global(&self, name: &str) -> Option<Global> {
self.get_export(name)?.into_global()
}
#[doc(hidden)]
pub fn handle(&self) -> &InstanceHandle {
&self.instance_handle
}
}