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