Repository: bytecodealliance/wasmtime
Pull request #1524 (2020-04-16 18:39:18)

Refactor
This contains refactorings and API changes split off from #1516.

The main public-facing change here is that `Instance` and `Module` now compute their imports and exports on demand rather than holding precomputed arrays, and `Func::get*` return closures which capture an `InstanceHandle` so that they hold the instance live dynamically.

<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

- [ ] This has been discussed in issue #..., or if not, please tell us why
  here.
- [ ] A short description of what this does, why it is needed; if the
  description becomes long, the matter should probably be discussed in an issue
  first.
- [ ] This PR contains test cases, if meaningful.
- [ ] A reviewer from the core maintainer team has been assigned for this PR.
  If you don't know who could review this, please indicate so. The list of
  suggested reviewers on the right can help you.

Please ensure all communication adheres to the [code of
conduct](https://github.com/bytecodealliance/wasmtime/blob/master/CODE_OF_CONDUCT.md).
-->


github-actions[bot]: #### Subscribe to Label Action

cc @peterhuene

<details>
This issue or pull request has been labeled: "wasmtime:api"

Thus the following users have been cc'd because of the following labels:

* peterhuene: wasmtime:api

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>
github-actions[bot]: #### Subscribe to Label Action

cc @fitzgen, @peterhuene

<details>
This issue or pull request has been labeled: "fuzzing", "wasmtime:c-api"

Thus the following users have been cc'd because of the following labels:

* fitzgen: fuzzing
* peterhuene: wasmtime:c-api

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>
github-actions[bot]: #### Subscribe to Label Action

cc @bnjbvr

<details>
This issue or pull request has been labeled: "cranelift", "cranelift:wasm"

Thus the following users have been cc'd because of the following labels:

* bnjbvr: cranelift

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>

Patch:
diff --git a/cranelift/wasm/src/translation_utils.rs b/cranelift/wasm/src/translation_utils.rs
index eb12db950..74bc04399 100644
--- a/cranelift/wasm/src/translation_utils.rs
+++ b/cranelift/wasm/src/translation_utils.rs
@@ -68,7 +68,7 @@ pub struct ElemIndex(u32);
 entity_impl!(ElemIndex);
 
 /// WebAssembly global.
-#[derive(Debug, Clone, Copy, Hash)]
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 pub struct Global {
     /// The type of the value stored in the global.
     pub ty: ir::Type,
@@ -79,7 +79,7 @@ pub struct Global {
 }
 
 /// Globals are initialized via the `const` operators or by referring to another import.
-#[derive(Debug, Clone, Copy, Hash)]
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 pub enum GlobalInit {
     /// An `i32.const`.
     I32Const(i32),
@@ -102,7 +102,7 @@ pub enum GlobalInit {
 }
 
 /// WebAssembly table.
-#[derive(Debug, Clone, Copy, Hash)]
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 pub struct Table {
     /// The type of data stored in elements of the table.
     pub ty: TableElementType,
@@ -113,7 +113,7 @@ pub struct Table {
 }
 
 /// WebAssembly table element. Can be a function or a scalar type.
-#[derive(Debug, Clone, Copy, Hash)]
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 pub enum TableElementType {
     /// A scalar type.
     Val(ir::Type),
@@ -122,7 +122,7 @@ pub enum TableElementType {
 }
 
 /// WebAssembly linear memory.
-#[derive(Debug, Clone, Copy, Hash)]
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 pub struct Memory {
     /// The minimum number of pages in the memory.
     pub minimum: u32,
diff --git a/crates/api/src/externals.rs b/crates/api/src/externals.rs
index 4ea3ca3dd..576c92e15 100644
--- a/crates/api/src/externals.rs
+++ b/crates/api/src/externals.rs
@@ -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()
+    }
+}
diff --git a/crates/api/src/func.rs b/crates/api/src/func.rs
index d70195946..b6a4dec62 100644
--- a/crates/api/src/func.rs
+++ b/crates/api/src/func.rs
@@ -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,
diff --git a/crates/api/src/instance.rs b/crates/api/src/instance.rs
index fd576d987..c1b66ebe2 100644
--- a/crates/api/src/instance.rs
+++ b/crates/api/src/instance.rs
@@ -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)]
diff --git a/crates/api/src/linker.rs b/crates/api/src/linker.rs
index 694c2dc21..888ad1cff 100644
--- a/crates/api/src/linker.rs
+++ b/crates/api/src/linker.rs
@@ -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())
     }
 }
diff --git a/crates/api/src/module.rs b/crates/api/src/module.rs
index 1060167a9..c741d434e 100644
--- a/crates/api/src/module.rs
+++ b/crates/api/src/module.rs
@@ -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.
diff --git a/crates/api/src/trampoline/func.rs b/crates/api/src/trampoline/func.rs
index 072d99dd2..0c5f36c62 100644
--- a/crates/api/src/trampoline/func.rs
+++ b/crates/api/src/trampoline/func.rs
@@ -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);
diff --git a/crates/api/src/trampoline/global.rs b/crates/api/src/trampoline/global.rs
index 1499d18be..db2366fc9 100644
--- a/crates/api/src/trampoline/global.rs
+++ b/crates/api/src/trampoline/global.rs
@@ -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,
diff --git a/crates/api/src/trampoline/memory.rs b/crates/api/src/trampoline/memory.rs
index cd8c5ebff..b3a2bf6a4 100644
--- a/crates/api/src/trampoline/memory.rs
+++ b/crates/api/src/trampoline/memory.rs
@@ -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,
diff --git a/crates/api/src/trampoline/table.rs b/crates/api/src/trampoline/table.rs
index 3de2f771f..eb6c12dd6 100644
--- a/crates/api/src/trampoline/table.rs
+++ b/crates/api/src/trampoline/table.rs
@@ -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,
diff --git a/crates/api/src/types.rs b/crates/api/src/types.rs
index 7d39c8047..5b3513f6b 100644
--- a/crates/api/src/types.rs
+++ b/crates/api/src/types.rs
@@ -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()
     }
 }
diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs
index e5d6fc615..b882082fa 100644
--- a/crates/c-api/src/extern.rs
+++ b/crates/c-api/src/extern.rs
@@ -41,10 +41,10 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t {
 #[no_mangle]
 pub extern "C" fn wasm_extern_type(e: &wasm_extern_t) -> Box<wasm_externtype_t> {
     let ty = match &e.which {
-        ExternHost::Func(f) => ExternType::Func(f.borrow().ty().clone()),
-        ExternHost::Global(f) => ExternType::Global(f.borrow().ty().clone()),
-        ExternHost::Table(f) => ExternType::Table(f.borrow().ty().clone()),
-        ExternHost::Memory(f) => ExternType::Memory(f.borrow().ty().clone()),
+        ExternHost::Func(f) => ExternType::Func(f.borrow().ty()),
+        ExternHost::Global(f) => ExternType::Global(f.borrow().ty()),
+        ExternHost::Table(f) => ExternType::Table(f.borrow().ty()),
+        ExternHost::Memory(f) => ExternType::Memory(f.borrow().ty()),
     };
     Box::new(wasm_externtype_t::new(ty))
 }
diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs
index 9c9718216..3c58d1ca4 100644
--- a/crates/c-api/src/func.rs
+++ b/crates/c-api/src/func.rs
@@ -246,7 +246,7 @@ fn _wasmtime_func_call(
 
 #[no_mangle]
 pub extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
-    Box::new(wasm_functype_t::new(f.func().borrow().ty().clone()))
+    Box::new(wasm_functype_t::new(f.func().borrow().ty()))
 }
 
 #[no_mangle]
@@ -272,10 +272,10 @@ pub unsafe extern "C" fn wasmtime_caller_export_get(
     let name = str::from_utf8(name.as_slice()).ok()?;
     let export = caller.caller.get_export(name)?;
     let which = match export {
-        Extern::Func(f) => ExternHost::Func(HostRef::new(f.clone())),
-        Extern::Global(g) => ExternHost::Global(HostRef::new(g.clone())),
-        Extern::Memory(m) => ExternHost::Memory(HostRef::new(m.clone())),
-        Extern::Table(t) => ExternHost::Table(HostRef::new(t.clone())),
+        Extern::Func(f) => ExternHost::Func(HostRef::new(f)),
+        Extern::Global(g) => ExternHost::Global(HostRef::new(g)),
+        Extern::Memory(m) => ExternHost::Memory(HostRef::new(m)),
+        Extern::Table(t) => ExternHost::Table(HostRef::new(t)),
     };
     Some(Box::new(wasm_extern_t { which }))
 }
diff --git a/crates/c-api/src/global.rs b/crates/c-api/src/global.rs
index 4c6f025d6..fb3f163ba 100644
--- a/crates/c-api/src/global.rs
+++ b/crates/c-api/src/global.rs
@@ -71,7 +71,7 @@ pub extern "C" fn wasm_global_as_extern(g: &wasm_global_t) -> &wasm_extern_t {
 
 #[no_mangle]
 pub extern "C" fn wasm_global_type(g: &wasm_global_t) -> Box<wasm_globaltype_t> {
-    let globaltype = g.global().borrow().ty().clone();
+    let globaltype = g.global().borrow().ty();
     Box::new(wasm_globaltype_t::new(globaltype))
 }
 
diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs
index bbeb5626e..0ac864f5c 100644
--- a/crates/c-api/src/instance.rs
+++ b/crates/c-api/src/instance.rs
@@ -145,12 +145,11 @@ pub extern "C" fn wasm_instance_exports(instance: &wasm_instance_t, out: &mut wa
         let instance = &instance.instance.borrow();
         instance
             .exports()
-            .iter()
-            .map(|e| match e {
-                Extern::Func(f) => ExternHost::Func(HostRef::new(f.clone())),
-                Extern::Global(f) => ExternHost::Global(HostRef::new(f.clone())),
-                Extern::Memory(f) => ExternHost::Memory(HostRef::new(f.clone())),
-                Extern::Table(f) => ExternHost::Table(HostRef::new(f.clone())),
+            .map(|e| match e.into_extern() {
+                Extern::Func(f) => ExternHost::Func(HostRef::new(f)),
+                Extern::Global(f) => ExternHost::Global(HostRef::new(f)),
+                Extern::Memory(f) => ExternHost::Memory(HostRef::new(f)),
+                Extern::Table(f) => ExternHost::Table(HostRef::new(f)),
             })
             .collect()
     });
diff --git a/crates/c-api/src/memory.rs b/crates/c-api/src/memory.rs
index ab814bc29..d77f01c4e 100644
--- a/crates/c-api/src/memory.rs
+++ b/crates/c-api/src/memory.rs
@@ -51,7 +51,7 @@ pub extern "C" fn wasm_memory_as_extern(m: &wasm_memory_t) -> &wasm_extern_t {
 
 #[no_mangle]
 pub extern "C" fn wasm_memory_type(m: &wasm_memory_t) -> Box<wasm_memorytype_t> {
-    let ty = m.memory().borrow().ty().clone();
+    let ty = m.memory().borrow().ty();
     Box::new(wasm_memorytype_t::new(ty))
 }
 
diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs
index 4d1b2ec0c..036363260 100644
--- a/crates/c-api/src/module.rs
+++ b/crates/c-api/src/module.rs
@@ -46,13 +46,11 @@ pub extern "C" fn wasmtime_module_new(
     handle_result(Module::from_binary(store, binary), |module| {
         let imports = module
             .imports()
-            .iter()
-            .map(|i| wasm_importtype_t::new(i.clone()))
+            .map(|i| wasm_importtype_t::new(i.module().to_owned(), i.name().to_owned(), i.ty()))
             .collect::<Vec<_>>();
         let exports = module
             .exports()
-            .iter()
-            .map(|e| wasm_exporttype_t::new(e.clone()))
+            .map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty()))
             .collect::<Vec<_>>();
         let module = Box::new(wasm_module_t {
             module: HostRef::new(module),
diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs
index 8f4c0dca0..a52b79048 100644
--- a/crates/c-api/src/table.rs
+++ b/crates/c-api/src/table.rs
@@ -54,7 +54,7 @@ pub unsafe extern "C" fn wasm_table_new(
 
 #[no_mangle]
 pub extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box<wasm_tabletype_t> {
-    let ty = t.table().borrow().ty().clone();
+    let ty = t.table().borrow().ty();
     Box::new(wasm_tabletype_t::new(ty))
 }
 
diff --git a/crates/c-api/src/types/export.rs b/crates/c-api/src/types/export.rs
index 90d69b96e..3d83cc41f 100644
--- a/crates/c-api/src/types/export.rs
+++ b/crates/c-api/src/types/export.rs
@@ -1,12 +1,12 @@
 use crate::{wasm_externtype_t, wasm_name_t};
 use once_cell::unsync::OnceCell;
-use std::str;
-use wasmtime::ExportType;
+use wasmtime::ExternType;
 
 #[repr(C)]
 #[derive(Clone)]
 pub struct wasm_exporttype_t {
-    ty: ExportType,
+    name: String,
+    ty: ExternType,
     name_cache: OnceCell<wasm_name_t>,
     type_cache: OnceCell<wasm_externtype_t>,
 }
@@ -14,8 +14,9 @@ pub struct wasm_exporttype_t {
 wasmtime_c_api_macros::declare_ty!(wasm_exporttype_t);
 
 impl wasm_exporttype_t {
-    pub(crate) fn new(ty: ExportType) -> wasm_exporttype_t {
+    pub(crate) fn new(name: String, ty: ExternType) -> wasm_exporttype_t {
         wasm_exporttype_t {
+            name,
             ty,
             name_cache: OnceCell::new(),
             type_cache: OnceCell::new(),
@@ -29,19 +30,18 @@ pub extern "C" fn wasm_exporttype_new(
     ty: Box<wasm_externtype_t>,
 ) -> Option<Box<wasm_exporttype_t>> {
     let name = name.take();
-    let name = str::from_utf8(&name).ok()?;
-    let ty = ExportType::new(name, ty.ty());
-    Some(Box::new(wasm_exporttype_t::new(ty)))
+    let name = String::from_utf8(name).ok()?;
+    Some(Box::new(wasm_exporttype_t::new(name, ty.ty())))
 }
 
 #[no_mangle]
 pub extern "C" fn wasm_exporttype_name(et: &wasm_exporttype_t) -> &wasm_name_t {
     et.name_cache
-        .get_or_init(|| wasm_name_t::from_name(&et.ty.name()))
+        .get_or_init(|| wasm_name_t::from_name(et.name.clone()))
 }
 
 #[no_mangle]
 pub extern "C" fn wasm_exporttype_type(et: &wasm_exporttype_t) -> &wasm_externtype_t {
     et.type_cache
-        .get_or_init(|| wasm_externtype_t::new(et.ty.ty().clone()))
+        .get_or_init(|| wasm_externtype_t::new(et.ty.clone()))
 }
diff --git a/crates/c-api/src/types/import.rs b/crates/c-api/src/types/import.rs
index 66306d2ad..58f1516a7 100644
--- a/crates/c-api/src/types/import.rs
+++ b/crates/c-api/src/types/import.rs
@@ -1,12 +1,13 @@
 use crate::{wasm_externtype_t, wasm_name_t};
 use once_cell::unsync::OnceCell;
-use std::str;
-use wasmtime::ImportType;
+use wasmtime::ExternType;
 
 #[repr(C)]
 #[derive(Clone)]
 pub struct wasm_importtype_t {
-    pub(crate) ty: ImportType,
+    pub(crate) module: String,
+    pub(crate) name: String,
+    pub(crate) ty: ExternType,
     module_cache: OnceCell<wasm_name_t>,
     name_cache: OnceCell<wasm_name_t>,
     type_cache: OnceCell<wasm_externtype_t>,
@@ -15,8 +16,10 @@ pub struct wasm_importtype_t {
 wasmtime_c_api_macros::declare_ty!(wasm_importtype_t);
 
 impl wasm_importtype_t {
-    pub(crate) fn new(ty: ImportType) -> wasm_importtype_t {
+    pub(crate) fn new(module: String, name: String, ty: ExternType) -> wasm_importtype_t {
         wasm_importtype_t {
+            module,
+            name,
             ty,
             module_cache: OnceCell::new(),
             name_cache: OnceCell::new(),
@@ -33,26 +36,25 @@ pub extern "C" fn wasm_importtype_new(
 ) -> Option<Box<wasm_importtype_t>> {
     let module = module.take();
     let name = name.take();
-    let module = str::from_utf8(&module).ok()?;
-    let name = str::from_utf8(&name).ok()?;
-    let ty = ImportType::new(module, name, ty.ty());
-    Some(Box::new(wasm_importtype_t::new(ty)))
+    let module = String::from_utf8(module).ok()?;
+    let name = String::from_utf8(name).ok()?;
+    Some(Box::new(wasm_importtype_t::new(module, name, ty.ty())))
 }
 
 #[no_mangle]
 pub extern "C" fn wasm_importtype_module(it: &wasm_importtype_t) -> &wasm_name_t {
     it.module_cache
-        .get_or_init(|| wasm_name_t::from_name(&it.ty.module()))
+        .get_or_init(|| wasm_name_t::from_name(it.module.clone()))
 }
 
 #[no_mangle]
 pub extern "C" fn wasm_importtype_name(it: &wasm_importtype_t) -> &wasm_name_t {
     it.name_cache
-        .get_or_init(|| wasm_name_t::from_name(&it.ty.name()))
+        .get_or_init(|| wasm_name_t::from_name(it.name.clone()))
 }
 
 #[no_mangle]
 pub extern "C" fn wasm_importtype_type(it: &wasm_importtype_t) -> &wasm_externtype_t {
     it.type_cache
-        .get_or_init(|| wasm_externtype_t::new(it.ty.ty().clone()))
+        .get_or_init(|| wasm_externtype_t::new(it.ty.clone()))
 }
diff --git a/crates/c-api/src/vec.rs b/crates/c-api/src/vec.rs
index 15918b29c..8a7d7395a 100644
--- a/crates/c-api/src/vec.rs
+++ b/crates/c-api/src/vec.rs
@@ -9,8 +9,8 @@ use std::slice;
 pub type wasm_name_t = wasm_byte_vec_t;
 
 impl wasm_name_t {
-    pub(crate) fn from_name(name: &str) -> wasm_name_t {
-        name.to_string().into_bytes().into()
+    pub(crate) fn from_name(name: String) -> wasm_name_t {
+        name.into_bytes().into()
     }
 }
 
diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs
index 96b379809..a3b3ef8e8 100644
--- a/crates/c-api/src/wasi.rs
+++ b/crates/c-api/src/wasi.rs
@@ -7,6 +7,7 @@ use std::fs::File;
 use std::os::raw::{c_char, c_int};
 use std::path::{Path, PathBuf};
 use std::slice;
+use std::str;
 use wasi_common::{
     old::snapshot_0::WasiCtxBuilder as WasiSnapshot0CtxBuilder, preopen_dir,
     WasiCtxBuilder as WasiPreview1CtxBuilder,
@@ -296,7 +297,7 @@ pub unsafe extern "C" fn wasi_instance_new(
         })),
         Err(e) => {
             *trap = Box::into_raw(Box::new(wasm_trap_t {
-                trap: HostRef::new(Trap::new(e.to_string())),
+                trap: HostRef::new(Trap::new(e)),
             }));
 
             None
@@ -312,26 +313,26 @@ pub extern "C" fn wasi_instance_bind_import<'a>(
     instance: &'a mut wasi_instance_t,
     import: &wasm_importtype_t,
 ) -> Option<&'a wasm_extern_t> {
-    let module = import.ty.module();
-    let name = import.ty.name();
+    let module = &import.module;
+    let name = str::from_utf8(import.name.as_bytes()).ok()?;
 
     let export = match &instance.wasi {
         WasiInstance::Preview1(wasi) => {
             if module != "wasi_snapshot_preview1" {
                 return None;
             }
-            wasi.get_export(name)?
+            wasi.get_export(&name)?
         }
         WasiInstance::Snapshot0(wasi) => {
             if module != "wasi_unstable" {
                 return None;
             }
 
-            wasi.get_export(name)?
+            wasi.get_export(&name)?
         }
     };
 
-    if export.ty() != import.ty.ty().func()? {
+    if &export.ty() != import.ty.func()? {
         return None;
     }
 
diff --git a/crates/c-api/src/wat2wasm.rs b/crates/c-api/src/wat2wasm.rs
index 696ee579a..4223da67d 100644
--- a/crates/c-api/src/wat2wasm.rs
+++ b/crates/c-api/src/wat2wasm.rs
@@ -10,6 +10,6 @@ pub extern "C" fn wasmtime_wat2wasm(
         Err(_) => return bad_utf8(),
     };
     handle_result(wat::parse_str(wat).map_err(|e| e.into()), |bytes| {
-        ret.set_buffer(bytes.into())
+        ret.set_buffer(bytes)
     })
 }
diff --git a/crates/environ/src/cranelift.rs b/crates/environ/src/cranelift.rs
index f9b8b4be8..7d78024a4 100644
--- a/crates/environ/src/cranelift.rs
+++ b/crates/environ/src/cranelift.rs
@@ -203,7 +203,7 @@ fn compile(env: CompileEnv<'_>) -> Result<ModuleCacheDataTupleType, CompileError
             let func_index = env.local.func_index(*i);
             let mut context = Context::new();
             context.func.name = get_func_name(func_index);
-            context.func.signature = env.local.signatures[env.local.functions[func_index]].clone();
+            context.func.signature = env.local.func_signature(func_index).clone();
             if env.tunables.debug_info {
                 context.func.collect_debug_info();
             }
diff --git a/crates/environ/src/func_environ.rs b/crates/environ/src/func_environ.rs
index 25e91e2e9..572d5adb7 100644
--- a/crates/environ/src/func_environ.rs
+++ b/crates/environ/src/func_environ.rs
@@ -906,8 +906,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         func: &mut ir::Function,
         index: FuncIndex,
     ) -> WasmResult<ir::FuncRef> {
-        let sigidx = self.module.functions[index];
-        let signature = func.import_signature(self.module.signatures[sigidx].clone());
+        let sig = self.module.func_signature(index);
+        let signature = func.import_signature(sig.clone());
         let name = get_func_name(index);
         Ok(func.import_function(ir::ExtFuncData {
             name,
diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs
index f259fa45d..33858efa7 100644
--- a/crates/environ/src/lib.rs
+++ b/crates/environ/src/lib.rs
@@ -55,7 +55,7 @@ pub use crate::func_environ::BuiltinFunctionIndex;
 #[cfg(feature = "lightbeam")]
 pub use crate::lightbeam::Lightbeam;
 pub use crate::module::{
-    Export, MemoryPlan, MemoryStyle, Module, ModuleLocal, TableElements, TablePlan, TableStyle,
+    EntityIndex, MemoryPlan, MemoryStyle, Module, ModuleLocal, TableElements, TablePlan, TableStyle,
 };
 pub use crate::module_environ::{
     translate_signature, DataInitializer, DataInitializerLocation, FunctionBodyData,
diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs
index 3264d4664..9a079e157 100644
--- a/crates/environ/src/module.rs
+++ b/crates/environ/src/module.rs
@@ -30,16 +30,16 @@ pub struct TableElements {
     pub elements: Box<[FuncIndex]>,
 }
 
-/// An entity to export.
+/// An index of an entity.
 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Export {
-    /// Function export.
+pub enum EntityIndex {
+    /// Function index.
     Function(FuncIndex),
-    /// Table export.
+    /// Table index.
     Table(TableIndex),
-    /// Memory export.
+    /// Memory index.
     Memory(MemoryIndex),
-    /// Global export.
+    /// Global index.
     Global(GlobalIndex),
 }
 
@@ -150,21 +150,11 @@ pub struct Module {
     /// function.
     pub local: ModuleLocal,
 
-    /// Names of imported functions, as well as the index of the import that
-    /// performed this import.
-    pub imported_funcs: PrimaryMap<FuncIndex, (String, String, u32)>,
-
-    /// Names of imported tables.
-    pub imported_tables: PrimaryMap<TableIndex, (String, String, u32)>,
-
-    /// Names of imported memories.
-    pub imported_memories: PrimaryMap<MemoryIndex, (String, String, u32)>,
-
-    /// Names of imported globals.
-    pub imported_globals: PrimaryMap<GlobalIndex, (String, String, u32)>,
+    /// All import records, in the order they are declared in the module.
+    pub imports: Vec<(String, String, EntityIndex)>,
 
     /// Exported entities.
-    pub exports: IndexMap<String, Export>,
+    pub exports: IndexMap<String, EntityIndex>,
 
     /// The module "start" function, if present.
     pub start_func: Option<FuncIndex>,
@@ -226,10 +216,7 @@ impl Module {
         Self {
             id: NEXT_ID.fetch_add(1, SeqCst),
             name: None,
-            imported_funcs: PrimaryMap::new(),
-            imported_tables: PrimaryMap::new(),
-            imported_memories: PrimaryMap::new(),
-            imported_globals: PrimaryMap::new(),
+            imports: Vec::new(),
             exports: IndexMap::new(),
             start_func: None,
             table_elements: Vec::new(),
@@ -344,4 +331,9 @@ impl ModuleLocal {
     pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
         index.index() < self.num_imported_globals
     }
+
+    /// Convenience method for looking up the signature of a function.
+    pub fn func_signature(&self, func_index: FuncIndex) -> &ir::Signature {
+        &self.signatures[self.functions[func_index]]
+    }
 }
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs
index b4489e45f..378d419fd 100644
--- a/crates/environ/src/module_environ.rs
+++ b/crates/environ/src/module_environ.rs
@@ -1,5 +1,5 @@
 use crate::func_environ::FuncEnvironment;
-use crate::module::{Export, MemoryPlan, Module, TableElements, TablePlan};
+use crate::module::{EntityIndex, MemoryPlan, Module, TableElements, TablePlan};
 use crate::tunables::Tunables;
 use cranelift_codegen::ir;
 use cranelift_codegen::ir::{AbiParam, ArgumentPurpose};
@@ -8,7 +8,7 @@ use cranelift_entity::PrimaryMap;
 use cranelift_wasm::{
     self, translate_module, DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex,
     Memory, MemoryIndex, ModuleTranslationState, SignatureIndex, Table, TableIndex,
-    TargetEnvironment, WasmResult,
+    TargetEnvironment, WasmError, WasmResult,
 };
 use std::convert::TryFrom;
 use std::sync::Arc;
@@ -57,7 +57,6 @@ impl<'data> ModuleTranslation<'data> {
 pub struct ModuleEnvironment<'data> {
     /// The result to be filled in.
     result: ModuleTranslation<'data>,
-    imports: u32,
 }
 
 impl<'data> ModuleEnvironment<'data> {
@@ -72,7 +71,6 @@ impl<'data> ModuleEnvironment<'data> {
                 tunables: tunables.clone(),
                 module_translation: None,
             },
-            imports: 0,
         }
     }
 
@@ -89,7 +87,7 @@ impl<'data> ModuleEnvironment<'data> {
         Ok(self.result)
     }
 
-    fn declare_export(&mut self, export: Export, name: &str) -> WasmResult<()> {
+    fn declare_export(&mut self, export: EntityIndex, name: &str) -> WasmResult<()> {
         self.result
             .module
             .exports
@@ -123,6 +121,14 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
         Ok(())
     }
 
+    fn reserve_imports(&mut self, num: u32) -> WasmResult<()> {
+        Ok(self
+            .result
+            .module
+            .imports
+            .reserve_exact(usize::try_from(num).unwrap()))
+    }
+
     fn declare_func_import(
         &mut self,
         sig_index: SignatureIndex,
@@ -131,37 +137,33 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
     ) -> WasmResult<()> {
         debug_assert_eq!(
             self.result.module.local.functions.len(),
-            self.result.module.imported_funcs.len(),
+            self.result.module.local.num_imported_funcs,
             "Imported functions must be declared first"
         );
-        self.result.module.local.functions.push(sig_index);
-
-        self.result.module.imported_funcs.push((
-            String::from(module),
-            String::from(field),
-            self.imports,
+        let func_index = self.result.module.local.functions.push(sig_index);
+        self.result.module.imports.push((
+            module.to_owned(),
+            field.to_owned(),
+            EntityIndex::Function(func_index),
         ));
         self.result.module.local.num_imported_funcs += 1;
-        self.imports += 1;
         Ok(())
     }
 
     fn declare_table_import(&mut self, table: Table, module: &str, field: &str) -> WasmResult<()> {
         debug_assert_eq!(
             self.result.module.local.table_plans.len(),
-            self.result.module.imported_tables.len(),
+            self.result.module.local.num_imported_tables,
             "Imported tables must be declared first"
         );
         let plan = TablePlan::for_table(table, &self.result.tunables);
-        self.result.module.local.table_plans.push(plan);
-
-        self.result.module.imported_tables.push((
-            String::from(module),
-            String::from(field),
-            self.imports,
+        let table_index = self.result.module.local.table_plans.push(plan);
+        self.result.module.imports.push((
+            module.to_owned(),
+            field.to_owned(),
+            EntityIndex::Table(table_index),
         ));
         self.result.module.local.num_imported_tables += 1;
-        self.imports += 1;
         Ok(())
     }
 
@@ -173,19 +175,20 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
     ) -> WasmResult<()> {
         debug_assert_eq!(
             self.result.module.local.memory_plans.len(),
-            self.result.module.imported_memories.len(),
+            self.result.module.local.num_imported_memories,
             "Imported memories must be declared first"
         );
+        if memory.shared {
+            return Err(WasmError::Unsupported("shared memories".to_owned()));
+        }
         let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
-        self.result.module.local.memory_plans.push(plan);
-
-        self.result.module.imported_memories.push((
-            String::from(module),
-            String::from(field),
-            self.imports,
+        let memory_index = self.result.module.local.memory_plans.push(plan);
+        self.result.module.imports.push((
+            module.to_owned(),
+            field.to_owned(),
+            EntityIndex::Memory(memory_index),
         ));
         self.result.module.local.num_imported_memories += 1;
-        self.imports += 1;
         Ok(())
     }
 
@@ -197,26 +200,16 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
     ) -> WasmResult<()> {
         debug_assert_eq!(
             self.result.module.local.globals.len(),
-            self.result.module.imported_globals.len(),
+            self.result.module.local.num_imported_globals,
             "Imported globals must be declared first"
         );
-        self.result.module.local.globals.push(global);
-
-        self.result.module.imported_globals.push((
-            String::from(module),
-            String::from(field),
-            self.imports,
+        let global_index = self.result.module.local.globals.push(global);
+        self.result.module.imports.push((
+            module.to_owned(),
+            field.to_owned(),
+            EntityIndex::Global(global_index),
         ));
         self.result.module.local.num_imported_globals += 1;
-        self.imports += 1;
-        Ok(())
-    }
-
-    fn finish_imports(&mut self) -> WasmResult<()> {
-        self.result.module.imported_funcs.shrink_to_fit();
-        self.result.module.imported_tables.shrink_to_fit();
-        self.result.module.imported_memories.shrink_to_fit();
-        self.result.module.imported_globals.shrink_to_fit();
         Ok(())
     }
 
@@ -262,6 +255,9 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
     }
 
     fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
+        if memory.shared {
+            return Err(WasmError::Unsupported("shared memories".to_owned()));
+        }
         let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
         self.result.module.local.memory_plans.push(plan);
         Ok(())
@@ -290,19 +286,19 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
     }
 
     fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) -> WasmResult<()> {
-        self.declare_export(Export::Function(func_index), name)
+        self.declare_export(EntityIndex::Function(func_index), name)
     }
 
     fn declare_table_export(&mut self, table_index: TableIndex, name: &str) -> WasmResult<()> {
-        self.declare_export(Export::Table(table_index), name)
+        self.declare_export(EntityIndex::Table(table_index), name)
     }
 
     fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) -> WasmResult<()> {
-        self.declare_export(Export::Memory(memory_index), name)
+        self.declare_export(EntityIndex::Memory(memory_index), name)
     }
 
     fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) -> WasmResult<()> {
-        self.declare_export(Export::Global(global_index), name)
+        self.declare_export(EntityIndex::Global(global_index), name)
     }
 
     fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
@@ -421,6 +417,27 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
             .insert(func_index, name.to_string());
         Ok(())
     }
+
+    fn custom_section(&mut self, name: &'data str, _data: &'data [u8]) -> WasmResult<()> {
+        match name {
+            "webidl-bindings" | "wasm-interface-types" => Err(WasmError::Unsupported(
+                "\
+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
+"
+                .to_owned(),
+            )),
+            // skip other sections
+            _ => Ok(()),
+        }
+    }
 }
 
 /// Add environment-specific function parameters.
diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs
index ecb9be11e..b842ab2d0 100644
--- a/crates/fuzzing/src/oracles.rs
+++ b/crates/fuzzing/src/oracles.rs
@@ -169,31 +169,14 @@ pub fn differential_execution(
             }
         };
 
-        let funcs = module
-            .exports()
-            .iter()
-            .filter_map(|e| {
-                if let ExternType::Func(_) = e.ty() {
-                    Some(e.name())
-                } else {
-                    None
-                }
-            })
-            .collect::<Vec<_>>();
-
-        for name in funcs {
+        for (name, f) in instance.exports().filter_map(|e| {
+            let name = e.name();
+            e.into_func().map(|f| (name, f))
+        }) {
             // Always call the hang limit initializer first, so that we don't
             // infinite loop when calling another export.
             init_hang_limit(&instance);
 
-            let f = match instance
-                .get_export(&name)
-                .expect("instance should have export from module")
-            {
-                Extern::Func(f) => f.clone(),
-                _ => panic!("export should be a function"),
-            };
-
             let ty = f.ty();
             let params = match dummy::dummy_values(ty.params()) {
                 Ok(p) => p,
@@ -378,8 +361,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
 
                 let funcs = instance
                     .exports()
-                    .iter()
-                    .filter_map(|e| match e {
+                    .filter_map(|e| match e.into_extern() {
                         Extern::Func(f) => Some(f.clone()),
                         _ => None,
                     })
diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs
index 6db1ab14e..2e904ec1a 100644
--- a/crates/fuzzing/src/oracles/dummy.rs
+++ b/crates/fuzzing/src/oracles/dummy.rs
@@ -6,19 +6,20 @@ use wasmtime::{
 };
 
 /// Create a set of dummy functions/globals/etc for the given imports.
-pub fn dummy_imports(store: &Store, import_tys: &[ImportType]) -> Result<Vec<Extern>, Trap> {
-    let mut imports = Vec::with_capacity(import_tys.len());
-    for imp in import_tys {
-        imports.push(match imp.ty() {
-            ExternType::Func(func_ty) => Extern::Func(dummy_func(&store, func_ty.clone())),
-            ExternType::Global(global_ty) => {
-                Extern::Global(dummy_global(&store, global_ty.clone())?)
-            }
-            ExternType::Table(table_ty) => Extern::Table(dummy_table(&store, table_ty.clone())?),
-            ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(&store, mem_ty.clone())),
-        });
-    }
-    Ok(imports)
+pub fn dummy_imports<'module>(
+    store: &Store,
+    import_tys: impl Iterator<Item = ImportType<'module>>,
+) -> Result<Vec<Extern>, Trap> {
+    import_tys
+        .map(|imp| {
+            Ok(match imp.ty() {
+                ExternType::Func(func_ty) => Extern::Func(dummy_func(&store, func_ty)),
+                ExternType::Global(global_ty) => Extern::Global(dummy_global(&store, global_ty)?),
+                ExternType::Table(table_ty) => Extern::Table(dummy_table(&store, table_ty)?),
+                ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(&store, mem_ty)),
+            })
+        })
+        .collect()
 }
 
 /// Construct a dummy function for the given function type
diff --git a/crates/jit/src/imports.rs b/crates/jit/src/imports.rs
index adc76ccff..743b6e512 100644
--- a/crates/jit/src/imports.rs
+++ b/crates/jit/src/imports.rs
@@ -3,9 +3,10 @@
 use crate::resolver::Resolver;
 use more_asserts::assert_ge;
 use std::collections::HashSet;
+use std::convert::TryInto;
 use wasmtime_environ::entity::PrimaryMap;
 use wasmtime_environ::wasm::{Global, GlobalInit, Memory, Table, TableElementType};
-use wasmtime_environ::{MemoryPlan, MemoryStyle, Module, TablePlan};
+use wasmtime_environ::{EntityIndex, MemoryPlan, MemoryStyle, Module, TablePlan};
 use wasmtime_runtime::{
     Export, Imports, InstanceHandle, LinkError, SignatureRegistry, VMFunctionImport,
     VMGlobalImport, VMMemoryImport, VMTableImport,
@@ -22,156 +23,139 @@ pub fn resolve_imports(
 ) -> Result<Imports, LinkError> {
     let mut dependencies = HashSet::new();
 
-    let mut function_imports = PrimaryMap::with_capacity(module.imported_funcs.len());
-    for (index, (module_name, field, import_idx)) in module.imported_funcs.iter() {
-        match resolver.resolve(*import_idx, module_name, field) {
-            Some(export_value) => match export_value {
-                Export::Function(f) => {
-                    let import_signature = &module.local.signatures[module.local.functions[index]];
-                    let signature = signatures.lookup(f.signature).unwrap();
-                    if signature != *import_signature {
-                        // TODO: If the difference is in the calling convention,
-                        // we could emit a wrapper function to fix it up.
-                        return Err(LinkError(format!(
-                            "{}/{}: incompatible import type: exported function with signature {} \
-                             incompatible with function import with signature {}",
-                            module_name, field, signature, import_signature
-                        )));
-                    }
-                    dependencies.insert(unsafe { InstanceHandle::from_vmctx(f.vmctx) });
-                    function_imports.push(VMFunctionImport {
-                        body: f.address,
-                        vmctx: f.vmctx,
-                    });
-                }
-                Export::Table(_) | Export::Memory(_) | Export::Global(_) => {
+    let mut function_imports = PrimaryMap::with_capacity(module.local.num_imported_funcs);
+    let mut table_imports = PrimaryMap::with_capacity(module.local.num_imported_tables);
+    let mut memory_imports = PrimaryMap::with_capacity(module.local.num_imported_memories);
+    let mut global_imports = PrimaryMap::with_capacity(module.local.num_imported_globals);
+
+    for (import_idx, (module_name, field_name, import)) in module.imports.iter().enumerate() {
+        let import_idx = import_idx.try_into().unwrap();
+        let export = resolver.resolve(import_idx, module_name, field_name);
+
+        match (import, &export) {
+            (EntityIndex::Function(func_index), Some(Export::Function(f))) => {
+                let import_signature = module.local.func_signature(*func_index);
+                let signature = signatures.lookup(f.signature).unwrap();
+                if signature != *import_signature {
+                    // TODO: If the difference is in the calling convention,
+                    // we could emit a wrapper function to fix it up.
                     return Err(LinkError(format!(
-                        "{}/{}: incompatible import type: export incompatible with function import",
-                        module_name, field
+                        "{}/{}: incompatible import type: exported function with signature {} \
+                         incompatible with function import with signature {}",
+                        module_name, field_name, signature, import_signature
                     )));
                 }
-            },
-            None => {
+                dependencies.insert(unsafe { InstanceHandle::from_vmctx(f.vmctx) });
+                function_imports.push(VMFunctionImport {
+                    body: f.address,
+                    vmctx: f.vmctx,
+                });
+            }
+            (EntityIndex::Function(_), Some(_)) => {
+                return Err(LinkError(format!(
+                    "{}/{}: incompatible import type: export incompatible with function import",
+                    module_name, field_name
+                )));
+            }
+            (EntityIndex::Function(_), None) => {
                 return Err(LinkError(format!(
                     "{}/{}: unknown import function: function not provided",
-                    module_name, field
+                    module_name, field_name
                 )));
             }
-        }
-    }
 
-    let mut table_imports = PrimaryMap::with_capacity(module.imported_tables.len());
-    for (index, (module_name, field, import_idx)) in module.imported_tables.iter() {
-        match resolver.resolve(*import_idx, module_name, field) {
-            Some(export_value) => match export_value {
-                Export::Table(t) => {
-                    let import_table = &module.local.table_plans[index];
-                    if !is_table_compatible(&t.table, import_table) {
-                        return Err(LinkError(format!(
-                            "{}/{}: incompatible import type: exported table incompatible with \
-                             table import",
-                            module_name, field,
-                        )));
-                    }
-                    dependencies.insert(unsafe { InstanceHandle::from_vmctx(t.vmctx) });
-                    table_imports.push(VMTableImport {
-                        from: t.definition,
-                        vmctx: t.vmctx,
-                    });
-                }
-                Export::Global(_) | Export::Memory(_) | Export::Function(_) => {
+            (EntityIndex::Table(table_index), Some(Export::Table(t))) => {
+                let import_table = &module.local.table_plans[*table_index];
+                if !is_table_compatible(&t.table, import_table) {
                     return Err(LinkError(format!(
-                        "{}/{}: incompatible import type: export incompatible with table import",
-                        module_name, field
+                        "{}/{}: incompatible import type: exported table incompatible with \
+                             table import",
+                        module_name, field_name,
                     )));
                 }
-            },
-            None => {
+                dependencies.insert(unsafe { InstanceHandle::from_vmctx(t.vmctx) });
+                table_imports.push(VMTableImport {
+                    from: t.definition,
+                    vmctx: t.vmctx,
+                });
+            }
+            (EntityIndex::Table(_), Some(_)) => {
                 return Err(LinkError(format!(
-                    "unknown import: no provided import table for {}/{}",
-                    module_name, field
+                    "{}/{}: incompatible import type: export incompatible with table import",
+                    module_name, field_name
+                )));
+            }
+            (EntityIndex::Table(_), None) => {
+                return Err(LinkError(format!(
+                    "{}/{}: unknown import table: table not provided",
+                    module_name, field_name
                 )));
             }
-        }
-    }
-
-    let mut memory_imports = PrimaryMap::with_capacity(module.imported_memories.len());
-    for (index, (module_name, field, import_idx)) in module.imported_memories.iter() {
-        match resolver.resolve(*import_idx, module_name, field) {
-            Some(export_value) => match export_value {
-                Export::Memory(m) => {
-                    let import_memory = &module.local.memory_plans[index];
-                    if !is_memory_compatible(&m.memory, import_memory) {
-                        return Err(LinkError(format!(
-                            "{}/{}: incompatible import type: exported memory incompatible with \
-                             memory import",
-                            module_name, field
-                        )));
-                    }
-
-                    // Sanity-check: Ensure that the imported memory has at least
-                    // guard-page protections the importing module expects it to have.
-                    if let (
-                        MemoryStyle::Static { bound },
-                        MemoryStyle::Static {
-                            bound: import_bound,
-                        },
-                    ) = (m.memory.style, &import_memory.style)
-                    {
-                        assert_ge!(bound, *import_bound);
-                    }
-                    assert_ge!(m.memory.offset_guard_size, import_memory.offset_guard_size);
 
-                    dependencies.insert(unsafe { InstanceHandle::from_vmctx(m.vmctx) });
-                    memory_imports.push(VMMemoryImport {
-                        from: m.definition,
-                        vmctx: m.vmctx,
-                    });
-                }
-                Export::Table(_) | Export::Global(_) | Export::Function(_) => {
+            (EntityIndex::Memory(memory_index), Some(Export::Memory(m))) => {
+                let import_memory = &module.local.memory_plans[*memory_index];
+                if !is_memory_compatible(&m.memory, import_memory) {
                     return Err(LinkError(format!(
-                        "{}/{}: incompatible import type: export incompatible with memory import",
-                        module_name, field
+                        "{}/{}: incompatible import type: exported memory incompatible with \
+                             memory import",
+                        module_name, field_name
                     )));
                 }
-            },
-            None => {
+
+                // Sanity-check: Ensure that the imported memory has at least
+                // guard-page protections the importing module expects it to have.
+                if let (
+                    MemoryStyle::Static { bound },
+                    MemoryStyle::Static {
+                        bound: import_bound,
+                    },
+                ) = (&m.memory.style, &import_memory.style)
+                {
+                    assert_ge!(*bound, *import_bound);
+                }
+                assert_ge!(m.memory.offset_guard_size, import_memory.offset_guard_size);
+
+                dependencies.insert(unsafe { InstanceHandle::from_vmctx(m.vmctx) });
+                memory_imports.push(VMMemoryImport {
+                    from: m.definition,
+                    vmctx: m.vmctx,
+                });
+            }
+            (EntityIndex::Memory(_), Some(_)) => {
                 return Err(LinkError(format!(
-                    "unknown import: no provided import memory for {}/{}",
-                    module_name, field
+                    "{}/{}: incompatible import type: export incompatible with memory import",
+                    module_name, field_name
+                )));
+            }
+            (EntityIndex::Memory(_), None) => {
+                return Err(LinkError(format!(
+                    "{}/{}: unknown import memory: memory not provided",
+                    module_name, field_name
                 )));
             }
-        }
-    }
 
-    let mut global_imports = PrimaryMap::with_capacity(module.imported_globals.len());
-    for (index, (module_name, field, import_idx)) in module.imported_globals.iter() {
-        match resolver.resolve(*import_idx, module_name, field) {
-            Some(export_value) => match export_value {
-                Export::Table(_) | Export::Memory(_) | Export::Function(_) => {
+            (EntityIndex::Global(global_index), Some(Export::Global(g))) => {
+                let imported_global = module.local.globals[*global_index];
+                if !is_global_compatible(&g.global, &imported_global) {
                     return Err(LinkError(format!(
                         "{}/{}: incompatible import type: exported global incompatible with \
-                         global import",
-                        module_name, field
-                    )));
-                }
-                Export::Global(g) => {
-                    let imported_global = module.local.globals[index];
-                    if !is_global_compatible(&g.global, &imported_global) {
-                        return Err(LinkError(format!(
-                            "{}/{}: incompatible import type: exported global incompatible with \
                              global import",
-                            module_name, field
-                        )));
-                    }
-                    dependencies.insert(unsafe { InstanceHandle::from_vmctx(g.vmctx) });
-                    global_imports.push(VMGlobalImport { from: g.definition });
+                        module_name, field_name
+                    )));
                 }
-            },
-            None => {
+                dependencies.insert(unsafe { InstanceHandle::from_vmctx(g.vmctx) });
+                global_imports.push(VMGlobalImport { from: g.definition });
+            }
+            (EntityIndex::Global(_), Some(_)) => {
+                return Err(LinkError(format!(
+                    "{}/{}: incompatible import type: export incompatible with global import",
+                    module_name, field_name
+                )));
+            }
+            (EntityIndex::Global(_), None) => {
                 return Err(LinkError(format!(
-                    "unknown import: no provided import global for {}/{}",
-                    module_name, field
+                    "{}/{}: unknown import global: global not provided",
+                    module_name, field_name
                 )));
             }
         }
diff --git a/crates/obj/src/context.rs b/crates/obj/src/context.rs
index 9faa5768a..70a99d7b7 100644
--- a/crates/obj/src/context.rs
+++ b/crates/obj/src/context.rs
@@ -42,7 +42,7 @@ pub fn layout_vmcontext(
         }
     }
 
-    let num_tables_imports = module.imported_tables.len();
+    let num_tables_imports = module.local.num_imported_tables;
     let mut table_relocs = Vec::with_capacity(module.local.table_plans.len() - num_tables_imports);
     for (index, table) in module.local.table_plans.iter().skip(num_tables_imports) {
         let def_index = module.local.defined_table_index(index).unwrap();
@@ -66,7 +66,7 @@ pub fn layout_vmcontext(
         });
     }
 
-    let num_globals_imports = module.imported_globals.len();
+    let num_globals_imports = module.local.num_imported_globals;
     for (index, global) in module.local.globals.iter().skip(num_globals_imports) {
         let def_index = module.local.defined_global_index(index).unwrap();
         let offset = ofs.vmctx_vmglobal_definition(def_index) as usize;
diff --git a/crates/obj/src/function.rs b/crates/obj/src/function.rs
index e5598b293..b3b3dfaa2 100644
--- a/crates/obj/src/function.rs
+++ b/crates/obj/src/function.rs
@@ -11,7 +11,7 @@ pub fn declare_functions(
     module: &Module,
     relocations: &Relocations,
 ) -> Result<()> {
-    for i in 0..module.imported_funcs.len() {
+    for i in 0..module.local.num_imported_funcs {
         let string_name = format!("_wasm_function_{}", i);
         obj.declare(string_name, Decl::function_import())?;
     }
@@ -32,7 +32,7 @@ pub fn emit_functions(
 ) -> Result<()> {
     debug_assert!(
         module.start_func.is_none()
-            || module.start_func.unwrap().index() >= module.imported_funcs.len(),
+            || module.start_func.unwrap().index() >= module.local.num_imported_funcs,
         "imported start functions not supported yet"
     );
 
diff --git a/crates/runtime/src/debug_builtins.rs b/crates/runtime/src/debug_builtins.rs
index 831bbdd3a..3a6cfc8b8 100644
--- a/crates/runtime/src/debug_builtins.rs
+++ b/crates/runtime/src/debug_builtins.rs
@@ -16,7 +16,7 @@ pub unsafe extern "C" fn resolve_vmctx_memory_ptr(p: *const u32) -> *const u8 {
     );
     let handle = InstanceHandle::from_vmctx(VMCTX_AND_MEMORY.0);
     assert!(
-        VMCTX_AND_MEMORY.1 < handle.instance().module().local.memory_plans.len(),
+        VMCTX_AND_MEMORY.1 < handle.module().local.memory_plans.len(),
         "memory index for debugger is out of bounds"
     );
     let index = MemoryIndex::new(VMCTX_AND_MEMORY.1);
diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs
index 205215c5c..dc5a6698c 100644
--- a/crates/runtime/src/instance.rs
+++ b/crates/runtime/src/instance.rs
@@ -31,7 +31,7 @@ use wasmtime_environ::wasm::{
     DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
     ElemIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableIndex,
 };
-use wasmtime_environ::{ir, DataInitializer, Module, TableElements, VMOffsets};
+use wasmtime_environ::{ir, DataInitializer, EntityIndex, Module, TableElements, VMOffsets};
 
 cfg_if::cfg_if! {
     if #[cfg(unix)] {
@@ -296,9 +296,9 @@ impl Instance {
     }
 
     /// Lookup an export with the given export declaration.
-    pub fn lookup_by_declaration(&self, export: &wasmtime_environ::Export) -> Export {
+    pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
         match export {
-            wasmtime_environ::Export::Function(index) => {
+            EntityIndex::Function(index) => {
                 let signature = self.signature_id(self.module.local.functions[*index]);
                 let (address, vmctx) =
                     if let Some(def_index) = self.module.local.defined_func_index(*index) {
@@ -317,7 +317,7 @@ impl Instance {
                 }
                 .into()
             }
-            wasmtime_environ::Export::Table(index) => {
+            EntityIndex::Table(index) => {
                 let (definition, vmctx) =
                     if let Some(def_index) = self.module.local.defined_table_index(*index) {
                         (self.table_ptr(def_index), self.vmctx_ptr())
@@ -332,7 +332,7 @@ impl Instance {
                 }
                 .into()
             }
-            wasmtime_environ::Export::Memory(index) => {
+            EntityIndex::Memory(index) => {
                 let (definition, vmctx) =
                     if let Some(def_index) = self.module.local.defined_memory_index(*index) {
                         (self.memory_ptr(def_index), self.vmctx_ptr())
@@ -347,7 +347,7 @@ impl Instance {
                 }
                 .into()
             }
-            wasmtime_environ::Export::Global(index) => ExportGlobal {
+            EntityIndex::Global(index) => ExportGlobal {
                 definition: if let Some(def_index) = self.module.local.defined_global_index(*index)
                 {
                     self.global_ptr(def_index)
@@ -363,10 +363,10 @@ impl Instance {
 
     /// Return an iterator over the exports of this instance.
     ///
-    /// Specifically, it provides access to the key-value pairs, where they keys
+    /// Specifically, it provides access to the key-value pairs, where the keys
     /// are export names, and the values are export declarations which can be
     /// resolved `lookup_by_declaration`.
-    pub fn exports(&self) -> indexmap::map::Iter<String, wasmtime_environ::Export> {
+    pub fn exports(&self) -> indexmap::map::Iter<String, EntityIndex> {
         self.module.exports.iter()
     }
 
@@ -383,22 +383,35 @@ impl Instance {
             None => return Ok(()),
         };
 
-        let (callee_address, callee_vmctx) = match self.module.local.defined_func_index(start_index)
-        {
-            Some(defined_index) => {
-                let body = *self
-                    .finished_functions
-                    .get(defined_index)
-                    .expect("function index is out of bounds");
-                (body as *const _, self.vmctx_ptr())
-            }
-            None => {
-                assert_lt!(start_index.index(), self.module.imported_funcs.len());
-                let import = self.imported_function(start_index);
-                (import.body, import.vmctx)
-            }
-        };
+        self.invoke_function_index(start_index)
+            .map_err(InstantiationError::StartTrap)
+    }
+
+    fn invoke_function_index(&self, callee_index: FuncIndex) -> Result<(), Trap> {
+        let (callee_address, callee_vmctx) =
+            match self.module.local.defined_func_index(callee_index) {
+                Some(defined_index) => {
+                    let body = *self
+                        .finished_functions
+                        .get(defined_index)
+                        .expect("function index is out of bounds");
+                    (body as *const _, self.vmctx_ptr())
+                }
+                None => {
+                    assert_lt!(callee_index.index(), self.module.local.num_imported_funcs);
+                    let import = self.imported_function(callee_index);
+                    (import.body, import.vmctx)
+                }
+            };
+
+        self.invoke_function(callee_vmctx, callee_address)
+    }
 
+    fn invoke_function(
+        &self,
+        callee_vmctx: *mut VMContext,
+        callee_address: *const VMFunctionBody,
+    ) -> Result<(), Trap> {
         // Make the call.
         unsafe {
             catch_traps(callee_vmctx, || {
@@ -407,7 +420,6 @@ impl Instance {
                     unsafe extern "C" fn(*mut VMContext, *mut VMContext),
                 >(callee_address)(callee_vmctx, self.vmctx_ptr())
             })
-            .map_err(InstantiationError::StartTrap)
         }
     }
 
@@ -1019,7 +1031,7 @@ impl InstanceHandle {
     }
 
     /// Lookup an export with the given export declaration.
-    pub fn lookup_by_declaration(&self, export: &wasmtime_environ::Export) -> Export {
+    pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
         self.instance().lookup_by_declaration(export)
     }
 
@@ -1028,7 +1040,7 @@ impl InstanceHandle {
     /// Specifically, it provides access to the key-value pairs, where the keys
     /// are export names, and the values are export declarations which can be
     /// resolved `lookup_by_declaration`.
-    pub fn exports(&self) -> indexmap::map::Iter<String, wasmtime_environ::Export> {
+    pub fn exports(&self) -> indexmap::map::Iter<String, EntityIndex> {
         self.instance().exports()
     }
 
@@ -1204,7 +1216,7 @@ fn check_memory_init_bounds(
 
 /// Allocate memory for just the tables of the current module.
 fn create_tables(module: &Module) -> BoxedSlice<DefinedTableIndex, Table> {
-    let num_imports = module.imported_tables.len();
+    let num_imports = module.local.num_imported_tables;
     let mut tables: PrimaryMap<DefinedTableIndex, _> =
         PrimaryMap::with_capacity(module.local.table_plans.len() - num_imports);
     for table in &module.local.table_plans.values().as_slice()[num_imports..] {
@@ -1291,7 +1303,7 @@ fn create_memories(
     module: &Module,
     mem_creator: &dyn RuntimeMemoryCreator,
 ) -> Result<BoxedSlice<DefinedMemoryIndex, Box<dyn RuntimeLinearMemory>>, InstantiationError> {
-    let num_imports = module.imported_memories.len();
+    let num_imports = module.local.num_imported_memories;
     let mut memories: PrimaryMap<DefinedMemoryIndex, _> =
         PrimaryMap::with_capacity(module.local.memory_plans.len() - num_imports);
     for plan in &module.local.memory_plans.values().as_slice()[num_imports..] {
@@ -1336,7 +1348,7 @@ fn initialize_memories(
 /// Allocate memory for just the globals of the current module,
 /// with initializers applied.
 fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDefinition> {
-    let num_imports = module.imported_globals.len();
+    let num_imports = module.local.num_imported_globals;
     let mut vmctx_globals = PrimaryMap::with_capacity(module.local.globals.len() - num_imports);
 
     for _ in &module.local.globals.values().as_slice()[num_imports..] {
@@ -1348,7 +1360,7 @@ fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDef
 
 fn initialize_globals(instance: &Instance) {
     let module = Arc::clone(&instance.module);
-    let num_imports = module.imported_globals.len();
+    let num_imports = module.local.num_imported_globals;
     for (index, global) in module.local.globals.iter().skip(num_imports) {
         let def_index = module.local.defined_global_index(index).unwrap();
         unsafe {
@@ -1394,7 +1406,7 @@ pub enum InstantiationError {
     #[error("Trap occurred during instantiation")]
     Trap(Trap),
 
-    /// A compilation error occured.
+    /// A trap occurred while running the wasm start function.
     #[error("Trap occurred while invoking start function")]
     StartTrap(Trap),
 }
diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs
index b4866072c..96fbc3ad4 100644
--- a/crates/wast/src/wast.rs
+++ b/crates/wast/src/wast.rs
@@ -66,7 +66,7 @@ impl WastContext {
         }
     }
 
-    fn get_export(&self, module: Option<&str>, name: &str) -> Result<&Extern> {
+    fn get_export(&self, module: Option<&str>, name: &str) -> Result<Extern> {
         match module {
             Some(module) => self.linker.get_one_by_name(module, name),
             None => self
@@ -156,7 +156,7 @@ impl WastContext {
     ) -> Result<Outcome> {
         let func = self
             .get_export(instance_name, field)?
-            .func()
+            .into_func()
             .ok_or_else(|| anyhow!("no function named `{}`", field))?;
         Ok(match func.call(args) {
             Ok(result) => Outcome::Ok(result.into()),
@@ -168,7 +168,7 @@ impl WastContext {
     fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<Outcome> {
         let global = self
             .get_export(instance_name, field)?
-            .global()
+            .into_global()
             .ok_or_else(|| anyhow!("no global named `{}`", field))?;
         Ok(Outcome::Ok(vec![global.get()]))
     }
diff --git a/docs/lang-rust.md b/docs/lang-rust.md
index c87d1bffc..e95f61687 100644
--- a/docs/lang-rust.md
+++ b/docs/lang-rust.md
@@ -74,10 +74,8 @@ fn main() -> Result<(), Box<dyn Error>> {
     // The `Instance` gives us access to various exported functions and items,
     // which we access here to pull out our `answer` exported function and
     // run it.
-    let answer = instance.get_export("answer")
-        .expect("export named `answer` not found")
-        .func()
-        .expect("export `answer` was not a function");
+    let answer = instance.get_func("answer")
+        .expect("`answer` was not an exported function");
 
     // There's a few ways we can call the `answer` `Func` value. The easiest
     // is to statically assert its signature with `get0` (in this case asserting
diff --git a/docs/wasm-wat.md b/docs/wasm-wat.md
index e1f519b33..0f9153771 100644
--- a/docs/wasm-wat.md
+++ b/docs/wasm-wat.md
@@ -47,7 +47,7 @@ let wat = r#"
 "#;
 let module = Module::new(&store, wat)?;
 let instance = Instance::new(&module, &[])?;
-let add = instance.get_export("add").and_then(|f| f.func()).unwrap();
+let add = instance.get_func("add").unwrap();
 let add = add.get2::<i32, i32, i32>()?;
 println!("1 + 2 = {}", add(1, 2)?);
 # Ok(())
diff --git a/examples/fib-debug/main.rs b/examples/fib-debug/main.rs
index 79a5841c4..ff35b8c83 100644
--- a/examples/fib-debug/main.rs
+++ b/examples/fib-debug/main.rs
@@ -22,8 +22,7 @@ fn main() -> Result<()> {
 
     // Invoke `fib` export
     let fib = instance
-        .get_export("fib")
-        .and_then(|e| e.func())
+        .get_func("fib")
         .ok_or(anyhow::format_err!("failed to find `fib` function export"))?
         .get1::<i32, i32>()?;
     println!("fib(6) = {}", fib(6)?);
diff --git a/examples/gcd.rs b/examples/gcd.rs
index f310672f3..067ab7b2d 100644
--- a/examples/gcd.rs
+++ b/examples/gcd.rs
@@ -16,8 +16,7 @@ fn main() -> Result<()> {
 
     // Invoke `gcd` export
     let gcd = instance
-        .get_export("gcd")
-        .and_then(|e| e.func())
+        .get_func("gcd")
         .ok_or(anyhow::format_err!("failed to find `gcd` function export"))?
         .get2::<i32, i32, i32>()?;
 
diff --git a/examples/hello.rs b/examples/hello.rs
index a23974942..00772b22e 100644
--- a/examples/hello.rs
+++ b/examples/hello.rs
@@ -35,8 +35,7 @@ fn main() -> Result<()> {
     // Next we poke around a bit to extract the `run` function from the module.
     println!("Extracting export...");
     let run = instance
-        .get_export("run")
-        .and_then(|e| e.func())
+        .get_func("run")
         .ok_or(anyhow::format_err!("failed to find `run` function export"))?
         .get0::<()>()?;
 
diff --git a/examples/linking.rs b/examples/linking.rs
index 88dfb9956..4f9548739 100644
--- a/examples/linking.rs
+++ b/examples/linking.rs
@@ -26,7 +26,7 @@ fn main() -> Result<()> {
 
     // And with that we can perform the final link and the execute the module.
     let linking1 = linker.instantiate(&linking1)?;
-    let run = linking1.get_export("run").and_then(|e| e.func()).unwrap();
+    let run = linking1.get_func("run").unwrap();
     let run = run.get0::<()>()?;
     run()?;
     Ok(())
diff --git a/examples/memory.rs b/examples/memory.rs
index b86ecc34b..6f952dbb2 100644
--- a/examples/memory.rs
+++ b/examples/memory.rs
@@ -18,22 +18,18 @@ fn main() -> Result<()> {
 
     // Load up our exports from the instance
     let memory = instance
-        .get_export("memory")
-        .and_then(|e| e.memory())
+        .get_memory("memory")
         .ok_or(anyhow::format_err!("failed to find `memory` export"))?;
     let size = instance
-        .get_export("size")
-        .and_then(|e| e.func())
+        .get_func("size")
         .ok_or(anyhow::format_err!("failed to find `size` export"))?
         .get0::<i32>()?;
     let load = instance
-        .get_export("load")
-        .and_then(|e| e.func())
+        .get_func("load")
         .ok_or(anyhow::format_err!("failed to find `load` export"))?
         .get1::<i32, i32>()?;
     let store = instance
-        .get_export("store")
-        .and_then(|e| e.func())
+        .get_func("store")
         .ok_or(anyhow::format_err!("failed to find `store` export"))?
         .get2::<i32, i32, ()>()?;
 
diff --git a/examples/multi.rs b/examples/multi.rs
index 9d04ec4e2..0a5af6389 100644
--- a/examples/multi.rs
+++ b/examples/multi.rs
@@ -43,8 +43,7 @@ fn main() -> Result<()> {
     // Extract exports.
     println!("Extracting export...");
     let g = instance
-        .get_export("g")
-        .and_then(|e| e.func())
+        .get_func("g")
         .ok_or(format_err!("failed to find export `g`"))?;
 
     // Call `$g`.
@@ -60,8 +59,7 @@ fn main() -> Result<()> {
     // Call `$round_trip_many`.
     println!("Calling export \"round_trip_many\"...");
     let round_trip_many = instance
-        .get_export("round_trip_many")
-        .and_then(|e| e.func())
+        .get_func("round_trip_many")
         .ok_or(format_err!("failed to find export `round_trip_many`"))?;
     let args = vec![
         Val::I64(0),
diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs
index 9216bce52..8971364cd 100644
--- a/examples/wasi/main.rs
+++ b/examples/wasi/main.rs
@@ -33,10 +33,7 @@ fn main() -> Result<()> {
     // Instance our module with the imports we've created, then we can run the
     // standard wasi `_start` function.
     let instance = Instance::new(&module, &imports)?;
-    let start = instance
-        .get_export("_start")
-        .and_then(|e| e.func())
-        .unwrap();
+    let start = instance.get_func("_start").unwrap();
     let start = start.get0::<()>()?;
     start()?;
     Ok(())
diff --git a/src/commands/run.rs b/src/commands/run.rs
index 5908215ed..95c795d58 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -190,7 +190,7 @@ impl RunCommand {
         store: &Store,
         module_registry: &ModuleRegistry,
         path: &Path,
-    ) -> Result<(Instance, Module)> {
+    ) -> Result<Instance> {
         // Read the wasm module binary either as `*.wat` or a raw binary
         let data = wat::parse_file(path)?;
 
@@ -199,7 +199,6 @@ impl RunCommand {
         // Resolve import using module_registry.
         let imports = module
             .imports()
-            .iter()
             .map(|i| {
                 let export = match i.module() {
                     "wasi_snapshot_preview1" => {
@@ -222,20 +221,16 @@ impl RunCommand {
         let instance = Instance::new(&module, &imports)
             .context(format!("failed to instantiate {:?}", path))?;
 
-        Ok((instance, module))
+        Ok(instance)
     }
 
     fn handle_module(&self, store: &Store, module_registry: &ModuleRegistry) -> Result<()> {
-        let (instance, module) = Self::instantiate_module(store, module_registry, &self.module)?;
+        let instance = Self::instantiate_module(store, module_registry, &self.module)?;
 
         // If a function to invoke was given, invoke it.
         if let Some(name) = self.invoke.as_ref() {
             self.invoke_export(instance, name)?;
-        } else if module
-            .exports()
-            .iter()
-            .any(|export| export.name().is_empty())
-        {
+        } else if instance.exports().any(|export| export.name().is_empty()) {
             // Launch the default command export.
             self.invoke_export(instance, "")?;
         } else {
@@ -248,19 +243,16 @@ impl RunCommand {
     }
 
     fn invoke_export(&self, instance: Instance, name: &str) -> Result<()> {
-        let pos = instance
-            .module()
-            .exports()
-            .iter()
-            .enumerate()
-            .find(|(_, e)| e.name() == name);
-        let (ty, export) = match pos {
-            Some((i, ty)) => match (ty.ty(), &instance.exports()[i]) {
-                (wasmtime::ExternType::Func(ty), wasmtime::Extern::Func(f)) => (ty, f),
-                _ => bail!("export of `{}` wasn't a function", name),
-            },
-            None => bail!("failed to find export of `{}` in module", name),
+        let func = if let Some(export) = instance.get_export(name) {
+            if let Some(func) = export.into_func() {
+                func
+            } else {
+                bail!("export of `{}` wasn't a function", name)
+            }
+        } else {
+            bail!("failed to find export of `{}` in module", name)
         };
+        let ty = func.ty();
         if ty.params().len() > 0 {
             eprintln!(
                 "warning: using `--invoke` with a function that takes arguments \
@@ -288,7 +280,7 @@ impl RunCommand {
 
         // Invoke the function and then afterwards print all the results that came
         // out, if there are any.
-        let results = export
+        let results = func
             .call(&values)
             .with_context(|| format!("failed to invoke `{}`", name))?;
         if !results.is_empty() {


Test patch:
diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs
index 019990bd9..0157a9c61 100644
--- a/crates/test-programs/tests/wasm_tests/runtime.rs
+++ b/crates/test-programs/tests/wasm_tests/runtime.rs
@@ -51,7 +51,6 @@ pub fn instantiate(
     let module = Module::new(&store, &data).context("failed to create wasm module")?;
     let imports = module
         .imports()
-        .iter()
         .map(|i| {
             let field_name = i.name();
             if let Some(export) = snapshot1.get_export(field_name) {
@@ -60,7 +59,7 @@ pub fn instantiate(
                 bail!(
                     "import {} was not found in module {}",
                     field_name,
-                    i.module(),
+                    i.module()
                 )
             }
         })
@@ -71,13 +70,10 @@ pub fn instantiate(
         bin_name,
     ))?;
 
-    let export = instance
+    instance
         .get_export("_start")
         .context("expected a _start export")?
-        .clone();
-
-    export
-        .func()
+        .into_func()
         .context("expected export to be a func")?
         .call(&[])?;
 
diff --git a/tests/all/custom_signal_handler.rs b/tests/all/custom_signal_handler.rs
index 27d14fc91..2f1be828b 100644
--- a/tests/all/custom_signal_handler.rs
+++ b/tests/all/custom_signal_handler.rs
@@ -38,18 +38,13 @@ mod tests {
 "#;
 
     fn invoke_export(instance: &Instance, func_name: &str) -> Result<Box<[Val]>> {
-        let ret = instance
-            .get_export(func_name)
-            .unwrap()
-            .func()
-            .unwrap()
-            .call(&[])?;
+        let ret = instance.get_func(func_name).unwrap().call(&[])?;
         Ok(ret)
     }
 
     // Locate "memory" export, get base address and size and set memory protection to PROT_NONE
     fn set_up_memory(instance: &Instance) -> (*mut u8, usize) {
-        let mem_export = instance.get_export("memory").unwrap().memory().unwrap();
+        let mem_export = instance.get_memory("memory").unwrap();
         let base = mem_export.data_ptr();
         let length = mem_export.data_size();
 
@@ -105,9 +100,6 @@ mod tests {
             });
         }
 
-        let exports = instance.exports();
-        assert!(!exports.is_empty());
-
         // these invoke wasmtime_call_trampoline from action.rs
         {
             println!("calling read...");
@@ -130,8 +122,8 @@ mod tests {
 
         // these invoke wasmtime_call_trampoline from callable.rs
         {
-            let read_func = exports[0]
-                .func()
+            let read_func = instance
+                .get_func("read")
                 .expect("expected a 'read' func in the module");
             println!("calling read...");
             let result = read_func.call(&[]).expect("expected function not to trap");
@@ -139,8 +131,8 @@ mod tests {
         }
 
         {
-            let read_out_of_bounds_func = exports[1]
-                .func()
+            let read_out_of_bounds_func = instance
+                .get_func("read_out_of_bounds")
                 .expect("expected a 'read_out_of_bounds' func in the module");
             println!("calling read_out_of_bounds...");
             let trap = read_out_of_bounds_func
@@ -216,8 +208,8 @@ mod tests {
 
         // First instance1
         {
-            let exports1 = instance1.exports();
-            assert!(!exports1.is_empty());
+            let mut exports1 = instance1.exports();
+            assert!(exports1.next().is_some());
 
             println!("calling instance1.read...");
             let result = invoke_export(&instance1, "read").expect("read succeeded");
@@ -231,8 +223,8 @@ mod tests {
 
         // And then instance2
         {
-            let exports2 = instance2.exports();
-            assert!(!exports2.is_empty());
+            let mut exports2 = instance2.exports();
+            assert!(exports2.next().is_some());
 
             println!("calling instance2.read...");
             let result = invoke_export(&instance2, "read").expect("read succeeded");
@@ -262,13 +254,12 @@ mod tests {
             });
         }
 
-        let instance1_exports = instance1.exports();
-        assert!(!instance1_exports.is_empty());
-        let instance1_read = instance1_exports[0].clone();
+        let mut instance1_exports = instance1.exports();
+        let instance1_read = instance1_exports.next().unwrap();
 
-        // instance2 wich calls 'instance1.read'
+        // instance2 which calls 'instance1.read'
         let module2 = Module::new(&store, WAT2)?;
-        let instance2 = Instance::new(&module2, &[instance1_read])?;
+        let instance2 = Instance::new(&module2, &[instance1_read.into_extern()])?;
         // since 'instance2.run' calls 'instance1.read' we need to set up the signal handler to handle
         // SIGSEGV originating from within the memory of instance1
         unsafe {
diff --git a/tests/all/func.rs b/tests/all/func.rs
index 431de8645..1cbb3e081 100644
--- a/tests/all/func.rs
+++ b/tests/all/func.rs
@@ -269,14 +269,14 @@ fn get_from_module() -> anyhow::Result<()> {
         "#,
     )?;
     let instance = Instance::new(&module, &[])?;
-    let f0 = instance.get_export("f0").unwrap().func().unwrap();
+    let f0 = instance.get_func("f0").unwrap();
     assert!(f0.get0::<()>().is_ok());
     assert!(f0.get0::<i32>().is_err());
-    let f1 = instance.get_export("f1").unwrap().func().unwrap();
+    let f1 = instance.get_func("f1").unwrap();
     assert!(f1.get0::<()>().is_err());
     assert!(f1.get1::<i32, ()>().is_ok());
     assert!(f1.get1::<i32, f32>().is_err());
-    let f2 = instance.get_export("f2").unwrap().func().unwrap();
+    let f2 = instance.get_func("f2").unwrap();
     assert!(f2.get0::<()>().is_err());
     assert!(f2.get0::<i32>().is_ok());
     assert!(f2.get1::<i32, ()>().is_err());
diff --git a/tests/all/globals.rs b/tests/all/globals.rs
index 8f5406d39..d0e6896c1 100644
--- a/tests/all/globals.rs
+++ b/tests/all/globals.rs
@@ -70,7 +70,7 @@ fn use_after_drop() -> anyhow::Result<()> {
         "#,
     )?;
     let instance = Instance::new(&module, &[])?;
-    let g = instance.exports()[0].global().unwrap().clone();
+    let g = instance.get_global("foo").unwrap();
     assert_eq!(g.get().i32(), Some(100));
     g.set(101.into())?;
     drop(instance);
diff --git a/tests/all/import_calling_export.rs b/tests/all/import_calling_export.rs
index 44db6f39f..97c95384d 100644
--- a/tests/all/import_calling_export.rs
+++ b/tests/all/import_calling_export.rs
@@ -40,18 +40,14 @@ fn test_import_calling_export() {
     let instance =
         Instance::new(&module, imports.as_slice()).expect("failed to instantiate module");
 
-    let exports = instance.exports();
-    assert!(!exports.is_empty());
-
-    let run_func = exports[0]
-        .func()
+    let run_func = instance
+        .get_func("run")
         .expect("expected a run func in the module");
 
     *other.borrow_mut() = Some(
-        exports[1]
-            .func()
-            .expect("expected an other func in the module")
-            .clone(),
+        instance
+            .get_func("other")
+            .expect("expected an other func in the module"),
     );
 
     run_func.call(&[]).expect("expected function not to trap");
@@ -84,11 +80,8 @@ fn test_returns_incorrect_type() -> Result<()> {
     let imports = vec![callback_func.into()];
     let instance = Instance::new(&module, imports.as_slice())?;
 
-    let exports = instance.exports();
-    assert!(!exports.is_empty());
-
-    let run_func = exports[0]
-        .func()
+    let run_func = instance
+        .get_func("run")
         .expect("expected a run func in the module");
 
     let trap = run_func
diff --git a/tests/all/import_indexes.rs b/tests/all/import_indexes.rs
index befef2214..d13d100a9 100644
--- a/tests/all/import_indexes.rs
+++ b/tests/all/import_indexes.rs
@@ -43,7 +43,7 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> {
     ];
     let instance = Instance::new(&module, &imports)?;
 
-    let func = instance.get_export("foo").unwrap().func().unwrap();
+    let func = instance.get_func("foo").unwrap();
     let results = func.call(&[])?;
     assert_eq!(results.len(), 1);
     match results[0] {
diff --git a/tests/all/invoke_func_via_table.rs b/tests/all/invoke_func_via_table.rs
index 28ba779ec..8f59fb17d 100644
--- a/tests/all/invoke_func_via_table.rs
+++ b/tests/all/invoke_func_via_table.rs
@@ -17,9 +17,7 @@ fn test_invoke_func_via_table() -> Result<()> {
     let instance = Instance::new(&module, &[]).context("> Error instantiating module!")?;
 
     let f = instance
-        .get_export("table")
-        .unwrap()
-        .table()
+        .get_table("table")
         .unwrap()
         .get(0)
         .unwrap()
diff --git a/tests/all/linker.rs b/tests/all/linker.rs
index 2fcb58980..efca28526 100644
--- a/tests/all/linker.rs
+++ b/tests/all/linker.rs
@@ -90,7 +90,7 @@ fn interposition() -> Result<()> {
         )?;
     }
     let instance = linker.instantiate(&module)?;
-    let func = instance.get_export("export").unwrap().func().unwrap();
+    let func = instance.get_func("export").unwrap();
     let func = func.get0::<i32>()?;
     assert_eq!(func()?, 112);
     Ok(())
diff --git a/tests/all/memory_creator.rs b/tests/all/memory_creator.rs
index e5825da43..ed80cc72c 100644
--- a/tests/all/memory_creator.rs
+++ b/tests/all/memory_creator.rs
@@ -169,15 +169,7 @@ mod not_for_windows {
 
         assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 2);
 
-        assert_eq!(
-            instance2
-                .get_export("memory")
-                .unwrap()
-                .memory()
-                .unwrap()
-                .size(),
-            2
-        );
+        assert_eq!(instance2.get_memory("memory").unwrap().size(), 2);
 
         // we take the lock outside the assert, so it won't get poisoned on assert failure
         let tot_pages = *mem_creator.num_total_pages.lock().unwrap();
diff --git a/tests/all/traps.rs b/tests/all/traps.rs
index 755fa8120..5858dc3d8 100644
--- a/tests/all/traps.rs
+++ b/tests/all/traps.rs
@@ -17,9 +17,7 @@ fn test_trap_return() -> Result<()> {
     let hello_func = Func::new(&store, hello_type, |_, _, _| Err(Trap::new("test 123")));
 
     let instance = Instance::new(&module, &[hello_func.into()])?;
-    let run_func = instance.exports()[0]
-        .func()
-        .expect("expected function export");
+    let run_func = instance.get_func("run").expect("expected function export");
 
     let e = run_func
         .call(&[])
@@ -44,9 +42,7 @@ fn test_trap_trace() -> Result<()> {
 
     let module = Module::new(&store, wat)?;
     let instance = Instance::new(&module, &[])?;
-    let run_func = instance.exports()[0]
-        .func()
-        .expect("expected function export");
+    let run_func = instance.get_func("run").expect("expected function export");
 
     let e = run_func
         .call(&[])
@@ -91,9 +87,7 @@ fn test_trap_trace_cb() -> Result<()> {
 
     let module = Module::new(&store, wat)?;
     let instance = Instance::new(&module, &[fn_func.into()])?;
-    let run_func = instance.exports()[0]
-        .func()
-        .expect("expected function export");
+    let run_func = instance.get_func("run").expect("expected function export");
 
     let e = run_func
         .call(&[])
@@ -123,9 +117,7 @@ fn test_trap_stack_overflow() -> Result<()> {
 
     let module = Module::new(&store, wat)?;
     let instance = Instance::new(&module, &[])?;
-    let run_func = instance.exports()[0]
-        .func()
-        .expect("expected function export");
+    let run_func = instance.get_func("run").expect("expected function export");
 
     let e = run_func
         .call(&[])
@@ -159,9 +151,7 @@ fn trap_display_pretty() -> Result<()> {
 
     let module = Module::new(&store, wat)?;
     let instance = Instance::new(&module, &[])?;
-    let run_func = instance.exports()[0]
-        .func()
-        .expect("expected function export");
+    let run_func = instance.get_func("bar").expect("expected function export");
 
     let e = run_func.call(&[]).err().expect("error calling function");
     assert_eq!(
@@ -192,7 +182,7 @@ fn trap_display_multi_module() -> Result<()> {
 
     let module = Module::new(&store, wat)?;
     let instance = Instance::new(&module, &[])?;
-    let bar = instance.exports()[0].clone();
+    let bar = instance.get_export("bar").unwrap();
 
     let wat = r#"
         (module $b
@@ -203,9 +193,7 @@ fn trap_display_multi_module() -> Result<()> {
     "#;
     let module = Module::new(&store, wat)?;
     let instance = Instance::new(&module, &[bar])?;
-    let bar2 = instance.exports()[0]
-        .func()
-        .expect("expected function export");
+    let bar2 = instance.get_func("bar2").expect("expected function export");
 
     let e = bar2.call(&[]).err().expect("error calling function");
     assert_eq!(
@@ -268,14 +256,14 @@ fn rust_panic_import() -> Result<()> {
             Func::wrap(&store, || panic!("this is another panic")).into(),
         ],
     )?;
-    let func = instance.exports()[0].func().unwrap().clone();
+    let func = instance.get_func("foo").unwrap();
     let err = panic::catch_unwind(AssertUnwindSafe(|| {
         drop(func.call(&[]));
     }))
     .unwrap_err();
     assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
 
-    let func = instance.exports()[1].func().unwrap().clone();
+    let func = instance.get_func("bar").unwrap();
     let err = panic::catch_unwind(AssertUnwindSafe(|| {
         drop(func.call(&[]));
     }))
@@ -333,7 +321,7 @@ fn mismatched_arguments() -> Result<()> {
 
     let module = Module::new(&store, &binary)?;
     let instance = Instance::new(&module, &[])?;
-    let func = instance.exports()[0].func().unwrap().clone();
+    let func = instance.get_func("foo").unwrap();
     assert_eq!(
         func.call(&[]).unwrap_err().to_string(),
         "expected 1 arguments, got 0"
@@ -417,7 +405,7 @@ fn present_after_module_drop() -> Result<()> {
     let store = Store::default();
     let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?;
     let instance = Instance::new(&module, &[])?;
-    let func = instance.exports()[0].func().unwrap().clone();
+    let func = instance.get_func("foo").unwrap();
 
     println!("asserting before we drop modules");
     assert_trap(func.call(&[]).unwrap_err().downcast()?);
diff --git a/tests/misc_testsuite/threads.wast b/tests/misc_testsuite/threads.wast
index 4c98ae2c3..035cc529e 100644
--- a/tests/misc_testsuite/threads.wast
+++ b/tests/misc_testsuite/threads.wast
@@ -1,1 +1,1 @@
-(assert_invalid (module (memory 1 1 shared)) "not supported")
+(assert_invalid (module (memory 1 1 shared)) "Unsupported feature: shared memories")