wasmi/func/mod.rs
1mod caller;
2mod error;
3mod func_type;
4mod funcref;
5mod into_func;
6mod typed_func;
7
8pub(crate) use self::typed_func::CallResultsTuple;
9pub use self::{
10 caller::Caller,
11 error::FuncError,
12 func_type::FuncType,
13 funcref::FuncRef,
14 into_func::{IntoFunc, WasmRet, WasmTy, WasmTyList},
15 typed_func::{TypedFunc, WasmParams, WasmResults},
16};
17use super::{
18 engine::{CompiledFunc, DedupFuncType, FuncFinished, FuncParams},
19 AsContext,
20 AsContextMut,
21 Instance,
22 StoreContext,
23 Stored,
24};
25use crate::{collections::arena::ArenaIndex, engine::ResumableCall, Error, Val};
26use core::{fmt, fmt::Debug, num::NonZeroU32};
27use std::{boxed::Box, sync::Arc};
28
29/// A raw index to a function entity.
30#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
31pub struct FuncIdx(NonZeroU32);
32
33impl ArenaIndex for FuncIdx {
34 fn into_usize(self) -> usize {
35 self.0.get().wrapping_sub(1) as usize
36 }
37
38 fn from_usize(index: usize) -> Self {
39 index
40 .try_into()
41 .ok()
42 .map(|index: u32| index.wrapping_add(1))
43 .and_then(NonZeroU32::new)
44 .map(Self)
45 .unwrap_or_else(|| panic!("out of bounds func index {index}"))
46 }
47}
48
49/// A raw index to a host function trampoline entity.
50#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
51pub struct TrampolineIdx(usize);
52
53impl ArenaIndex for TrampolineIdx {
54 fn into_usize(self) -> usize {
55 self.0
56 }
57
58 fn from_usize(index: usize) -> Self {
59 Self(index)
60 }
61}
62
63/// A host function reference.
64#[derive(Debug, Copy, Clone)]
65#[repr(transparent)]
66pub struct Trampoline(Stored<TrampolineIdx>);
67
68impl Trampoline {
69 /// Creates a new host function reference.
70 pub(super) fn from_inner(stored: Stored<TrampolineIdx>) -> Self {
71 Self(stored)
72 }
73
74 /// Returns the underlying stored representation.
75 pub(super) fn as_inner(&self) -> &Stored<TrampolineIdx> {
76 &self.0
77 }
78}
79
80/// A Wasm or host function instance.
81#[derive(Debug)]
82pub enum FuncEntity {
83 /// A Wasm function.
84 Wasm(WasmFuncEntity),
85 /// A host function.
86 Host(HostFuncEntity),
87}
88
89impl From<WasmFuncEntity> for FuncEntity {
90 fn from(func: WasmFuncEntity) -> Self {
91 Self::Wasm(func)
92 }
93}
94
95impl From<HostFuncEntity> for FuncEntity {
96 fn from(func: HostFuncEntity) -> Self {
97 Self::Host(func)
98 }
99}
100
101/// A host function reference and its function type.
102#[derive(Debug, Copy, Clone)]
103pub struct HostFuncEntity {
104 /// The function type of the host function.
105 ty: DedupFuncType,
106 /// A reference to the trampoline of the host function.
107 func: Trampoline,
108}
109
110impl HostFuncEntity {
111 /// Creates a new [`HostFuncEntity`].
112 pub fn new(ty: DedupFuncType, func: Trampoline) -> Self {
113 Self { ty, func }
114 }
115
116 /// Returns the signature of the host function.
117 pub fn ty_dedup(&self) -> &DedupFuncType {
118 &self.ty
119 }
120
121 /// Returns the [`Trampoline`] of the host function.
122 pub fn trampoline(&self) -> &Trampoline {
123 &self.func
124 }
125}
126
127impl FuncEntity {
128 /// Returns the signature of the Wasm function.
129 pub fn ty_dedup(&self) -> &DedupFuncType {
130 match self {
131 Self::Wasm(func) => func.ty_dedup(),
132 Self::Host(func) => func.ty_dedup(),
133 }
134 }
135}
136
137/// A Wasm function instance.
138#[derive(Debug, Clone)]
139pub struct WasmFuncEntity {
140 /// The function type of the Wasm function.
141 ty: DedupFuncType,
142 /// The compiled function body of the Wasm function.
143 body: CompiledFunc,
144 /// The instance associated to the Wasm function.
145 instance: Instance,
146}
147
148impl WasmFuncEntity {
149 /// Creates a new Wasm function from the given raw parts.
150 pub fn new(signature: DedupFuncType, body: CompiledFunc, instance: Instance) -> Self {
151 Self {
152 ty: signature,
153 body,
154 instance,
155 }
156 }
157
158 /// Returns the signature of the Wasm function.
159 pub fn ty_dedup(&self) -> &DedupFuncType {
160 &self.ty
161 }
162
163 /// Returns the instance where the [`Func`] belong to.
164 pub fn instance(&self) -> &Instance {
165 &self.instance
166 }
167
168 /// Returns the Wasm function body of the [`Func`].
169 pub fn func_body(&self) -> CompiledFunc {
170 self.body
171 }
172}
173
174/// A host function instance.
175pub struct HostFuncTrampolineEntity<T> {
176 /// The type of the associated host function.
177 ty: FuncType,
178 /// The trampoline of the associated host function.
179 trampoline: TrampolineEntity<T>,
180}
181
182impl<T> Clone for HostFuncTrampolineEntity<T> {
183 fn clone(&self) -> Self {
184 Self {
185 ty: self.ty.clone(),
186 trampoline: self.trampoline.clone(),
187 }
188 }
189}
190
191impl<T> Debug for HostFuncTrampolineEntity<T> {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 Debug::fmt(&self.ty, f)
194 }
195}
196
197impl<T> HostFuncTrampolineEntity<T> {
198 /// Creates a new host function trampoline from the given dynamically typed closure.
199 pub fn new(
200 // engine: &Engine,
201 ty: FuncType,
202 func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Error> + Send + Sync + 'static,
203 ) -> Self {
204 // Preprocess parameters and results buffers so that we can reuse those
205 // computations within the closure implementation. We put both parameters
206 // and results into a single buffer which we can split to minimize the
207 // amount of allocations per trampoline invokation.
208 let params_iter = ty.params().iter().copied().map(Val::default);
209 let results_iter = ty.results().iter().copied().map(Val::default);
210 let len_params = ty.params().len();
211 let params_results: Box<[Val]> = params_iter.chain(results_iter).collect();
212 let trampoline = <TrampolineEntity<T>>::new(move |caller, args| {
213 // We are required to clone the buffer because we are operating within a `Fn`.
214 // This way the trampoline closure only has to own a single slice buffer.
215 // Note: An alternative solution is to use interior mutability but that solution
216 // comes with its own downsides.
217 let mut params_results = params_results.clone();
218 let (params, results) = params_results.split_at_mut(len_params);
219 let func_results = args.decode_params_into_slice(params).unwrap();
220 func(caller, params, results)?;
221 Ok(func_results.encode_results_from_slice(results).unwrap())
222 });
223 // let ty = engine.alloc_func_type(ty.clone());
224 Self { ty, trampoline }
225 }
226
227 /// Creates a new host function trampoline from the given statically typed closure.
228 pub fn wrap<Params, Results>(func: impl IntoFunc<T, Params, Results>) -> Self {
229 let (ty, trampoline) = func.into_func();
230 // let ty = engine.alloc_func_type(signature);
231 Self { ty, trampoline }
232 }
233
234 /// Returns the [`FuncType`] of the host function.
235 pub fn func_type(&self) -> &FuncType {
236 &self.ty
237 }
238
239 /// Returns the trampoline of the host function.
240 pub fn trampoline(&self) -> &TrampolineEntity<T> {
241 &self.trampoline
242 }
243}
244
245type TrampolineFn<T> =
246 dyn Fn(Caller<T>, FuncParams) -> Result<FuncFinished, Error> + Send + Sync + 'static;
247
248pub struct TrampolineEntity<T> {
249 closure: Arc<TrampolineFn<T>>,
250}
251
252impl<T> Debug for TrampolineEntity<T> {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 f.debug_struct("TrampolineEntity").finish()
255 }
256}
257
258impl<T> TrampolineEntity<T> {
259 /// Creates a new [`TrampolineEntity`] from the given host function.
260 pub fn new<F>(trampoline: F) -> Self
261 where
262 F: Fn(Caller<T>, FuncParams) -> Result<FuncFinished, Error> + Send + Sync + 'static,
263 {
264 Self {
265 closure: Arc::new(trampoline),
266 }
267 }
268
269 /// Calls the host function trampoline with the given inputs.
270 ///
271 /// The result is written back into the `outputs` buffer.
272 pub fn call(
273 &self,
274 mut ctx: impl AsContextMut<Data = T>,
275 instance: Option<&Instance>,
276 params: FuncParams,
277 ) -> Result<FuncFinished, Error> {
278 let caller = <Caller<T>>::new(&mut ctx, instance);
279 (self.closure)(caller, params)
280 }
281}
282
283impl<T> Clone for TrampolineEntity<T> {
284 fn clone(&self) -> Self {
285 Self {
286 closure: self.closure.clone(),
287 }
288 }
289}
290
291/// A Wasm or host function reference.
292#[derive(Debug, Copy, Clone)]
293#[repr(transparent)]
294pub struct Func(Stored<FuncIdx>);
295
296impl Func {
297 /// Creates a new Wasm or host function reference.
298 pub(super) fn from_inner(stored: Stored<FuncIdx>) -> Self {
299 Self(stored)
300 }
301
302 /// Returns the underlying stored representation.
303 pub(super) fn as_inner(&self) -> &Stored<FuncIdx> {
304 &self.0
305 }
306
307 /// Creates a new [`Func`] with the given arguments.
308 ///
309 /// This is typically used to create a host-defined function to pass as an import to a Wasm module.
310 ///
311 /// - `ty`: the signature that the given closure adheres to,
312 /// used to indicate what the inputs and outputs are.
313 /// - `func`: the native code invoked whenever this Func will be called.
314 /// The closure is provided a [`Caller`] as its first argument
315 /// which allows it to query information about the [`Instance`]
316 /// that is associated to the call.
317 ///
318 /// # Note
319 ///
320 /// - The given [`FuncType`] `ty` must match the parameters and results otherwise
321 /// the resulting host [`Func`] might trap during execution.
322 /// - It is the responsibility of the caller of [`Func::new`] to guarantee that
323 /// the correct amount and types of results are written into the results buffer
324 /// from the `func` closure. If an incorrect amount of results or types of results
325 /// is written into the buffer then the remaining computation may fail in unexpected
326 /// ways. This footgun can be avoided by using the typed [`Func::wrap`] method instead.
327 /// - Prefer using [`Func::wrap`] over this method if possible since [`Func`] instances
328 /// created using this constructor have runtime overhead for every invocation that
329 /// can be avoided by using [`Func::wrap`].
330 pub fn new<T>(
331 mut ctx: impl AsContextMut<Data = T>,
332 ty: FuncType,
333 func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Error> + Send + Sync + 'static,
334 ) -> Self {
335 let engine = ctx.as_context().store.engine();
336 let host_func = HostFuncTrampolineEntity::new(ty, func);
337 let ty_dedup = engine.alloc_func_type(host_func.func_type().clone());
338 let trampoline = host_func.trampoline().clone();
339 let func = ctx.as_context_mut().store.alloc_trampoline(trampoline);
340 ctx.as_context_mut()
341 .store
342 .inner
343 .alloc_func(HostFuncEntity::new(ty_dedup, func).into())
344 }
345
346 /// Creates a new host function from the given closure.
347 pub fn wrap<T, Params, Results>(
348 mut ctx: impl AsContextMut<Data = T>,
349 func: impl IntoFunc<T, Params, Results>,
350 ) -> Self {
351 let engine = ctx.as_context().store.engine();
352 let host_func = HostFuncTrampolineEntity::wrap(func);
353 let ty_dedup = engine.alloc_func_type(host_func.func_type().clone());
354 let trampoline = host_func.trampoline().clone();
355 let func = ctx.as_context_mut().store.alloc_trampoline(trampoline);
356 ctx.as_context_mut()
357 .store
358 .inner
359 .alloc_func(HostFuncEntity::new(ty_dedup, func).into())
360 }
361
362 /// Returns the signature of the function.
363 pub(crate) fn ty_dedup<'a, T: 'a>(
364 &self,
365 ctx: impl Into<StoreContext<'a, T>>,
366 ) -> &'a DedupFuncType {
367 ctx.into().store.inner.resolve_func(self).ty_dedup()
368 }
369
370 /// Returns the function type of the [`Func`].
371 pub fn ty(&self, ctx: impl AsContext) -> FuncType {
372 ctx.as_context()
373 .store
374 .inner
375 .resolve_func_type(self.ty_dedup(&ctx))
376 }
377
378 /// Calls the Wasm or host function with the given inputs.
379 ///
380 /// The result is written back into the `outputs` buffer.
381 ///
382 /// # Errors
383 ///
384 /// - If the function returned a [`Error`].
385 /// - If the types of the `inputs` do not match the expected types for the
386 /// function signature of `self`.
387 /// - If the number of input values does not match the expected number of
388 /// inputs required by the function signature of `self`.
389 /// - If the number of output values does not match the expected number of
390 /// outputs required by the function signature of `self`.
391 pub fn call<T>(
392 &self,
393 mut ctx: impl AsContextMut<Data = T>,
394 inputs: &[Val],
395 outputs: &mut [Val],
396 ) -> Result<(), Error> {
397 self.verify_and_prepare_inputs_outputs(ctx.as_context(), inputs, outputs)?;
398 // Note: Cloning an [`Engine`] is intentionally a cheap operation.
399 ctx.as_context().store.engine().clone().execute_func(
400 ctx.as_context_mut(),
401 self,
402 inputs,
403 outputs,
404 )?;
405 Ok(())
406 }
407
408 /// Calls the Wasm or host function with the given inputs.
409 ///
410 /// The result is written back into the `outputs` buffer.
411 ///
412 /// Returns a resumable handle to the function invocation upon
413 /// encountering host errors with which it is possible to handle
414 /// the error and continue the execution as if no error occurred.
415 ///
416 /// # Note
417 ///
418 /// This is a non-standard WebAssembly API and might not be available
419 /// at other WebAssembly engines. Please be aware that depending on this
420 /// feature might mean a lock-in to Wasmi for users.
421 ///
422 /// # Errors
423 ///
424 /// - If the function returned a Wasm [`Error`].
425 /// - If the types of the `inputs` do not match the expected types for the
426 /// function signature of `self`.
427 /// - If the number of input values does not match the expected number of
428 /// inputs required by the function signature of `self`.
429 /// - If the number of output values does not match the expected number of
430 /// outputs required by the function signature of `self`.
431 pub fn call_resumable<T>(
432 &self,
433 mut ctx: impl AsContextMut<Data = T>,
434 inputs: &[Val],
435 outputs: &mut [Val],
436 ) -> Result<ResumableCall, Error> {
437 self.verify_and_prepare_inputs_outputs(ctx.as_context(), inputs, outputs)?;
438 // Note: Cloning an [`Engine`] is intentionally a cheap operation.
439 ctx.as_context()
440 .store
441 .engine()
442 .clone()
443 .execute_func_resumable(ctx.as_context_mut(), self, inputs, outputs)
444 .map_err(Into::into)
445 .map(ResumableCall::new)
446 }
447
448 /// Verify that the `inputs` and `outputs` value types match the function signature.
449 ///
450 /// Since [`Func`] is a dynamically typed function instance there is
451 /// a need to verify that the given input parameters match the required
452 /// types and that the given output slice matches the expected length.
453 ///
454 /// These checks can be avoided using the [`TypedFunc`] API.
455 ///
456 /// # Errors
457 ///
458 /// - If the `inputs` value types do not match the function input types.
459 /// - If the number of `inputs` do not match the function input types.
460 /// - If the number of `outputs` do not match the function output types.
461 fn verify_and_prepare_inputs_outputs(
462 &self,
463 ctx: impl AsContext,
464 inputs: &[Val],
465 outputs: &mut [Val],
466 ) -> Result<(), FuncError> {
467 let fn_type = self.ty_dedup(ctx.as_context());
468 ctx.as_context()
469 .store
470 .inner
471 .resolve_func_type_with(fn_type, |func_type| {
472 func_type.match_params(inputs)?;
473 func_type.match_results(outputs, false)?;
474 func_type.prepare_outputs(outputs);
475 Ok(())
476 })
477 }
478
479 /// Creates a new [`TypedFunc`] from this [`Func`].
480 ///
481 /// # Note
482 ///
483 /// This performs static type checks given `Params` as parameter types
484 /// to [`Func`] and `Results` as result types of [`Func`] so that those
485 /// type checks can be avoided when calling the created [`TypedFunc`].
486 ///
487 /// # Errors
488 ///
489 /// If the function signature of `self` does not match `Params` and `Results`
490 /// as parameter types and result types respectively.
491 pub fn typed<Params, Results>(
492 &self,
493 ctx: impl AsContext,
494 ) -> Result<TypedFunc<Params, Results>, Error>
495 where
496 Params: WasmParams,
497 Results: WasmResults,
498 {
499 TypedFunc::new(ctx, *self)
500 }
501}