diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 3f4b2ad496d9..023cbdac4640 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -183,7 +183,12 @@ pub struct Func(Stored); pub(crate) struct FuncData { kind: FuncKind, - ty: FuncType, + + // This is somewhat expensive to load from the `Engine` and in most + // optimized use cases (e.g. `TypedFunc`) it's not actually needed or it's + // only needed rarely. To handle that this is an optionally-contained field + // which is lazily loaded into as part of `Func::call`. + ty: Option, } /// The three ways that a function can be created and referenced from within a @@ -677,7 +682,39 @@ impl Func { /// /// Panics if `store` does not own this function. pub fn ty(&self, store: impl AsContext) -> FuncType { - store.as_context()[self.0].ty.clone() + self.load_ty(&store.as_context().0) + } + + /// Forcibly loads the type of this function from the `Engine`. + /// + /// Note that this is a somewhat expensive method since it requires taking a + /// lock as well as cloning a type. + fn load_ty(&self, store: &StoreOpaque) -> FuncType { + FuncType::from_wasm_func_type( + store + .engine() + .signatures() + .lookup_type(self.sig_index(store.store_data())) + .expect("signature should be registered"), + ) + } + + /// Gets a reference to the `FuncType` for this function. + /// + /// Note that this returns both a reference to the type of this function as + /// well as a reference back to the store itself. This enables using the + /// `StoreOpaque` while the `FuncType` is also being used (from the + /// perspective of the borrow-checker) because otherwise the signature would + /// consider `StoreOpaque` borrowed mutable while `FuncType` is in use. + fn ty_ref<'a>(&self, store: &'a mut StoreOpaque) -> (&'a FuncType, &'a StoreOpaque) { + // If we haven't loaded our type into the store yet then do so lazily at + // this time. + if store.store_data()[self.0].ty.is_none() { + let ty = self.load_ty(store); + store.store_data_mut()[self.0].ty = Some(ty); + } + + (store.store_data()[self.0].ty.as_ref().unwrap(), store) } pub(crate) fn sig_index(&self, data: &StoreData) -> VMSharedSignatureIndex { @@ -843,7 +880,7 @@ 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`. - let ty = &store[self.0].ty; + let (ty, opaque) = self.ty_ref(store.0); if ty.params().len() != params.len() { bail!( "expected {} arguments, got {}", @@ -866,7 +903,7 @@ impl Func { ty ); } - if !arg.comes_from_same_store(store.0) { + if !arg.comes_from_same_store(opaque) { bail!("cross-`Store` values are not currently supported"); } } @@ -904,7 +941,7 @@ impl Func { } for ((i, slot), val) in results.iter_mut().enumerate().zip(&values_vec) { - let ty = store[self.0].ty.results().nth(i).unwrap(); + let ty = self.ty_ref(store.0).0.results().nth(i).unwrap(); *slot = unsafe { Val::from_raw(&mut *store, *val, ty) }; } values_vec.truncate(0); @@ -930,17 +967,7 @@ impl Func { } fn from_func_kind(kind: FuncKind, store: &mut StoreOpaque) -> Self { - // Signatures should always be registered in the engine's registry of - // shared signatures, so we should be able to unwrap safely here. - let ty = unsafe { kind.export().anyfunc.as_ref().type_index }; - let ty = FuncType::from_wasm_func_type( - store - .engine() - .signatures() - .lookup_type(ty) - .expect("signature should be registered"), - ); - Func(store.store_data_mut().insert(FuncData { kind, ty })) + Func(store.store_data_mut().insert(FuncData { kind, ty: None })) } pub(crate) fn vmimport(&self, store: &mut StoreOpaque) -> VMFunctionImport {