Skip to content

Commit b99b643

Browse files
feat: add WasmTupleChain
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent 539167f commit b99b643

3 files changed

Lines changed: 215 additions & 53 deletions

File tree

crates/tinywasm/src/func.rs

Lines changed: 142 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,13 @@ impl HostFunction {
152152
/// Create a new typed host function import.
153153
pub fn from<P, R>(store: &mut Store, func: impl Fn(FuncContext<'_>, P) -> Result<R> + 'static) -> Function
154154
where
155-
P: FromWasmValueTuple + WasmTypesFromTuple,
156-
R: IntoWasmValueTuple + WasmTypesFromTuple,
155+
P: FromWasmValues + ToWasmTypes,
156+
R: IntoWasmValues + ToWasmTypes,
157157
{
158158
let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
159-
let args = P::from_wasm_value_tuple(args)?;
159+
let args = P::from_wasm_values(args)?;
160160
let result = func(ctx, args)?;
161-
Ok(result.into_wasm_value_tuple())
161+
Ok(result.into_wasm_values())
162162
};
163163

164164
let ty = Arc::new(tinywasm_types::FuncType::new(&P::wasm_types(), &R::wasm_types()));
@@ -383,47 +383,45 @@ fn collect_call_results(value_stack: &mut ValueStack, func_ty: &FuncType) -> Res
383383
Ok(res)
384384
}
385385

386-
pub trait IntoWasmValueTuple {
387-
fn into_wasm_value_tuple(self) -> Vec<WasmValue>;
386+
pub trait IntoWasmValues {
387+
fn into_wasm_values(self) -> Vec<WasmValue>;
388388
}
389389

390-
pub trait FromWasmValueTuple {
391-
fn from_wasm_value_tuple(values: &[WasmValue]) -> Result<Self>
392-
where
393-
Self: Sized;
390+
pub trait FromWasmValues: Sized {
391+
fn from_wasm_values(values: &[WasmValue]) -> Result<Self>;
394392
}
395393

396-
impl<P: IntoWasmValueTuple, R: FromWasmValueTuple> FunctionTyped<P, R> {
394+
impl<P: IntoWasmValues, R: FromWasmValues> FunctionTyped<P, R> {
397395
/// Call a typed function
398396
pub fn call(&self, store: &mut Store, params: P) -> Result<R> {
399397
// Convert params into Vec<WasmValue>
400-
let wasm_values = params.into_wasm_value_tuple();
398+
let wasm_values = params.into_wasm_values();
401399

402400
// Call the underlying WASM function
403401
let result = self.func.call(store, &wasm_values)?;
404402

405403
// Convert the Vec<WasmValue> back to R
406-
R::from_wasm_value_tuple(&result)
404+
R::from_wasm_values(&result)
407405
}
408406

409407
/// Call a typed function and return a resumable execution handle.
410408
///
411409
/// The handle keeps a mutable borrow of the [`Store`] until completion.
412410
pub fn call_resumable<'store>(&self, store: &'store mut Store, params: P) -> Result<FuncExecutionTyped<'store, R>> {
413-
let wasm_values = params.into_wasm_value_tuple();
411+
let wasm_values = params.into_wasm_values();
414412
let execution = self.func.call_resumable(store, &wasm_values)?;
415413
Ok(FuncExecutionTyped { execution, marker: core::marker::PhantomData })
416414
}
417415
}
418416

419-
impl<'store, R: FromWasmValueTuple> FuncExecutionTyped<'store, R> {
417+
impl<'store, R: FromWasmValues> FuncExecutionTyped<'store, R> {
420418
/// Resume typed execution with up to `fuel` units of fuel.
421419
///
422420
/// Fuel is accounted in chunks, so execution may overshoot the requested
423421
/// fuel before returning [`ExecProgress::Suspended`].
424422
pub fn resume_with_fuel(&mut self, fuel: u32) -> Result<ExecProgress<R>> {
425423
match self.execution.resume_with_fuel(fuel)? {
426-
ExecProgress::Completed(values) => Ok(ExecProgress::Completed(R::from_wasm_value_tuple(&values)?)),
424+
ExecProgress::Completed(values) => Ok(ExecProgress::Completed(R::from_wasm_values(&values)?)),
427425
ExecProgress::Suspended => Ok(ExecProgress::Suspended),
428426
}
429427
}
@@ -435,53 +433,57 @@ impl<'store, R: FromWasmValueTuple> FuncExecutionTyped<'store, R> {
435433
/// time budget before returning [`ExecProgress::Suspended`].
436434
pub fn resume_with_time_budget(&mut self, time_budget: crate::std::time::Duration) -> Result<ExecProgress<R>> {
437435
match self.execution.resume_with_time_budget(time_budget)? {
438-
ExecProgress::Completed(values) => Ok(ExecProgress::Completed(R::from_wasm_value_tuple(&values)?)),
436+
ExecProgress::Completed(values) => Ok(ExecProgress::Completed(R::from_wasm_values(&values)?)),
439437
ExecProgress::Suspended => Ok(ExecProgress::Suspended),
440438
}
441439
}
442440
}
443441

444-
pub trait WasmTypesFromTuple {
442+
/// Describes the WebAssembly value types produced by a Rust value or tuple shape.
443+
pub trait ToWasmTypes {
444+
/// Return the flattened WebAssembly value types for this tuple shape.
445445
fn wasm_types() -> Box<[WasmType]>;
446446
}
447447

448+
/// Describes the WebAssembly value types produced by a scalar Rust type.
448449
pub trait ToWasmType {
449-
fn to_wasm_type() -> WasmType;
450+
/// Return the single WebAssembly value type for this scalar type.
451+
fn wasm_type() -> WasmType;
450452
}
451453

452454
macro_rules! impl_scalar_wasm_traits {
453455
($($T:ty => $val_ty:ident),+ $(,)?) => {
454456
$(
455457
impl ToWasmType for $T {
456458
#[inline]
457-
fn to_wasm_type() -> WasmType {
459+
fn wasm_type() -> WasmType {
458460
WasmType::$val_ty
459461
}
460462
}
461463

462-
impl WasmTypesFromTuple for $T {
464+
impl ToWasmTypes for $T {
463465
#[inline]
464466
fn wasm_types() -> Box<[WasmType]> {
465467
Box::new([WasmType::$val_ty])
466468
}
467469
}
468470

469-
impl IntoWasmValueTuple for $T {
471+
impl IntoWasmValues for $T {
470472
#[inline]
471-
fn into_wasm_value_tuple(self) -> Vec<WasmValue> {
473+
fn into_wasm_values(self) -> Vec<WasmValue> {
472474
vec![self.into()]
473475
}
474476
}
475477

476-
impl FromWasmValueTuple for $T {
478+
impl FromWasmValues for $T {
477479
#[inline]
478-
fn from_wasm_value_tuple(values: &[WasmValue]) -> Result<Self> {
480+
fn from_wasm_values(values: &[WasmValue]) -> Result<Self> {
479481
let value = *values
480482
.first()
481483
.ok_or(Error::Other("Not enough values in WasmValue vector".to_string()))?;
482484
<$T>::try_from(value).map_err(|e| {
483485
Error::Other(format!(
484-
"FromWasmValueTuple: Could not convert WasmValue to expected type: {:?}",
486+
"FromWasmValues: Could not convert WasmValue to expected type: {:?}",
485487
e
486488
))
487489
})
@@ -493,34 +495,34 @@ macro_rules! impl_scalar_wasm_traits {
493495

494496
macro_rules! impl_tuple_traits {
495497
($($T:ident),+) => {
496-
impl<$($T),+> WasmTypesFromTuple for ($($T,)+)
498+
impl<$($T),+> ToWasmTypes for ($($T,)+)
497499
where
498500
$($T: ToWasmType,)+
499501
{
500502
#[inline]
501503
fn wasm_types() -> Box<[WasmType]> {
502-
Box::new([$($T::to_wasm_type(),)+])
504+
Box::new([$($T::wasm_type(),)+])
503505
}
504506
}
505507

506-
impl<$($T),+> IntoWasmValueTuple for ($($T,)+)
508+
impl<$($T),+> IntoWasmValues for ($($T,)+)
507509
where
508510
$($T: Into<WasmValue>,)+
509511
{
510512
#[allow(non_snake_case)]
511513
#[inline]
512-
fn into_wasm_value_tuple(self) -> Vec<WasmValue> {
514+
fn into_wasm_values(self) -> Vec<WasmValue> {
513515
let ($($T,)+) = self;
514516
vec![$($T.into(),)+]
515517
}
516518
}
517519

518-
impl<$($T),+> FromWasmValueTuple for ($($T,)+)
520+
impl<$($T),+> FromWasmValues for ($($T,)+)
519521
where
520522
$($T: TryFrom<WasmValue, Error = ()>,)+
521523
{
522524
#[inline]
523-
fn from_wasm_value_tuple(values: &[WasmValue]) -> Result<Self> {
525+
fn from_wasm_values(values: &[WasmValue]) -> Result<Self> {
524526
let mut iter = values.iter();
525527

526528
Ok((
@@ -530,7 +532,7 @@ macro_rules! impl_tuple_traits {
530532
.ok_or(Error::Other("Not enough values in WasmValue vector".to_string()))?
531533
)
532534
.map_err(|e| Error::Other(format!(
533-
"FromWasmValueTuple: Could not convert WasmValue to expected type: {:?}",
535+
"FromWasmValues: Could not convert WasmValue to expected type: {:?}",
534536
e,
535537
)))?,
536538
)+
@@ -554,10 +556,6 @@ macro_rules! impl_tuple {
554556
$macro!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
555557
$macro!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
556558
$macro!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
557-
$macro!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
558-
$macro!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
559-
$macro!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
560-
$macro!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
561559
};
562560
}
563561

@@ -571,23 +569,125 @@ impl_scalar_wasm_traits!(
571569
);
572570
impl_tuple!(impl_tuple_traits);
573571

574-
impl WasmTypesFromTuple for () {
572+
/// A helper type for using tuples of arbitrary number of elements as function parameters or results,
573+
/// by concatenating the Wasm types of each element.
574+
///
575+
/// This is useful when a function signature exceeds tuple arity 12. `tinywasm` only implements
576+
/// direct tuple conversions up to arity 12, but `WasmTupleChain` lets you describe longer
577+
/// signatures by combining smaller tuples at the type level.
578+
///
579+
/// ## Example
580+
/// ```rust
581+
/// # fn main() -> tinywasm::Result<()> {
582+
/// # use tinywasm::{ModuleInstance, Store, WasmTupleChain};
583+
/// # let wasm = wat::parse_str(r#"
584+
/// # (module
585+
/// # (func (export "echo13")
586+
/// # (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
587+
/// # (result i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
588+
/// # local.get 0
589+
/// # local.get 1
590+
/// # local.get 2
591+
/// # local.get 3
592+
/// # local.get 4
593+
/// # local.get 5
594+
/// # local.get 6
595+
/// # local.get 7
596+
/// # local.get 8
597+
/// # local.get 9
598+
/// # local.get 10
599+
/// # local.get 11
600+
/// # local.get 12)
601+
/// # )
602+
/// # "#).expect("valid wat");
603+
/// # let module = tinywasm::parse_bytes(&wasm)?;
604+
/// # let mut store = Store::default();
605+
/// # let instance = ModuleInstance::instantiate(&mut store, &module, None)?;
606+
///
607+
/// type Params = WasmTupleChain<
608+
/// (i32, i32, i32, i32, i32, i32),
609+
/// (i32, i32, i32, i32, i32, i32, i32),
610+
/// >;
611+
/// type Results = WasmTupleChain<
612+
/// (i32, i32, i32, i32, i32, i32),
613+
/// (i32, i32, i32, i32, i32, i32, i32),
614+
/// >;
615+
///
616+
/// let echo13 = instance.func::<Params, Results>(&store, "echo13")?;
617+
/// let result = echo13.call(&mut store, ((1, 2, 3, 4, 5, 6), (7, 8, 9, 10, 11, 12, 13)).into())?;
618+
/// assert_eq!(result.into_inner(), ((1, 2, 3, 4, 5, 6), (7, 8, 9, 10, 11, 12, 13)));
619+
/// # Ok(())
620+
/// # }
621+
/// ```
622+
#[derive(Default)]
623+
pub struct WasmTupleChain<T1, T2>(T1, T2);
624+
625+
impl<T1, T2> WasmTupleChain<T1, T2> {
626+
/// Create a new concatenated tuple wrapper.
627+
pub const fn new(left: T1, right: T2) -> Self {
628+
Self(left, right)
629+
}
630+
631+
/// Split the wrapper back into its two component values.
632+
pub fn into_inner(self) -> (T1, T2) {
633+
(self.0, self.1)
634+
}
635+
}
636+
637+
impl<T1, T2> From<(T1, T2)> for WasmTupleChain<T1, T2> {
638+
fn from((left, right): (T1, T2)) -> Self {
639+
Self::new(left, right)
640+
}
641+
}
642+
643+
impl<T1: ToWasmTypes, T2: ToWasmTypes> ToWasmTypes for WasmTupleChain<T1, T2> {
644+
#[inline]
645+
fn wasm_types() -> Box<[WasmType]> {
646+
let mut types = Vec::new();
647+
types.extend_from_slice(&T1::wasm_types());
648+
types.extend_from_slice(&T2::wasm_types());
649+
types.into_boxed_slice()
650+
}
651+
}
652+
653+
impl<T1: IntoWasmValues, T2: IntoWasmValues> IntoWasmValues for WasmTupleChain<T1, T2> {
654+
#[inline]
655+
fn into_wasm_values(self) -> Vec<WasmValue> {
656+
let (left, right) = self.into_inner();
657+
let mut values = Vec::new();
658+
values.extend(left.into_wasm_values());
659+
values.extend(right.into_wasm_values());
660+
values
661+
}
662+
}
663+
664+
impl<T1: FromWasmValues + ToWasmTypes, T2: FromWasmValues> FromWasmValues for WasmTupleChain<T1, T2> {
665+
#[inline]
666+
fn from_wasm_values(values: &[WasmValue]) -> Result<Self> {
667+
let left_len = T1::wasm_types().len();
668+
let left = T1::from_wasm_values(&values[..values.len().min(left_len)])?;
669+
let right = T2::from_wasm_values(values.get(left_len..).unwrap_or(&[]))?;
670+
Ok(Self::new(left, right))
671+
}
672+
}
673+
674+
impl ToWasmTypes for () {
575675
#[inline]
576676
fn wasm_types() -> Box<[WasmType]> {
577677
Box::new([])
578678
}
579679
}
580680

581-
impl IntoWasmValueTuple for () {
681+
impl IntoWasmValues for () {
582682
#[inline]
583-
fn into_wasm_value_tuple(self) -> Vec<WasmValue> {
683+
fn into_wasm_values(self) -> Vec<WasmValue> {
584684
vec![]
585685
}
586686
}
587687

588-
impl FromWasmValueTuple for () {
688+
impl FromWasmValues for () {
589689
#[inline]
590-
fn from_wasm_value_tuple(_values: &[WasmValue]) -> Result<Self> {
690+
fn from_wasm_values(_values: &[WasmValue]) -> Result<Self> {
591691
Ok(())
592692
}
593693
}

0 commit comments

Comments
 (0)