diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index af070727af1b..e23f79d708dd 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -2,14 +2,16 @@ use crate::linker::{Definition, DefinitionType}; use crate::store::{InstanceId, StoreOpaque, Stored}; use crate::types::matching; use crate::{ - AsContextMut, Engine, Export, Extern, Func, Global, Memory, Module, SharedMemory, StoreContext, - StoreContextMut, Table, TypedFunc, + AsContextMut, Engine, Export, Extern, Func, Global, Memory, Module, ModuleExport, SharedMemory, + StoreContext, StoreContextMut, Table, TypedFunc, }; use anyhow::{anyhow, bail, Context, Result}; use std::mem; use std::ptr::NonNull; use std::sync::Arc; -use wasmtime_environ::{EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex}; +use wasmtime_environ::{ + EntityIndex, EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex, +}; use wasmtime_runtime::{ Imports, InstanceAllocationRequest, StorePtr, VMContext, VMFuncRef, VMFunctionImport, VMGlobalImport, VMMemoryImport, VMNativeCallFunction, VMOpaqueContext, VMTableImport, @@ -395,8 +397,16 @@ impl Instance { let InstanceData { exports, id, .. } = &store[self.0]; if exports.iter().any(|e| e.is_none()) { let module = Arc::clone(store.instance(*id).module()); + let data = &store[self.0]; + let id = data.id; + for name in module.exports.keys() { - self._get_export(store, name); + let instance = store.instance(id); + if let Some((export_name_index, _, &entity)) = + instance.module().exports.get_full(name) + { + self._get_export(store, entity, export_name_index); + } } } @@ -427,26 +437,60 @@ impl Instance { /// instantiating a module faster, but also means this method requires a /// mutable context. pub fn get_export(&self, mut store: impl AsContextMut, name: &str) -> Option { - self._get_export(store.as_context_mut().0, name) + let store = store.as_context_mut().0; + let data = &store[self.0]; + let instance = store.instance(data.id); + let (export_name_index, _, &entity) = instance.module().exports.get_full(name)?; + self._get_export(store, entity, export_name_index) + } + + /// Looks up an exported [`Extern`] value by a [`ModuleExport`] value. + /// + /// This is similar to [`Instance::get_export`] but uses a [`ModuleExport`] value to avoid + /// string lookups where possible. [`ModuleExport`]s can be obtained by calling + /// [`Module::get_export_index`] on the [`Module`] that this instance was instantiated with. + /// + /// This method will search the module for an export with a matching entity index and return + /// the value, if found. + /// + /// Returns `None` if there was no export with a matching entity index. + /// # Panics + /// + /// Panics if `store` does not own this instance. + pub fn get_module_export( + &self, + mut store: impl AsContextMut, + export: &ModuleExport, + ) -> Option { + let store = store.as_context_mut().0; + + // Verify the `ModuleExport` matches the module used in this instance. + if self._module(store).id() != export.module { + return None; + } + + self._get_export(store, export.entity, export.export_name_index) } - fn _get_export(&self, store: &mut StoreOpaque, name: &str) -> Option { + fn _get_export( + &self, + store: &mut StoreOpaque, + entity: EntityIndex, + export_name_index: usize, + ) -> Option { // Instantiated instances will lazily fill in exports, so we process // all that lazy logic here. let data = &store[self.0]; - let instance = store.instance(data.id); - let (i, _, &index) = instance.module().exports.get_full(name)?; - if let Some(export) = &data.exports[i] { + if let Some(export) = &data.exports[export_name_index] { return Some(export.clone()); } - let id = data.id; - let instance = store.instance_mut(id); // reborrow the &mut Instancehandle + let instance = store.instance_mut(data.id); // Reborrow the &mut InstanceHandle let item = - unsafe { Extern::from_wasmtime_export(instance.get_export_by_index(index), store) }; + unsafe { Extern::from_wasmtime_export(instance.get_export_by_index(entity), store) }; let data = &mut store[self.0]; - data.exports[i] = Some(item.clone()); + data.exports[export_name_index] = Some(item.clone()); Some(item) } diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 28a455b60271..983466341302 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -454,7 +454,7 @@ pub use crate::instantiate::CompiledModule; pub use crate::limits::*; pub use crate::linker::*; pub use crate::memory::*; -pub use crate::module::Module; +pub use crate::module::{Module, ModuleExport}; #[cfg(feature = "profiling")] pub use crate::profiling::GuestProfiler; pub use crate::r#ref::ExternRef; diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index d27122f6c6de..fa86932c6ba5 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -17,8 +17,8 @@ use std::ptr::NonNull; use std::sync::Arc; use wasmparser::{Parser, ValidPayload, Validator}; use wasmtime_environ::{ - CompiledModuleInfo, DefinedFuncIndex, DefinedMemoryIndex, HostPtr, ModuleEnvironment, - ModuleTypes, ObjectKind, VMOffsets, + CompiledModuleInfo, DefinedFuncIndex, DefinedMemoryIndex, EntityIndex, HostPtr, + ModuleEnvironment, ModuleTypes, ObjectKind, VMOffsets, }; use wasmtime_runtime::{ CompiledModuleId, MemoryImage, MmapVec, ModuleMemoryImages, VMArrayCallFunction, @@ -897,6 +897,25 @@ impl Module { )) } + /// Looks up an export in this [`Module`] by name to get its index. + /// + /// This function will return the index of an export with the given name. This can be useful + /// to avoid the cost of looking up the export by name multiple times. Instead the + /// [`ModuleExport`] can be stored and used to look up the export on the + /// [`Instance`](crate::Instance) later. + pub fn get_export_index(&self, name: &str) -> Option { + let compiled_module = self.compiled_module(); + let module = compiled_module.module(); + module + .exports + .get_full(name) + .map(|(export_name_index, _, &entity)| ModuleExport { + module: self.id(), + entity, + export_name_index, + }) + } + /// Returns the [`Engine`] that this [`Module`] was compiled by. pub fn engine(&self) -> &Engine { &self.inner.engine @@ -1117,6 +1136,17 @@ impl Drop for ModuleInner { } } +/// Describes the location of an export in a module. +#[derive(Copy, Clone)] +pub struct ModuleExport { + /// The module that this export is defined in. + pub(crate) module: CompiledModuleId, + /// A raw index into the wasm module. + pub(crate) entity: EntityIndex, + /// The index of the export name. + pub(crate) export_name_index: usize, +} + fn _assert_send_sync() { fn _assert() {} _assert::();