* 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.
This commit is contained in:
Dan Gohman
2020-04-20 13:55:33 -07:00
committed by GitHub
parent 967827f4b5
commit 9364eb1d98
57 changed files with 788 additions and 875 deletions

View File

@@ -34,7 +34,7 @@ impl Extern {
/// Returns the underlying `Func`, if this external is a function.
///
/// Returns `None` if this is not a function.
pub fn func(&self) -> Option<&Func> {
pub fn into_func(self) -> Option<Func> {
match self {
Extern::Func(func) => Some(func),
_ => None,
@@ -44,7 +44,7 @@ impl Extern {
/// Returns the underlying `Global`, if this external is a global.
///
/// Returns `None` if this is not a global.
pub fn global(&self) -> Option<&Global> {
pub fn into_global(self) -> Option<Global> {
match self {
Extern::Global(global) => Some(global),
_ => None,
@@ -54,7 +54,7 @@ impl Extern {
/// Returns the underlying `Table`, if this external is a table.
///
/// Returns `None` if this is not a table.
pub fn table(&self) -> Option<&Table> {
pub fn into_table(self) -> Option<Table> {
match self {
Extern::Table(table) => Some(table),
_ => None,
@@ -64,7 +64,7 @@ impl Extern {
/// Returns the underlying `Memory`, if this external is a memory.
///
/// Returns `None` if this is not a memory.
pub fn memory(&self) -> Option<&Memory> {
pub fn into_memory(self) -> Option<Memory> {
match self {
Extern::Memory(memory) => Some(memory),
_ => None,
@@ -74,10 +74,10 @@ impl Extern {
/// Returns the type associated with this `Extern`.
pub fn ty(&self) -> ExternType {
match self {
Extern::Func(ft) => ExternType::Func(ft.ty().clone()),
Extern::Memory(ft) => ExternType::Memory(ft.ty().clone()),
Extern::Table(tt) => ExternType::Table(tt.ty().clone()),
Extern::Global(gt) => ExternType::Global(gt.ty().clone()),
Extern::Func(ft) => ExternType::Func(ft.ty()),
Extern::Memory(ft) => ExternType::Memory(ft.ty()),
Extern::Table(tt) => ExternType::Table(tt.ty()),
Extern::Global(gt) => ExternType::Global(gt.ty()),
}
}
@@ -91,11 +91,11 @@ impl Extern {
}
pub(crate) fn from_wasmtime_export(
wasmtime_export: wasmtime_runtime::Export,
store: &Store,
instance_handle: InstanceHandle,
export: wasmtime_runtime::Export,
) -> Extern {
match export {
match wasmtime_export {
wasmtime_runtime::Export::Function(f) => {
Extern::Func(Func::from_wasmtime_function(f, store, instance_handle))
}
@@ -164,7 +164,6 @@ impl From<Table> for Extern {
#[derive(Clone)]
pub struct Global {
store: Store,
ty: GlobalType,
wasmtime_export: wasmtime_runtime::ExportGlobal,
wasmtime_handle: InstanceHandle,
}
@@ -191,27 +190,44 @@ impl Global {
let (wasmtime_handle, wasmtime_export) = generate_global_export(store, &ty, val)?;
Ok(Global {
store: store.clone(),
ty,
wasmtime_export,
wasmtime_handle,
})
}
/// Returns the underlying type of this `global`.
pub fn ty(&self) -> &GlobalType {
&self.ty
pub fn ty(&self) -> GlobalType {
// The original export is coming from wasmtime_runtime itself we should
// support all the types coming out of it, so assert such here.
GlobalType::from_wasmtime_global(&self.wasmtime_export.global)
.expect("core wasm global type should be supported")
}
/// Returns the value type of this `global`.
pub fn val_type(&self) -> ValType {
ValType::from_wasmtime_type(self.wasmtime_export.global.ty)
.expect("core wasm type should be supported")
}
/// Returns the underlying mutability of this `global`.
pub fn mutability(&self) -> Mutability {
if self.wasmtime_export.global.mutability {
Mutability::Var
} else {
Mutability::Const
}
}
/// Returns the current [`Val`] of this global.
pub fn get(&self) -> Val {
unsafe {
let definition = &mut *self.wasmtime_export.definition;
match self.ty().content() {
match self.val_type() {
ValType::I32 => Val::from(*definition.as_i32()),
ValType::I64 => Val::from(*definition.as_i64()),
ValType::F32 => Val::F32(*definition.as_u32()),
ValType::F64 => Val::F64(*definition.as_u64()),
_ => unimplemented!("Global::get for {:?}", self.ty().content()),
ty => unimplemented!("Global::get for {:?}", ty),
}
}
}
@@ -223,15 +239,12 @@ impl Global {
/// Returns an error if this global has a different type than `Val`, or if
/// it's not a mutable global.
pub fn set(&self, val: Val) -> Result<()> {
if self.ty().mutability() != Mutability::Var {
if self.mutability() != Mutability::Var {
bail!("immutable global cannot be set");
}
if val.ty() != *self.ty().content() {
bail!(
"global of type {:?} cannot be set to {:?}",
self.ty().content(),
val.ty()
);
let ty = self.val_type();
if val.ty() != ty {
bail!("global of type {:?} cannot be set to {:?}", ty, val.ty());
}
if !val.comes_from_same_store(&self.store) {
bail!("cross-`Store` values are not supported");
@@ -254,13 +267,8 @@ impl Global {
store: &Store,
wasmtime_handle: InstanceHandle,
) -> Global {
// The original export is coming from wasmtime_runtime itself we should
// support all the types coming out of it, so assert such here.
let ty = GlobalType::from_wasmtime_global(&wasmtime_export.global)
.expect("core wasm global type should be supported");
Global {
store: store.clone(),
ty: ty,
wasmtime_export,
wasmtime_handle,
}
@@ -285,7 +293,6 @@ impl Global {
#[derive(Clone)]
pub struct Table {
store: Store,
ty: TableType,
wasmtime_handle: InstanceHandle,
wasmtime_export: wasmtime_runtime::ExportTable,
}
@@ -326,7 +333,6 @@ impl Table {
Ok(Table {
store: store.clone(),
ty,
wasmtime_handle,
wasmtime_export,
})
@@ -334,8 +340,8 @@ impl Table {
/// Returns the underlying type of this table, including its element type as
/// well as the maximum/minimum lower bounds.
pub fn ty(&self) -> &TableType {
&self.ty
pub fn ty(&self) -> TableType {
TableType::from_wasmtime_table(&self.wasmtime_export.table.table)
}
fn wasmtime_table_index(&self) -> wasm::DefinedTableIndex {
@@ -368,7 +374,7 @@ impl Table {
/// Returns the current size of this table.
pub fn size(&self) -> u32 {
unsafe { (&*self.wasmtime_export.definition).current_elements }
unsafe { (*self.wasmtime_export.definition).current_elements }
}
/// Grows the size of this table by `delta` more elements, initialization
@@ -432,10 +438,8 @@ impl Table {
store: &Store,
wasmtime_handle: wasmtime_runtime::InstanceHandle,
) -> Table {
let ty = TableType::from_wasmtime_table(&wasmtime_export.table.table);
Table {
store: store.clone(),
ty,
wasmtime_handle,
wasmtime_export,
}
@@ -651,7 +655,6 @@ impl Table {
#[derive(Clone)]
pub struct Memory {
store: Store,
ty: MemoryType,
wasmtime_handle: InstanceHandle,
wasmtime_export: wasmtime_runtime::ExportMemory,
}
@@ -684,7 +687,6 @@ impl Memory {
generate_memory_export(store, &ty).expect("generated memory");
Memory {
store: store.clone(),
ty,
wasmtime_handle,
wasmtime_export,
}
@@ -700,14 +702,14 @@ impl Memory {
/// let store = Store::default();
/// let module = Module::new(&store, "(module (memory (export \"mem\") 1))")?;
/// let instance = Instance::new(&module, &[])?;
/// let memory = instance.get_export("mem").unwrap().memory().unwrap();
/// let memory = instance.get_memory("mem").unwrap();
/// let ty = memory.ty();
/// assert_eq!(ty.limits().min(), 1);
/// # Ok(())
/// # }
/// ```
pub fn ty(&self) -> &MemoryType {
&self.ty
pub fn ty(&self) -> MemoryType {
MemoryType::from_wasmtime_memory(&self.wasmtime_export.memory.memory)
}
/// Returns this memory as a slice view that can be read natively in Rust.
@@ -812,7 +814,7 @@ impl Memory {
/// let store = Store::default();
/// let module = Module::new(&store, "(module (memory (export \"mem\") 1 2))")?;
/// let instance = Instance::new(&module, &[])?;
/// let memory = instance.get_export("mem").unwrap().memory().unwrap();
/// let memory = instance.get_memory("mem").unwrap();
///
/// assert_eq!(memory.size(), 1);
/// assert_eq!(memory.grow(1)?, 1);
@@ -838,10 +840,8 @@ impl Memory {
store: &Store,
wasmtime_handle: wasmtime_runtime::InstanceHandle,
) -> Memory {
let ty = MemoryType::from_wasmtime_memory(&wasmtime_export.memory.memory);
Memory {
store: store.clone(),
ty: ty,
wasmtime_handle,
wasmtime_export,
}
@@ -890,3 +890,66 @@ pub unsafe trait MemoryCreator: Send + Sync {
/// Create new LinearMemory
fn new_memory(&self, ty: MemoryType) -> Result<Box<dyn LinearMemory>, String>;
}
// Exports
/// An exported WebAssembly value.
///
/// This type is primarily accessed from the
/// [`Instance::exports`](crate::Instance::exports) accessor and describes what
/// names and items are exported from a wasm instance.
#[derive(Clone)]
pub struct Export<'instance> {
/// The name of the export.
name: &'instance str,
/// The definition of the export.
definition: Extern,
}
impl<'instance> Export<'instance> {
/// Creates a new export which is exported with the given `name` and has the
/// given `definition`.
pub(crate) fn new(name: &'instance str, definition: Extern) -> Export<'instance> {
Export { name, definition }
}
/// Returns the name by which this export is known.
pub fn name(&self) -> &'instance str {
self.name
}
/// Return the `ExternType` of this export.
pub fn ty(&self) -> ExternType {
self.definition.ty()
}
/// Consume this `Export` and return the contained `Extern`.
pub fn into_extern(self) -> Extern {
self.definition
}
/// Consume this `Export` and return the contained `Func`, if it's a function,
/// or `None` otherwise.
pub fn into_func(self) -> Option<Func> {
self.definition.into_func()
}
/// Consume this `Export` and return the contained `Table`, if it's a table,
/// or `None` otherwise.
pub fn into_table(self) -> Option<Table> {
self.definition.into_table()
}
/// Consume this `Export` and return the contained `Memory`, if it's a memory,
/// or `None` otherwise.
pub fn into_memory(self) -> Option<Memory> {
self.definition.into_memory()
}
/// Consume this `Export` and return the contained `Global`, if it's a global,
/// or `None` otherwise.
pub fn into_global(self) -> Option<Global> {
self.definition.into_global()
}
}

View File

@@ -38,7 +38,7 @@ use wasmtime_runtime::{ExportFunction, VMTrampoline};
/// let store = Store::default();
/// let module = Module::new(&store, r#"(module (func (export "foo")))"#)?;
/// let instance = Instance::new(&module, &[])?;
/// let foo = instance.exports()[0].func().expect("export wasn't a function");
/// let foo = instance.get_func("foo").expect("export wasn't a function");
///
/// // Work with `foo` as a `Func` at this point, such as calling it
/// // dynamically...
@@ -88,7 +88,7 @@ use wasmtime_runtime::{ExportFunction, VMTrampoline};
/// "#,
/// )?;
/// let instance = Instance::new(&module, &[add.into()])?;
/// let call_add_twice = instance.exports()[0].func().expect("export wasn't a function");
/// let call_add_twice = instance.get_func("call_add_twice").expect("export wasn't a function");
/// let call_add_twice = call_add_twice.get0::<i32>()?;
///
/// assert_eq!(call_add_twice()?, 10);
@@ -138,7 +138,6 @@ pub struct Func {
store: Store,
instance: InstanceHandle,
export: ExportFunction,
ty: FuncType,
trampoline: VMTrampoline,
}
@@ -149,15 +148,16 @@ macro_rules! getters {
)*) => ($(
$(#[$doc])*
#[allow(non_snake_case)]
pub fn $name<'a, $($args,)* R>(&'a self)
-> anyhow::Result<impl Fn($($args,)*) -> Result<R, Trap> + 'a>
pub fn $name<$($args,)* R>(&self)
-> anyhow::Result<impl Fn($($args,)*) -> Result<R, Trap>>
where
$($args: WasmTy,)*
R: WasmTy,
{
// Verify all the paramers match the expected parameters, and that
// there are no extra parameters...
let mut params = self.ty().params().iter().cloned();
let ty = self.ty();
let mut params = ty.params().iter().cloned();
let n = 0;
$(
let n = n + 1;
@@ -167,14 +167,18 @@ macro_rules! getters {
ensure!(params.next().is_none(), "Type mismatch: too many arguments (expected {})", n);
// ... then do the same for the results...
let mut results = self.ty().results().iter().cloned();
let mut results = ty.results().iter().cloned();
R::matches(&mut results)
.context("Type mismatch in return type")?;
ensure!(results.next().is_none(), "Type mismatch: too many return values (expected 1)");
// Pass the instance into the closure so that we keep it live for the lifetime
// of the closure. Pass the export in so that we can call it.
let instance = self.instance.clone();
let export = self.export.clone();
// ... and then once we've passed the typechecks we can hand out our
// object since our `transmute` below should be safe!
let f = self.wasmtime_function();
Ok(move |$($args: $args),*| -> Result<R, Trap> {
unsafe {
let fnptr = mem::transmute::<
@@ -184,12 +188,17 @@ macro_rules! getters {
*mut VMContext,
$($args,)*
) -> R,
>(f.address);
>(export.address);
let mut ret = None;
$(let $args = $args.into_abi();)*
wasmtime_runtime::catch_traps(f.vmctx, || {
ret = Some(fnptr(f.vmctx, ptr::null_mut(), $($args,)*));
wasmtime_runtime::catch_traps(export.vmctx, || {
ret = Some(fnptr(export.vmctx, ptr::null_mut(), $($args,)*));
}).map_err(Trap::from_jit)?;
// We're holding this handle just to ensure that the instance stays
// live while we call into it.
drop(&instance);
Ok(ret.unwrap())
}
})
@@ -272,7 +281,6 @@ impl Func {
crate::trampoline::generate_func_export(&ty, func, store).expect("generated func");
Func {
store: store.clone(),
ty,
instance,
export,
trampoline,
@@ -340,7 +348,7 @@ impl Func {
/// "#,
/// )?;
/// let instance = Instance::new(&module, &[add.into()])?;
/// let foo = instance.exports()[0].func().unwrap().get2::<i32, i32, i32>()?;
/// let foo = instance.get_func("foo").unwrap().get2::<i32, i32, i32>()?;
/// assert_eq!(foo(1, 2)?, 3);
/// # Ok(())
/// # }
@@ -371,7 +379,7 @@ impl Func {
/// "#,
/// )?;
/// let instance = Instance::new(&module, &[add.into()])?;
/// let foo = instance.exports()[0].func().unwrap().get2::<i32, i32, i32>()?;
/// let foo = instance.get_func("foo").unwrap().get2::<i32, i32, i32>()?;
/// assert_eq!(foo(1, 2)?, 3);
/// assert!(foo(i32::max_value(), 1).is_err());
/// # Ok(())
@@ -404,7 +412,7 @@ impl Func {
/// "#,
/// )?;
/// let instance = Instance::new(&module, &[debug.into()])?;
/// let foo = instance.exports()[0].func().unwrap().get0::<()>()?;
/// let foo = instance.get_func("foo").unwrap().get0::<()>()?;
/// foo()?;
/// # Ok(())
/// # }
@@ -460,7 +468,7 @@ impl Func {
/// "#,
/// )?;
/// let instance = Instance::new(&module, &[log_str.into()])?;
/// let foo = instance.exports()[0].func().unwrap().get0::<()>()?;
/// let foo = instance.get_func("foo").unwrap().get0::<()>()?;
/// foo()?;
/// # Ok(())
/// # }
@@ -470,18 +478,42 @@ impl Func {
}
/// Returns the underlying wasm type that this `Func` has.
pub fn ty(&self) -> &FuncType {
&self.ty
pub fn ty(&self) -> FuncType {
// Signatures should always be registered in the store's registry of
// shared signatures, so we should be able to unwrap safely here.
let sig = self
.store
.compiler()
.signatures()
.lookup(self.export.signature)
.expect("failed to lookup signature");
// This is only called with `Export::Function`, and since it's coming
// from wasmtime_runtime itself we should support all the types coming
// out of it, so assert such here.
FuncType::from_wasmtime_signature(&sig).expect("core wasm signature should be supported")
}
/// Returns the number of parameters that this function takes.
pub fn param_arity(&self) -> usize {
self.ty.params().len()
let sig = self
.store
.compiler()
.signatures()
.lookup(self.export.signature)
.expect("failed to lookup signature");
sig.params.len()
}
/// Returns the number of results this function produces.
pub fn result_arity(&self) -> usize {
self.ty.results().len()
let sig = self
.store
.compiler()
.signatures()
.lookup(self.export.signature)
.expect("failed to lookup signature");
sig.returns.len()
}
/// Invokes this function with the `params` given, returning the results and
@@ -499,18 +531,19 @@ impl Func {
// this function. This involves checking to make sure we have the right
// number and types of arguments as well as making sure everything is
// from the same `Store`.
if self.ty.params().len() != params.len() {
let my_ty = self.ty();
if my_ty.params().len() != params.len() {
bail!(
"expected {} arguments, got {}",
self.ty.params().len(),
my_ty.params().len(),
params.len()
);
}
let mut values_vec = vec![0; max(params.len(), self.ty.results().len())];
let mut values_vec = vec![0; max(params.len(), my_ty.results().len())];
// Store the argument values into `values_vec`.
let param_tys = self.ty.params().iter();
let param_tys = my_ty.params().iter();
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
if arg.ty() != *ty {
bail!("argument type mismatch");
@@ -538,8 +571,8 @@ impl Func {
}
// Load the return values out of `values_vec`.
let mut results = Vec::with_capacity(self.ty.results().len());
for (index, ty) in self.ty.results().iter().enumerate() {
let mut results = Vec::with_capacity(my_ty.results().len());
for (index, ty) in my_ty.results().iter().enumerate() {
unsafe {
let ptr = values_vec.as_ptr().add(index);
results.push(Val::read_value_from(ptr, ty));
@@ -558,20 +591,6 @@ impl Func {
store: &Store,
instance: InstanceHandle,
) -> Self {
// Signatures should always be registered in the store's registry of
// shared signatures, so we should be able to unwrap safely here.
let sig = store
.compiler()
.signatures()
.lookup(export.signature)
.expect("failed to lookup signature");
// This is only called with `Export::Function`, and since it's coming
// from wasmtime_runtime itself we should support all the types coming
// out of it, so assert such here.
let ty = FuncType::from_wasmtime_signature(sig)
.expect("core wasm signature should be supported");
// Each function signature in a module should have a trampoline stored
// on that module as well, so unwrap the result here since otherwise
// it's a bug in wasmtime.
@@ -583,7 +602,6 @@ impl Func {
instance,
export,
trampoline,
ty,
store: store.clone(),
}
}
@@ -1095,7 +1113,6 @@ macro_rules! impl_into_func {
.expect("failed to generate export");
Func {
store: store.clone(),
ty,
instance,
export,
trampoline,

View File

@@ -1,18 +1,19 @@
use crate::externals::Extern;
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::{Export, InstanceHandle, InstantiationError, SignatureRegistry};
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<Export> {
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())
@@ -68,7 +69,6 @@ fn instantiate(
pub struct Instance {
pub(crate) instance_handle: InstanceHandle,
module: Module,
exports: Box<[Extern]>,
}
impl Instance {
@@ -145,20 +145,9 @@ impl Instance {
Box::new(info),
)?;
let mut exports = Vec::with_capacity(module.exports().len());
for export in module.exports() {
let name = export.name().to_string();
let export = instance_handle.lookup(&name).expect("export");
exports.push(Extern::from_wasmtime_export(
store,
instance_handle.clone(),
export,
));
}
Ok(Instance {
instance_handle,
module: module.clone(),
exports: exports.into_boxed_slice(),
})
}
@@ -170,24 +159,19 @@ impl Instance {
self.module.store()
}
/// Returns the associated [`Module`] that this `Instance` instantiated.
///
/// The corresponding [`Module`] here is a static version of this `Instance`
/// which can be used to learn information such as naming information about
/// various functions.
pub fn module(&self) -> &Module {
&self.module
}
/// Returns the list of exported items from this [`Instance`].
///
/// Note that the exports here do not have names associated with them,
/// they're simply the values that are exported. To learn the value of each
/// export you'll need to consult [`Module::exports`]. The list returned
/// here maps 1:1 with the list that [`Module::exports`] returns, and
/// [`ExportType`](crate::ExportType) contains the name of each export.
pub fn exports(&self) -> &[Extern] {
&self.exports
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.
@@ -196,14 +180,45 @@ impl Instance {
/// the value, if found.
///
/// Returns `None` if there was no export named `name`.
pub fn get_export(&self, name: &str) -> Option<&Extern> {
let (i, _) = self
.module
.exports()
.iter()
.enumerate()
.find(|(_, e)| e.name() == name)?;
Some(&self.exports()[i])
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)]

View File

@@ -166,7 +166,7 @@ impl Linker {
if !item.comes_from_same_store(&self.store) {
bail!("all linker items must be from the same store");
}
self.insert(module, name, &item.ty(), item)?;
self.insert(module, name, item)?;
Ok(self)
}
@@ -264,8 +264,8 @@ impl Linker {
if !Store::same(&self.store, instance.store()) {
bail!("all linker items must be from the same store");
}
for (export, item) in instance.module().exports().iter().zip(instance.exports()) {
self.insert(module_name, export.name(), export.ty(), item.clone())?;
for export in instance.exports() {
self.insert(module_name, export.name(), export.into_extern())?;
}
Ok(self)
}
@@ -283,7 +283,7 @@ impl Linker {
let items = self
.iter()
.filter(|(m, _, _)| *m == module)
.map(|(_, name, item)| (name.to_string(), item.clone()))
.map(|(_, name, item)| (name.to_string(), item))
.collect::<Vec<_>>();
for (name, item) in items {
self.define(as_module, &name, item)?;
@@ -291,8 +291,8 @@ impl Linker {
Ok(())
}
fn insert(&mut self, module: &str, name: &str, ty: &ExternType, item: Extern) -> Result<()> {
let key = self.import_key(module, name, ty);
fn insert(&mut self, module: &str, name: &str, item: Extern) -> Result<()> {
let key = self.import_key(module, name, item.ty());
match self.map.entry(key) {
Entry::Occupied(o) if !self.allow_shadowing => bail!(
"import of `{}::{}` with kind {:?} defined twice",
@@ -310,7 +310,7 @@ impl Linker {
Ok(())
}
fn import_key(&mut self, module: &str, name: &str, ty: &ExternType) -> ImportKey {
fn import_key(&mut self, module: &str, name: &str, ty: ExternType) -> ImportKey {
ImportKey {
module: self.intern_str(module),
name: self.intern_str(name),
@@ -318,10 +318,10 @@ impl Linker {
}
}
fn import_kind(&self, ty: &ExternType) -> ImportKind {
fn import_kind(&self, ty: ExternType) -> ImportKind {
match ty {
ExternType::Func(f) => ImportKind::Func(f.clone()),
ExternType::Global(f) => ImportKind::Global(f.clone()),
ExternType::Func(f) => ImportKind::Func(f),
ExternType::Global(f) => ImportKind::Global(f),
ExternType::Memory(_) => ImportKind::Memory,
ExternType::Table(_) => ImportKind::Table,
}
@@ -378,8 +378,8 @@ impl Linker {
pub fn instantiate(&self, module: &Module) -> Result<Instance> {
let mut imports = Vec::new();
for import in module.imports() {
if let Some(item) = self.get(import) {
imports.push(item.clone());
if let Some(item) = self.get(&import) {
imports.push(item);
continue;
}
@@ -429,23 +429,27 @@ impl Linker {
///
/// Note that multiple `Extern` items may be defined for the same
/// module/name pair.
pub fn iter(&self) -> impl Iterator<Item = (&str, &str, &Extern)> {
self.map
.iter()
.map(move |(key, item)| (&*self.strings[key.module], &*self.strings[key.name], item))
pub fn iter(&self) -> impl Iterator<Item = (&str, &str, Extern)> {
self.map.iter().map(move |(key, item)| {
(
&*self.strings[key.module],
&*self.strings[key.name],
item.clone(),
)
})
}
/// Looks up a value in this `Linker` which matches the `import` type
/// provided.
///
/// Returns `None` if no match was found.
pub fn get(&self, import: &ImportType) -> Option<&Extern> {
pub fn get(&self, import: &ImportType) -> Option<Extern> {
let key = ImportKey {
module: *self.string2idx.get(import.module())?,
name: *self.string2idx.get(import.name())?,
kind: self.import_kind(import.ty()),
};
self.map.get(&key)
self.map.get(&key).cloned()
}
/// Returns all items defined for the `module` and `name` pair.
@@ -468,10 +472,10 @@ impl Linker {
/// Returns the single item defined for the `module` and `name` pair.
///
/// Unlike the similar [`Linker::get_by_name`] method this function returns
/// a single `&Extern` item. If the `module` and `name` pair isn't defined
/// a single `Extern` item. If the `module` and `name` pair isn't defined
/// in this linker then an error is returned. If more than one value exists
/// for the `module` and `name` pairs, then an error is returned as well.
pub fn get_one_by_name(&self, module: &str, name: &str) -> Result<&Extern> {
pub fn get_one_by_name(&self, module: &str, name: &str) -> Result<Extern> {
let mut items = self.get_by_name(module, name);
let ret = items
.next()
@@ -479,6 +483,6 @@ impl Linker {
if items.next().is_some() {
bail!("too many items named `{}` in `{}`", name, module);
}
Ok(ret)
Ok(ret.clone())
}
}

View File

@@ -1,66 +1,12 @@
use crate::frame_info::GlobalFrameInfoRegistration;
use crate::runtime::Store;
use crate::types::{
ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability,
TableType, ValType,
};
use anyhow::{bail, Error, Result};
use crate::types::{EntityType, ExportType, ImportType};
use anyhow::{Error, Result};
use std::path::Path;
use std::sync::{Arc, Mutex};
use wasmparser::{validate, ExternalKind, ImportSectionEntryType, ModuleReader, SectionCode};
use wasmparser::validate;
use wasmtime_jit::CompiledModule;
fn into_memory_type(mt: wasmparser::MemoryType) -> Result<MemoryType> {
if mt.shared {
bail!("shared memories are not supported yet");
}
Ok(MemoryType::new(Limits::new(
mt.limits.initial,
mt.limits.maximum,
)))
}
fn into_global_type(gt: wasmparser::GlobalType) -> GlobalType {
let mutability = if gt.mutable {
Mutability::Var
} else {
Mutability::Const
};
GlobalType::new(into_valtype(&gt.content_type), mutability)
}
// `into_valtype` is used for `map` which requires `&T`.
#[allow(clippy::trivially_copy_pass_by_ref)]
fn into_valtype(ty: &wasmparser::Type) -> ValType {
use wasmparser::Type::*;
match ty {
I32 => ValType::I32,
I64 => ValType::I64,
F32 => ValType::F32,
F64 => ValType::F64,
V128 => ValType::V128,
AnyFunc => ValType::FuncRef,
AnyRef => ValType::AnyRef,
_ => unimplemented!("types in into_valtype"),
}
}
fn into_func_type(mt: wasmparser::FuncType) -> FuncType {
assert_eq!(mt.form, wasmparser::Type::Func);
let params = mt.params.iter().map(into_valtype).collect::<Vec<_>>();
let returns = mt.returns.iter().map(into_valtype).collect::<Vec<_>>();
FuncType::new(params.into_boxed_slice(), returns.into_boxed_slice())
}
fn into_table_type(tt: wasmparser::TableType) -> TableType {
assert!(
tt.element_type == wasmparser::Type::AnyFunc || tt.element_type == wasmparser::Type::AnyRef
);
let ty = into_valtype(&tt.element_type);
let limits = Limits::new(tt.limits.initial, tt.limits.maximum);
TableType::new(ty, limits)
}
/// A compiled WebAssembly module, ready to be instantiated.
///
/// A `Module` is a compiled in-memory representation of an input WebAssembly
@@ -134,8 +80,6 @@ pub struct Module {
struct ModuleInner {
store: Store,
imports: Box<[ImportType]>,
exports: Box<[ExportType]>,
compiled: CompiledModule,
frame_info_registration: Mutex<Option<Option<Arc<GlobalFrameInfoRegistration>>>>,
}
@@ -332,9 +276,7 @@ impl Module {
/// be somewhat valid for decoding purposes, and the basics of decoding can
/// still fail.
pub unsafe fn from_binary_unchecked(store: &Store, binary: &[u8]) -> Result<Module> {
let mut ret = Module::compile(store, binary)?;
ret.read_imports_and_exports(binary)?;
Ok(ret)
Module::compile(store, binary)
}
/// Validates `binary` input data as a WebAssembly binary given the
@@ -372,8 +314,6 @@ impl Module {
Ok(Module {
inner: Arc::new(ModuleInner {
store: store.clone(),
imports: Box::new([]),
exports: Box::new([]),
compiled,
frame_info_registration: Mutex::new(None),
}),
@@ -451,7 +391,7 @@ impl Module {
/// "#;
/// let module = Module::new(&store, wat)?;
/// assert_eq!(module.imports().len(), 1);
/// let import = &module.imports()[0];
/// let import = module.imports().next().unwrap();
/// assert_eq!(import.module(), "host");
/// assert_eq!(import.name(), "foo");
/// match import.ty() {
@@ -461,8 +401,17 @@ impl Module {
/// # Ok(())
/// # }
/// ```
pub fn imports(&self) -> &[ImportType] {
&self.inner.imports
pub fn imports<'module>(
&'module self,
) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module {
let module = self.inner.compiled.module_ref();
module
.imports
.iter()
.map(move |(module_name, name, entity_index)| {
let r#type = EntityType::new(entity_index, module);
ImportType::new(module_name, name, r#type)
})
}
/// Returns the list of exports that this [`Module`] has and will be
@@ -482,7 +431,7 @@ impl Module {
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let module = Module::new(&store, "(module)")?;
/// assert!(module.exports().is_empty());
/// assert!(module.exports().next().is_none());
/// # Ok(())
/// # }
/// ```
@@ -502,14 +451,15 @@ impl Module {
/// let module = Module::new(&store, wat)?;
/// assert_eq!(module.exports().len(), 2);
///
/// let foo = &module.exports()[0];
/// let mut exports = module.exports();
/// let foo = exports.next().unwrap();
/// assert_eq!(foo.name(), "foo");
/// match foo.ty() {
/// ExternType::Func(_) => { /* ... */ }
/// _ => panic!("unexpected export type!"),
/// }
///
/// let memory = &module.exports()[1];
/// let memory = exports.next().unwrap();
/// assert_eq!(memory.name(), "memory");
/// match memory.ty() {
/// ExternType::Memory(_) => { /* ... */ }
@@ -518,8 +468,14 @@ impl Module {
/// # Ok(())
/// # }
/// ```
pub fn exports(&self) -> &[ExportType] {
&self.inner.exports
pub fn exports<'module>(
&'module self,
) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module {
let module = self.inner.compiled.module_ref();
module.exports.iter().map(move |(name, entity_index)| {
let r#type = EntityType::new(entity_index, module);
ExportType::new(name, r#type)
})
}
/// Returns the [`Store`] that this [`Module`] was compiled into.
@@ -527,141 +483,6 @@ impl Module {
&self.inner.store
}
fn read_imports_and_exports(&mut self, binary: &[u8]) -> Result<()> {
let inner = Arc::get_mut(&mut self.inner).unwrap();
let mut reader = ModuleReader::new(binary)?;
let mut imports = Vec::new();
let mut exports = Vec::new();
let mut memories = Vec::new();
let mut tables = Vec::new();
let mut func_sig = Vec::new();
let mut sigs = Vec::new();
let mut globals = Vec::new();
while !reader.eof() {
let section = reader.read()?;
match section.code {
SectionCode::Memory => {
let section = section.get_memory_section_reader()?;
memories.reserve_exact(section.get_count() as usize);
for entry in section {
memories.push(into_memory_type(entry?)?);
}
}
SectionCode::Type => {
let section = section.get_type_section_reader()?;
sigs.reserve_exact(section.get_count() as usize);
for entry in section {
sigs.push(into_func_type(entry?));
}
}
SectionCode::Function => {
let section = section.get_function_section_reader()?;
func_sig.reserve_exact(section.get_count() as usize);
for entry in section {
func_sig.push(entry?);
}
}
SectionCode::Global => {
let section = section.get_global_section_reader()?;
globals.reserve_exact(section.get_count() as usize);
for entry in section {
globals.push(into_global_type(entry?.ty));
}
}
SectionCode::Table => {
let section = section.get_table_section_reader()?;
tables.reserve_exact(section.get_count() as usize);
for entry in section {
tables.push(into_table_type(entry?))
}
}
SectionCode::Import => {
let section = section.get_import_section_reader()?;
imports.reserve_exact(section.get_count() as usize);
for entry in section {
let entry = entry?;
let r#type = match entry.ty {
ImportSectionEntryType::Function(index) => {
func_sig.push(index);
let sig = &sigs[index as usize];
ExternType::Func(sig.clone())
}
ImportSectionEntryType::Table(tt) => {
let table = into_table_type(tt);
tables.push(table.clone());
ExternType::Table(table)
}
ImportSectionEntryType::Memory(mt) => {
let memory = into_memory_type(mt)?;
memories.push(memory.clone());
ExternType::Memory(memory)
}
ImportSectionEntryType::Global(gt) => {
let global = into_global_type(gt);
globals.push(global.clone());
ExternType::Global(global)
}
};
imports.push(ImportType::new(entry.module, entry.field, r#type));
}
}
SectionCode::Export => {
let section = section.get_export_section_reader()?;
exports.reserve_exact(section.get_count() as usize);
for entry in section {
let entry = entry?;
let r#type = match entry.kind {
ExternalKind::Function => {
let sig_index = func_sig[entry.index as usize] as usize;
let sig = &sigs[sig_index];
ExternType::Func(sig.clone())
}
ExternalKind::Table => {
ExternType::Table(tables[entry.index as usize].clone())
}
ExternalKind::Memory => {
ExternType::Memory(memories[entry.index as usize].clone())
}
ExternalKind::Global => {
ExternType::Global(globals[entry.index as usize].clone())
}
};
exports.push(ExportType::new(entry.field, r#type));
}
}
SectionCode::Custom {
name: "webidl-bindings",
..
}
| SectionCode::Custom {
name: "wasm-interface-types",
..
} => {
bail!(
"\
support for interface types has temporarily been removed from `wasmtime`
for more information about this temoprary you can read on the issue online:
https://github.com/bytecodealliance/wasmtime/issues/1271
and for re-adding support for interface types you can see this issue:
https://github.com/bytecodealliance/wasmtime/issues/677
"
);
}
_ => {
// skip other sections
}
}
}
inner.imports = imports.into();
inner.exports = exports.into();
Ok(())
}
/// Register this module's stack frame information into the global scope.
///
/// This is required to ensure that any traps can be properly symbolicated.

View File

@@ -10,7 +10,7 @@ use std::mem;
use std::panic::{self, AssertUnwindSafe};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::{ir, settings, CompiledFunction, Export, Module};
use wasmtime_environ::{ir, settings, CompiledFunction, EntityIndex, Module};
use wasmtime_jit::trampoline::ir::{
ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind,
};
@@ -212,7 +212,7 @@ pub fn create_handle_with_function(
let pointer_type = isa.pointer_type();
let sig = match ft.get_wasmtime_signature(pointer_type) {
Some(sig) => sig.clone(),
Some(sig) => sig,
None => bail!("not a supported core wasm signature {:?}", ft),
};
@@ -228,7 +228,7 @@ pub fn create_handle_with_function(
let func_id = module.local.functions.push(sig_id);
module
.exports
.insert("trampoline".to_string(), Export::Function(func_id));
.insert("trampoline".to_string(), EntityIndex::Function(func_id));
let trampoline = make_trampoline(isa.as_ref(), &mut code_memory, &mut fn_builder_ctx, &sig);
finished_functions.push(trampoline);
@@ -276,7 +276,7 @@ pub unsafe fn create_handle_with_raw_function(
let pointer_type = isa.pointer_type();
let sig = match ft.get_wasmtime_signature(pointer_type) {
Some(sig) => sig.clone(),
Some(sig) => sig,
None => bail!("not a supported core wasm signature {:?}", ft),
};
@@ -288,7 +288,7 @@ pub unsafe fn create_handle_with_raw_function(
let func_id = module.local.functions.push(sig_id);
module
.exports
.insert("trampoline".to_string(), Export::Function(func_id));
.insert("trampoline".to_string(), EntityIndex::Function(func_id));
finished_functions.push(func);
let sig_id = store.compiler().signatures().register(&sig);
trampolines.insert(sig_id, trampoline);

View File

@@ -3,7 +3,7 @@ use crate::Store;
use crate::{GlobalType, Mutability, Val};
use anyhow::{bail, Result};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, Module};
use wasmtime_environ::{wasm, EntityIndex, Module};
use wasmtime_runtime::InstanceHandle;
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<InstanceHandle> {
@@ -26,10 +26,9 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<Instanc
};
let mut module = Module::new();
let global_id = module.local.globals.push(global);
module.exports.insert(
"global".to_string(),
wasmtime_environ::Export::Global(global_id),
);
module
.exports
.insert("global".to_string(), EntityIndex::Global(global_id));
let handle = create_handle(
module,
store,

View File

@@ -4,7 +4,7 @@ use crate::Store;
use crate::{Limits, MemoryType};
use anyhow::Result;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, MemoryPlan, Module, WASM_PAGE_SIZE};
use wasmtime_environ::{wasm, EntityIndex, MemoryPlan, Module, WASM_PAGE_SIZE};
use wasmtime_runtime::{
InstanceHandle, RuntimeLinearMemory, RuntimeMemoryCreator, VMMemoryDefinition,
};
@@ -23,10 +23,9 @@ pub fn create_handle_with_memory(store: &Store, memory: &MemoryType) -> Result<I
let memory_plan = wasmtime_environ::MemoryPlan::for_memory(memory, &tunable);
let memory_id = module.local.memory_plans.push(memory_plan);
module.exports.insert(
"memory".to_string(),
wasmtime_environ::Export::Memory(memory_id),
);
module
.exports
.insert("memory".to_string(), EntityIndex::Memory(memory_id));
create_handle(
module,

View File

@@ -3,7 +3,7 @@ use crate::Store;
use crate::{TableType, ValType};
use anyhow::{bail, Result};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, Module};
use wasmtime_environ::{wasm, EntityIndex, Module};
use wasmtime_runtime::InstanceHandle;
pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<InstanceHandle> {
@@ -21,10 +21,9 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Inst
let table_plan = wasmtime_environ::TablePlan::for_table(table, &tunable);
let table_id = module.local.table_plans.push(table_plan);
module.exports.insert(
"table".to_string(),
wasmtime_environ::Export::Table(table_id),
);
module
.exports
.insert("table".to_string(), EntityIndex::Table(table_id));
create_handle(
module,

View File

@@ -1,4 +1,5 @@
use wasmtime_environ::{ir, wasm};
use std::fmt;
use wasmtime_environ::{ir, wasm, EntityIndex};
// Type Representations
@@ -247,7 +248,7 @@ impl FuncType {
/// Returns `None` if any types in the signature can't be converted to the
/// types in this crate, but that should very rarely happen and largely only
/// indicate a bug in our cranelift integration.
pub(crate) fn from_wasmtime_signature(signature: ir::Signature) -> Option<FuncType> {
pub(crate) fn from_wasmtime_signature(signature: &ir::Signature) -> Option<FuncType> {
let params = signature
.params
.iter()
@@ -382,6 +383,53 @@ impl MemoryType {
}
}
// Entity Types
#[derive(Clone, Hash, Eq, PartialEq)]
pub(crate) enum EntityType<'module> {
Function(&'module ir::Signature),
Table(&'module wasm::Table),
Memory(&'module wasm::Memory),
Global(&'module wasm::Global),
}
impl<'module> EntityType<'module> {
/// Translate from a `EntityIndex` into an `ExternType`.
pub(crate) fn new(
entity_index: &EntityIndex,
module: &'module wasmtime_environ::Module,
) -> EntityType<'module> {
match entity_index {
EntityIndex::Function(func_index) => {
let sig = module.local.func_signature(*func_index);
EntityType::Function(&sig)
}
EntityIndex::Table(table_index) => {
EntityType::Table(&module.local.table_plans[*table_index].table)
}
EntityIndex::Memory(memory_index) => {
EntityType::Memory(&module.local.memory_plans[*memory_index].memory)
}
EntityIndex::Global(global_index) => {
EntityType::Global(&module.local.globals[*global_index])
}
}
}
fn extern_type(&self) -> ExternType {
match self {
EntityType::Function(sig) => FuncType::from_wasmtime_signature(sig)
.expect("core wasm function type should be supported")
.into(),
EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),
EntityType::Memory(memory) => MemoryType::from_wasmtime_memory(memory).into(),
EntityType::Global(global) => GlobalType::from_wasmtime_global(global)
.expect("core wasm global type should be supported")
.into(),
}
}
}
// Import Types
/// A descriptor for an imported value into a wasm module.
@@ -390,38 +438,53 @@ impl MemoryType {
/// [`Module::imports`](crate::Module::imports) API. Each [`ImportType`]
/// describes an import into the wasm module with the module/name that it's
/// imported from as well as the type of item that's being imported.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct ImportType {
module: String,
name: String,
ty: ExternType,
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ImportType<'module> {
/// The module of the import.
module: &'module str,
/// The field of the import.
name: &'module str,
/// The type of the import.
ty: EntityType<'module>,
}
impl ImportType {
impl<'module> ImportType<'module> {
/// Creates a new import descriptor which comes from `module` and `name` and
/// is of type `ty`.
pub fn new(module: &str, name: &str, ty: ExternType) -> ImportType {
ImportType {
module: module.to_string(),
name: name.to_string(),
ty,
}
pub(crate) fn new(
module: &'module str,
name: &'module str,
ty: EntityType<'module>,
) -> ImportType<'module> {
ImportType { module, name, ty }
}
/// Returns the module name that this import is expected to come from.
pub fn module(&self) -> &str {
&self.module
pub fn module(&self) -> &'module str {
self.module
}
/// Returns the field name of the module that this import is expected to
/// come from.
pub fn name(&self) -> &str {
&self.name
pub fn name(&self) -> &'module str {
self.name
}
/// Returns the expected type of this import.
pub fn ty(&self) -> &ExternType {
&self.ty
pub fn ty(&self) -> ExternType {
self.ty.extern_type()
}
}
impl<'module> fmt::Debug for ImportType<'module> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImportType")
.field("module", &self.module().to_owned())
.field("name", &self.name().to_owned())
.field("ty", &self.ty())
.finish()
}
}
@@ -433,29 +496,38 @@ impl ImportType {
/// [`Module::exports`](crate::Module::exports) accessor and describes what
/// names are exported from a wasm module and the type of the item that is
/// exported.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct ExportType {
name: String,
ty: ExternType,
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ExportType<'module> {
/// The name of the export.
name: &'module str,
/// The type of the export.
ty: EntityType<'module>,
}
impl ExportType {
impl<'module> ExportType<'module> {
/// Creates a new export which is exported with the given `name` and has the
/// given `ty`.
pub fn new(name: &str, ty: ExternType) -> ExportType {
ExportType {
name: name.to_string(),
ty,
}
pub(crate) fn new(name: &'module str, ty: EntityType<'module>) -> ExportType<'module> {
ExportType { name, ty }
}
/// Returns the name by which this export is known by.
pub fn name(&self) -> &str {
&self.name
/// Returns the name by which this export is known.
pub fn name(&self) -> &'module str {
self.name
}
/// Returns the type of this export.
pub fn ty(&self) -> &ExternType {
&self.ty
pub fn ty(&self) -> ExternType {
self.ty.extern_type()
}
}
impl<'module> fmt::Debug for ExportType<'module> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExportType")
.field("name", &self.name().to_owned())
.field("ty", &self.ty())
.finish()
}
}