wasm/execution/
interop.rs

1//! This module provides types, traits and impls to convert between
2//! Rust types and the Wasm [`Value`] type. Internally this module is
3//! not used, except for the top-level entry points for invocation.
4//!
5//! The main trait is [`InteropValue`]. It is implemented for all Rust
6//! types which can be converted into and from a [`Value`] through the
7//! [`From`] and [`TryFrom`] traits, respectively.
8//!
9//! Then, the [`InteropValueList`] trait is a layer on top, allowing
10//! the same conversions but instead for tuples/lists containing
11//! multiple values.
12
13use crate::{
14    addrs::FuncAddr,
15    value::{ExternAddr, Ref, F32, F64},
16    NumType, RefType, ValType, Value,
17};
18
19use alloc::{fmt::Debug, vec, vec::Vec};
20
21use super::value::ValueTypeMismatchError;
22
23/// An [InteropValue] is a Rust types that can be converted into a WASM [Value].
24/// This trait is intended to simplify translation between Rust values and WASM values and thus is not used internally.
25pub trait InteropValue
26where
27    Self: Copy + Debug + PartialEq + TryFrom<Value, Error = ValueTypeMismatchError>,
28    Value: From<Self>,
29{
30    const TY: ValType;
31}
32
33impl InteropValue for u32 {
34    const TY: ValType = ValType::NumType(NumType::I32);
35}
36
37impl InteropValue for i32 {
38    const TY: ValType = ValType::NumType(NumType::I32);
39}
40
41impl InteropValue for u64 {
42    const TY: ValType = ValType::NumType(NumType::I64);
43}
44
45impl InteropValue for i64 {
46    const TY: ValType = ValType::NumType(NumType::I64);
47}
48
49impl InteropValue for f32 {
50    const TY: ValType = ValType::NumType(NumType::F32);
51}
52
53impl InteropValue for f64 {
54    const TY: ValType = ValType::NumType(NumType::F64);
55}
56
57impl InteropValue for [u8; 16] {
58    const TY: ValType = ValType::VecType;
59}
60
61impl InteropValue for RefFunc {
62    const TY: ValType = ValType::RefType(RefType::FuncRef);
63}
64
65impl InteropValue for RefExtern {
66    const TY: ValType = ValType::RefType(RefType::ExternRef);
67}
68
69impl From<f32> for Value {
70    fn from(value: f32) -> Self {
71        F32(value).into()
72    }
73}
74
75impl TryFrom<Value> for f32 {
76    type Error = ValueTypeMismatchError;
77
78    fn try_from(value: Value) -> Result<Self, Self::Error> {
79        F32::try_from(value).map(|f| f.0)
80    }
81}
82
83impl From<f64> for Value {
84    fn from(value: f64) -> Self {
85        F64(value).into()
86    }
87}
88
89impl TryFrom<Value> for f64 {
90    type Error = ValueTypeMismatchError;
91
92    fn try_from(value: Value) -> Result<Self, Self::Error> {
93        F64::try_from(value).map(|f| f.0)
94    }
95}
96
97#[derive(Debug, Copy, Clone, PartialEq)]
98pub struct RefFunc(pub Option<FuncAddr>);
99
100impl From<RefFunc> for Value {
101    fn from(value: RefFunc) -> Self {
102        match value.0 {
103            Some(func_addr) => Ref::Func(func_addr),
104            None => Ref::Null(RefType::FuncRef),
105        }
106        .into()
107    }
108}
109
110impl TryFrom<Value> for RefFunc {
111    type Error = ValueTypeMismatchError;
112
113    fn try_from(value: Value) -> Result<Self, Self::Error> {
114        match Ref::try_from(value)? {
115            Ref::Func(func_addr) => Ok(Self(Some(func_addr))),
116            Ref::Null(RefType::FuncRef) => Ok(Self(None)),
117            _ => Err(ValueTypeMismatchError),
118        }
119    }
120}
121
122#[derive(Debug, Copy, Clone, PartialEq)]
123pub struct RefExtern(pub Option<ExternAddr>);
124
125impl From<RefExtern> for Value {
126    fn from(value: RefExtern) -> Self {
127        match value.0 {
128            Some(extern_addr) => Ref::Extern(extern_addr),
129            None => Ref::Null(RefType::ExternRef),
130        }
131        .into()
132    }
133}
134
135impl TryFrom<Value> for RefExtern {
136    type Error = ValueTypeMismatchError;
137
138    fn try_from(value: Value) -> Result<Self, Self::Error> {
139        match Ref::try_from(value)? {
140            Ref::Extern(extern_addr) => Ok(Self(Some(extern_addr))),
141            Ref::Null(RefType::ExternRef) => Ok(Self(None)),
142            _ => Err(ValueTypeMismatchError),
143        }
144    }
145}
146
147/// An [InteropValueList] is an iterable list of [InteropValue]s (i.e. Rust types that can be converted into WASM [Value]s).
148pub trait InteropValueList: Debug + Copy {
149    const TYS: &'static [ValType];
150
151    fn into_values(self) -> Vec<Value>;
152
153    fn try_from_values(
154        values: impl ExactSizeIterator<Item = Value>,
155    ) -> Result<Self, ValueTypeMismatchError>;
156}
157
158impl InteropValueList for () {
159    const TYS: &'static [ValType] = &[];
160
161    fn into_values(self) -> Vec<Value> {
162        Vec::new()
163    }
164
165    fn try_from_values(
166        values: impl ExactSizeIterator<Item = Value>,
167    ) -> Result<Self, ValueTypeMismatchError> {
168        if values.len() != 0 {
169            return Err(ValueTypeMismatchError);
170        }
171
172        Ok(())
173    }
174}
175
176impl<A> InteropValueList for A
177where
178    A: InteropValue,
179    Value: From<A>,
180{
181    const TYS: &'static [ValType] = &[A::TY];
182
183    fn into_values(self) -> Vec<Value> {
184        vec![self.into()]
185    }
186
187    fn try_from_values(
188        mut values: impl ExactSizeIterator<Item = Value>,
189    ) -> Result<Self, ValueTypeMismatchError> {
190        if values.len() != Self::TYS.len() {
191            return Err(ValueTypeMismatchError);
192        }
193
194        A::try_from(values.next().unwrap())
195    }
196}
197
198impl<A> InteropValueList for (A,)
199where
200    A: InteropValue,
201    Value: From<A>,
202{
203    const TYS: &'static [ValType] = &[A::TY];
204
205    fn into_values(self) -> Vec<Value> {
206        vec![self.0.into()]
207    }
208
209    fn try_from_values(
210        mut values: impl ExactSizeIterator<Item = Value>,
211    ) -> Result<Self, ValueTypeMismatchError> {
212        if values.len() != Self::TYS.len() {
213            return Err(ValueTypeMismatchError);
214        }
215
216        Ok((A::try_from(values.next().unwrap())?,))
217    }
218}
219
220impl<A, B> InteropValueList for (A, B)
221where
222    A: InteropValue,
223    B: InteropValue,
224    Value: From<A> + From<B>,
225{
226    const TYS: &'static [ValType] = &[A::TY, B::TY];
227
228    fn into_values(self) -> Vec<Value> {
229        vec![self.0.into(), self.1.into()]
230    }
231
232    fn try_from_values(
233        mut values: impl ExactSizeIterator<Item = Value>,
234    ) -> Result<Self, ValueTypeMismatchError> {
235        if values.len() != Self::TYS.len() {
236            return Err(ValueTypeMismatchError);
237        }
238
239        Ok((
240            A::try_from(values.next().unwrap())?,
241            B::try_from(values.next().unwrap())?,
242        ))
243    }
244}
245
246impl<A, B, C> InteropValueList for (A, B, C)
247where
248    A: InteropValue,
249    B: InteropValue,
250    C: InteropValue,
251    Value: From<A> + From<B> + From<C>,
252{
253    const TYS: &'static [ValType] = &[A::TY, B::TY, C::TY];
254
255    fn into_values(self) -> Vec<Value> {
256        vec![self.0.into(), self.1.into(), self.2.into()]
257    }
258
259    fn try_from_values(
260        mut values: impl ExactSizeIterator<Item = Value>,
261    ) -> Result<Self, ValueTypeMismatchError> {
262        if values.len() != Self::TYS.len() {
263            return Err(ValueTypeMismatchError);
264        }
265
266        Ok((
267            A::try_from(values.next().unwrap())?,
268            B::try_from(values.next().unwrap())?,
269            C::try_from(values.next().unwrap())?,
270        ))
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use crate::addrs::FuncAddr;
277    use crate::value::{ExternAddr, Value, ValueTypeMismatchError};
278    use alloc::vec::Vec;
279
280    use super::{InteropValueList, RefExtern, RefFunc};
281
282    // We use function shorthands to generate error types because it's shorter
283    const fn ok<T>(t: T) -> Result<T, ValueTypeMismatchError> {
284        Result::<T, ValueTypeMismatchError>::Ok(t)
285    }
286    const fn err<T>() -> Result<T, ValueTypeMismatchError> {
287        Result::<T, ValueTypeMismatchError>::Err(ValueTypeMismatchError)
288    }
289
290    #[test]
291    fn roundtrip_single_u32() {
292        const RUST_VALUE: u32 = 5;
293        let wasm_value: Value = RUST_VALUE.into();
294        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE));
295        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE as i32));
296        assert_eq!(wasm_value.try_into(), err::<u64>());
297        assert_eq!(wasm_value.try_into(), err::<i64>());
298        assert_eq!(wasm_value.try_into(), err::<f32>());
299        assert_eq!(wasm_value.try_into(), err::<f64>());
300        assert_eq!(wasm_value.try_into(), err::<RefFunc>());
301        assert_eq!(wasm_value.try_into(), err::<RefExtern>());
302    }
303
304    #[test]
305    fn roundtrip_single_i32() {
306        const RUST_VALUE: i32 = 5;
307        let wasm_value: Value = RUST_VALUE.into();
308        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE as u32));
309        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE));
310        assert_eq!(wasm_value.try_into(), err::<u64>());
311        assert_eq!(wasm_value.try_into(), err::<i64>());
312        assert_eq!(wasm_value.try_into(), err::<f32>());
313        assert_eq!(wasm_value.try_into(), err::<f64>());
314        assert_eq!(wasm_value.try_into(), err::<RefFunc>());
315        assert_eq!(wasm_value.try_into(), err::<RefExtern>());
316    }
317
318    #[test]
319    fn roundtrip_single_u64() {
320        const RUST_VALUE: u64 = 5;
321        let wasm_value: Value = RUST_VALUE.into();
322        assert_eq!(wasm_value.try_into(), err::<u32>());
323        assert_eq!(wasm_value.try_into(), err::<i32>());
324        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE));
325        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE as i64));
326        assert_eq!(wasm_value.try_into(), err::<f32>());
327        assert_eq!(wasm_value.try_into(), err::<f64>());
328        assert_eq!(wasm_value.try_into(), err::<RefFunc>());
329        assert_eq!(wasm_value.try_into(), err::<RefExtern>());
330    }
331
332    #[test]
333    fn roundtrip_single_i64() {
334        const RUST_VALUE: i64 = 5;
335        let wasm_value: Value = RUST_VALUE.into();
336        assert_eq!(wasm_value.try_into(), err::<u32>());
337        assert_eq!(wasm_value.try_into(), err::<i32>());
338        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE as u64));
339        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE));
340        assert_eq!(wasm_value.try_into(), err::<f32>());
341        assert_eq!(wasm_value.try_into(), err::<f64>());
342        assert_eq!(wasm_value.try_into(), err::<RefFunc>());
343        assert_eq!(wasm_value.try_into(), err::<RefExtern>());
344    }
345
346    #[test]
347    fn roundtrip_single_f32() {
348        const RUST_VALUE: f32 = 123.12;
349        let wasm_value: Value = RUST_VALUE.into();
350        assert_eq!(wasm_value.try_into(), err::<u32>());
351        assert_eq!(wasm_value.try_into(), err::<i32>());
352        assert_eq!(wasm_value.try_into(), err::<u64>());
353        assert_eq!(wasm_value.try_into(), err::<i64>());
354        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE));
355        assert_eq!(wasm_value.try_into(), err::<f64>());
356        assert_eq!(wasm_value.try_into(), err::<RefFunc>());
357        assert_eq!(wasm_value.try_into(), err::<RefExtern>());
358    }
359
360    #[test]
361    fn roundtrip_single_f64() {
362        const RUST_VALUE: f64 = 123.12;
363        let wasm_value: Value = RUST_VALUE.into();
364        assert_eq!(wasm_value.try_into(), err::<u32>());
365        assert_eq!(wasm_value.try_into(), err::<i32>());
366        assert_eq!(wasm_value.try_into(), err::<u64>());
367        assert_eq!(wasm_value.try_into(), err::<i64>());
368        assert_eq!(wasm_value.try_into(), err::<f32>());
369        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE));
370        assert_eq!(wasm_value.try_into(), err::<RefFunc>());
371        assert_eq!(wasm_value.try_into(), err::<RefExtern>());
372    }
373
374    #[test]
375    fn roundtrip_single_ref_func() {
376        const RUST_VALUE: RefFunc = RefFunc(Some(FuncAddr::INVALID));
377        let wasm_value: Value = RUST_VALUE.into();
378        assert_eq!(wasm_value.try_into(), err::<u32>());
379        assert_eq!(wasm_value.try_into(), err::<i32>());
380        assert_eq!(wasm_value.try_into(), err::<u64>());
381        assert_eq!(wasm_value.try_into(), err::<i64>());
382        assert_eq!(wasm_value.try_into(), err::<f32>());
383        assert_eq!(wasm_value.try_into(), err::<f64>());
384        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE));
385        assert_eq!(wasm_value.try_into(), err::<RefExtern>());
386    }
387
388    #[test]
389    fn roundtrip_single_ref_extern() {
390        const RUST_VALUE: RefExtern = RefExtern(Some(ExternAddr(51)));
391        let wasm_value: Value = RUST_VALUE.into();
392        assert_eq!(wasm_value.try_into(), err::<u32>());
393        assert_eq!(wasm_value.try_into(), err::<i32>());
394        assert_eq!(wasm_value.try_into(), err::<u64>());
395        assert_eq!(wasm_value.try_into(), err::<i64>());
396        assert_eq!(wasm_value.try_into(), err::<f32>());
397        assert_eq!(wasm_value.try_into(), err::<f64>());
398        assert_eq!(wasm_value.try_into(), err::<RefFunc>());
399        assert_eq!(wasm_value.try_into(), ok(RUST_VALUE));
400    }
401
402    #[test]
403    fn roundtrip_single_ref_func_null() {
404        const RUST_VALUE: RefFunc = RefFunc(None);
405        let wasm_value: Value = RUST_VALUE.into();
406        assert_eq!(wasm_value.try_into(), err::<u32>());
407        assert_eq!(wasm_value.try_into(), err::<i32>());
408        assert_eq!(wasm_value.try_into(), err::<u64>());
409        assert_eq!(wasm_value.try_into(), err::<i64>());
410        assert_eq!(wasm_value.try_into(), err::<f32>());
411        assert_eq!(wasm_value.try_into(), err::<f64>());
412        assert_eq!(wasm_value.try_into(), ok::<RefFunc>(RUST_VALUE));
413        assert_eq!(wasm_value.try_into(), err::<RefExtern>());
414    }
415
416    #[test]
417    fn roundtrip_single_ref_extern_null() {
418        const RUST_VALUE: RefExtern = RefExtern(None);
419        let wasm_value: Value = RUST_VALUE.into();
420        assert_eq!(wasm_value.try_into(), err::<u32>());
421        assert_eq!(wasm_value.try_into(), err::<i32>());
422        assert_eq!(wasm_value.try_into(), err::<u64>());
423        assert_eq!(wasm_value.try_into(), err::<i64>());
424        assert_eq!(wasm_value.try_into(), err::<f32>());
425        assert_eq!(wasm_value.try_into(), err::<f64>());
426        assert_eq!(wasm_value.try_into(), err::<RefFunc>());
427        assert_eq!(wasm_value.try_into(), ok::<RefExtern>(RUST_VALUE));
428    }
429
430    #[test]
431    fn roundtrip_list0() {
432        const RUST_VALUES: () = ();
433        let wasm_values: Vec<Value> = RUST_VALUES.into_values();
434        let roundtrip_rust_values = InteropValueList::try_from_values(wasm_values.into_iter());
435        assert_eq!(roundtrip_rust_values, Ok(RUST_VALUES));
436    }
437
438    #[test]
439    fn roundtrip_list1_single() {
440        const RUST_VALUES: u32 = 5;
441        let wasm_values: Vec<Value> = RUST_VALUES.into_values();
442        let roundtrip_rust_values = InteropValueList::try_from_values(wasm_values.into_iter());
443        assert_eq!(roundtrip_rust_values, Ok(RUST_VALUES));
444    }
445
446    #[test]
447    fn roundtrip_list1() {
448        const RUST_VALUES: (u32,) = (5,);
449        let wasm_values: Vec<Value> = RUST_VALUES.into_values();
450        let roundtrip_rust_values = InteropValueList::try_from_values(wasm_values.into_iter());
451        assert_eq!(roundtrip_rust_values, Ok(RUST_VALUES));
452    }
453
454    #[test]
455    fn roundtrip_list2() {
456        const RUST_VALUES: (f32, RefFunc) = (3.0, RefFunc(Some(FuncAddr::INVALID)));
457        let wasm_values: Vec<Value> = RUST_VALUES.into_values();
458        let roundtrip_rust_values = InteropValueList::try_from_values(wasm_values.into_iter());
459        assert_eq!(roundtrip_rust_values, Ok(RUST_VALUES));
460    }
461
462    #[test]
463    fn roundtrip_list3() {
464        const RUST_VALUES: (RefExtern, u64, i32) =
465            (RefExtern(Some(ExternAddr(123))), 8472846, -61864);
466        let wasm_values: Vec<Value> = RUST_VALUES.into_values();
467        let roundtrip_rust_values = InteropValueList::try_from_values(wasm_values.into_iter());
468        assert_eq!(roundtrip_rust_values, Ok(RUST_VALUES))
469    }
470
471    #[test]
472    fn list_incorrect_lengths() {
473        let wasm_values0: Vec<Value> = ().into_values();
474        let wasm_values1_single: Vec<Value> = 5u32.into_values();
475        let wasm_values1: Vec<Value> = (5u32,).into_values();
476        let wasm_values2: Vec<Value> = (5u32, 5u32).into_values();
477        let wasm_values3: Vec<Value> = (5u32, 5u32, 5u32).into_values();
478
479        assert_eq!(
480            InteropValueList::try_from_values(wasm_values0.clone().into_iter()),
481            err::<u32>()
482        );
483        assert_eq!(
484            InteropValueList::try_from_values(wasm_values0.clone().into_iter()),
485            err::<(u32,)>()
486        );
487        assert_eq!(
488            InteropValueList::try_from_values(wasm_values0.clone().into_iter()),
489            err::<(u32, u32)>()
490        );
491        assert_eq!(
492            InteropValueList::try_from_values(wasm_values0.clone().into_iter()),
493            err::<(u32, u32, u32)>()
494        );
495
496        assert_eq!(
497            InteropValueList::try_from_values(wasm_values1_single.clone().into_iter()),
498            err::<()>()
499        );
500        assert_eq!(
501            InteropValueList::try_from_values(wasm_values1_single.clone().into_iter()),
502            err::<(u32, u32)>()
503        );
504        assert_eq!(
505            InteropValueList::try_from_values(wasm_values1_single.clone().into_iter()),
506            err::<(u32, u32, u32)>()
507        );
508
509        assert_eq!(
510            InteropValueList::try_from_values(wasm_values1.clone().into_iter()),
511            err::<()>()
512        );
513        assert_eq!(
514            InteropValueList::try_from_values(wasm_values1.clone().into_iter()),
515            err::<(u32, u32)>()
516        );
517        assert_eq!(
518            InteropValueList::try_from_values(wasm_values1.clone().into_iter()),
519            err::<(u32, u32, u32)>()
520        );
521
522        assert_eq!(
523            InteropValueList::try_from_values(wasm_values2.clone().into_iter()),
524            err::<()>()
525        );
526        assert_eq!(
527            InteropValueList::try_from_values(wasm_values2.clone().into_iter()),
528            err::<(u32,)>()
529        );
530        assert_eq!(
531            InteropValueList::try_from_values(wasm_values2.clone().into_iter()),
532            err::<(u32, u32, u32)>()
533        );
534
535        assert_eq!(
536            InteropValueList::try_from_values(wasm_values3.clone().into_iter()),
537            err::<()>()
538        );
539        assert_eq!(
540            InteropValueList::try_from_values(wasm_values3.clone().into_iter()),
541            err::<(u32,)>()
542        );
543        assert_eq!(
544            InteropValueList::try_from_values(wasm_values3.clone().into_iter()),
545            err::<(u32, u32)>()
546        );
547    }
548}