Skip to content

Commit

Permalink
Clean up and improve host function trampoline API (#664)
Browse files Browse the repository at this point in the history
* rename FuncResults -> FuncFinished

* clean up and improve host function trampoline API

* apply rustfmt
  • Loading branch information
Robbepop authored Feb 10, 2023
1 parent e840f78 commit 852f14a
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 56 deletions.
110 changes: 65 additions & 45 deletions crates/wasmi/src/engine/func_args.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! API using the Rust type system to guide host function trampoline execution.

use crate::{value::WithType, Value};
use core::cmp;
use wasmi_core::{DecodeUntypedSlice, EncodeUntypedSlice, UntypedError, UntypedValue};

/// Used to decode host function parameters.
#[derive(Debug)]
pub struct FuncParams<'a> {
/// Slice holding the raw (encoded but untyped) parameters
Expand All @@ -18,11 +21,55 @@ pub struct FuncParams<'a> {
len_results: usize,
}

/// Utility type to ensure at compile time that host functions always call
/// [`FuncParams::encode_results`] or [`FuncParams::encode_results_from_slice`]
/// Used to encode host function results.
#[derive(Debug)]
pub struct FuncResults<'a> {
results: &'a mut [UntypedValue],
}

impl<'a> FuncResults<'a> {
/// Create new [`FuncResults`] from the given `results` slice.
fn new(results: &'a mut [UntypedValue]) -> Self {
Self { results }
}

/// Encodes the results of the host function invocation as `T`.
///
/// # Panics
///
/// If the number of results dictated by `T` does not match the expected amount.
pub fn encode_results<T>(self, values: T) -> FuncFinished
where
T: EncodeUntypedSlice,
{
UntypedValue::encode_slice::<T>(self.results, values)
.unwrap_or_else(|error| panic!("encountered unexpected invalid tuple length: {error}"));
FuncFinished {}
}

/// Encodes the results of the host function invocation given the `values` slice.
///
/// # Panics
///
/// If the number of expected results does not match the length of `values`.
pub fn encode_results_from_slice(self, values: &[Value]) -> Result<FuncFinished, UntypedError> {
assert_eq!(self.results.len(), values.len());
self.results.iter_mut().zip(values).for_each(|(dst, src)| {
*dst = src.clone().into();
});
Ok(FuncFinished {})
}
}

/// Used to guarantee by the type system that this API has been used correctly.
///
/// Ensures at compile time that host functions always call
/// [`FuncParams::decode_params`] or [`FuncParams::decode_params_into_slice`]
/// followed by
/// [`FuncResults::encode_results`] or [`FuncResults::encode_results_from_slice`]
/// at the end of their execution.
#[derive(Debug)]
pub struct FuncResults {}
pub struct FuncFinished {}

impl<'a> FuncParams<'a> {
/// Create new [`FuncParams`].
Expand All @@ -31,7 +78,7 @@ impl<'a> FuncParams<'a> {
///
/// If the length of hte `params_results` slice does not match the maximum
/// of the `len_params` and `Len_results`.
pub fn new(
pub(super) fn new(
params_results: &'a mut [UntypedValue],
len_params: usize,
len_results: usize,
Expand All @@ -49,67 +96,40 @@ impl<'a> FuncParams<'a> {
&self.params_results[..self.len_params]
}

/// Returns an exclusive reference to the slice of function results.
fn results(&mut self) -> &mut [UntypedValue] {
&mut self.params_results[..self.len_results]
}

/// Decodes and returns the executed host function parameters as `T`.
///
/// # Panics
///
/// If the number of function parameters dictated by `T` does not match.
pub fn decode_params<T>(&self) -> T
pub fn decode_params<T>(self) -> (T, FuncResults<'a>)
where
T: DecodeUntypedSlice,
{
UntypedValue::decode_slice::<T>(self.params())
.unwrap_or_else(|error| panic!("encountered unexpected invalid tuple length: {error}"))
let decoded = UntypedValue::decode_slice::<T>(self.params())
.unwrap_or_else(|error| panic!("encountered unexpected invalid tuple length: {error}"));
let results = self.into_func_results();
(decoded, results)
}

/// Decodes and stores the executed host functions parameters into `values`.
///
/// # Panics
///
/// If the number of host function parameters and items in `values` does not match.
pub fn decode_params_into_slice(&self, values: &mut [Value]) -> Result<(), UntypedError> {
pub fn decode_params_into_slice(
self,
values: &mut [Value],
) -> Result<FuncResults<'a>, UntypedError> {
assert_eq!(self.params().len(), values.len());
self.params().iter().zip(values).for_each(|(src, dst)| {
*dst = src.with_type(dst.ty());
});
Ok(())
}

/// Encodes the results of the host function invocation as `T`.
///
/// # Panics
///
/// If the number of results dictated by `T` does not match the expected amount.
pub fn encode_results<T>(mut self, values: T) -> FuncResults
where
T: EncodeUntypedSlice,
{
UntypedValue::encode_slice::<T>(self.results(), values)
.unwrap_or_else(|error| panic!("encountered unexpected invalid tuple length: {error}"));
FuncResults {}
let results = self.into_func_results();
Ok(results)
}

/// Encodes the results of the host function invocation given the `values` slice.
///
/// # Panics
///
/// If the number of expected results does not match the length of `values`.
pub fn encode_results_from_slice(
mut self,
values: &[Value],
) -> Result<FuncResults, UntypedError> {
assert_eq!(self.results().len(), values.len());
self.results()
.iter_mut()
.zip(values)
.for_each(|(dst, src)| {
*dst = src.clone().into();
});
Ok(FuncResults {})
/// Consumes `self` to return the [`FuncResults`] out of it.
fn into_func_results(self) -> FuncResults<'a> {
FuncResults::new(&mut self.params_results[..self.len_results])
}
}
2 changes: 1 addition & 1 deletion crates/wasmi/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use self::{
stack::{FuncFrame, Stack, ValueStack},
};
pub(crate) use self::{
func_args::{FuncParams, FuncResults},
func_args::{FuncFinished, FuncParams, FuncResults},
func_types::DedupFuncType,
};
use super::{func::FuncEntityInternal, AsContextMut, Func};
Expand Down
8 changes: 4 additions & 4 deletions crates/wasmi/src/func/into_func.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{
super::engine::{FuncParams, FuncResults},
super::engine::{FuncFinished, FuncParams, FuncResults},
HostFuncTrampoline,
};
use crate::{
Expand Down Expand Up @@ -75,11 +75,11 @@ macro_rules! impl_into_func {
<Self::Results as WasmTypeList>::types(),
);
let trampoline = HostFuncTrampoline::new(
move |caller: Caller<T>, params_results: FuncParams| -> Result<FuncResults, Trap> {
let ($($tuple,)*): Self::Params = params_results.decode_params();
move |caller: Caller<T>, params_results: FuncParams| -> Result<FuncFinished, Trap> {
let (($($tuple,)*), func_results): (Self::Params, FuncResults) = params_results.decode_params();
let results: Self::Results =
(self)(caller, $($tuple),*).into_fallible()?;
Ok(params_results.encode_results(results))
Ok(func_results.encode_results(results))
},
);
(signature, trampoline)
Expand Down
12 changes: 6 additions & 6 deletions crates/wasmi/src/func/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use self::{
typed_func::{TypedFunc, WasmParams, WasmResults},
};
use super::{
engine::{DedupFuncType, FuncBody, FuncParams, FuncResults},
engine::{DedupFuncType, FuncBody, FuncFinished, FuncParams},
AsContext,
AsContextMut,
Instance,
Expand Down Expand Up @@ -182,7 +182,7 @@ impl<T> Clone for HostFuncEntity<T> {
}

type HostFuncTrampolineFn<T> =
dyn Fn(Caller<T>, FuncParams) -> Result<FuncResults, Trap> + Send + Sync + 'static;
dyn Fn(Caller<T>, FuncParams) -> Result<FuncFinished, Trap> + Send + Sync + 'static;

pub struct HostFuncTrampoline<T> {
closure: Arc<HostFuncTrampolineFn<T>>,
Expand All @@ -192,7 +192,7 @@ impl<T> HostFuncTrampoline<T> {
/// Creates a new [`HostFuncTrampoline`] from the given trampoline function.
pub fn new<F>(trampoline: F) -> Self
where
F: Fn(Caller<T>, FuncParams) -> Result<FuncResults, Trap> + Send + Sync + 'static,
F: Fn(Caller<T>, FuncParams) -> Result<FuncFinished, Trap> + Send + Sync + 'static,
{
Self {
closure: Arc::new(trampoline),
Expand Down Expand Up @@ -236,9 +236,9 @@ impl<T> HostFuncEntity<T> {
// comes with its own downsides.
let mut params_results = params_results.clone();
let (params, results) = params_results.split_at_mut(len_params);
args.decode_params_into_slice(params).unwrap();
let func_results = args.decode_params_into_slice(params).unwrap();
func(caller, params, results)?;
Ok(args.encode_results_from_slice(results).unwrap())
Ok(func_results.encode_results_from_slice(results).unwrap())
});
let signature = ctx.as_context_mut().store.inner.alloc_func_type(ty.clone());
Self {
Expand Down Expand Up @@ -273,7 +273,7 @@ impl<T> HostFuncEntity<T> {
mut ctx: impl AsContextMut<UserState = T>,
instance: Option<&Instance>,
params: FuncParams,
) -> Result<FuncResults, Trap> {
) -> Result<FuncFinished, Trap> {
let caller = <Caller<T>>::new(&mut ctx, instance);
(self.trampoline.closure)(caller, params)
}
Expand Down

0 comments on commit 852f14a

Please sign in to comment.