Skip to main content

wasmtime/runtime/wave/
component.rs

1use crate::component;
2use crate::prelude::*;
3use std::borrow::Cow;
4
5use super::{canonicalize_nan32, canonicalize_nan64, unwrap_2val, unwrap_val};
6use component::wasm_wave::wasm::{
7    DisplayValue, WasmFunc, WasmType, WasmTypeKind, WasmValue, WasmValueError, ensure_type_kind,
8};
9
10macro_rules! maybe_unwrap_type {
11    ($ty:expr, $case:path) => {
12        match $ty {
13            $case(v) => Some(v),
14            _ => None,
15        }
16    };
17}
18
19impl WasmType for component::Type {
20    fn kind(&self) -> WasmTypeKind {
21        match self {
22            Self::Bool => WasmTypeKind::Bool,
23            Self::S8 => WasmTypeKind::S8,
24            Self::U8 => WasmTypeKind::U8,
25            Self::S16 => WasmTypeKind::S16,
26            Self::U16 => WasmTypeKind::U16,
27            Self::S32 => WasmTypeKind::S32,
28            Self::U32 => WasmTypeKind::U32,
29            Self::S64 => WasmTypeKind::S64,
30            Self::U64 => WasmTypeKind::U64,
31            Self::Float32 => WasmTypeKind::F32,
32            Self::Float64 => WasmTypeKind::F64,
33            Self::Char => WasmTypeKind::Char,
34            Self::String => WasmTypeKind::String,
35            Self::List(_) => WasmTypeKind::List,
36            Self::Record(_) => WasmTypeKind::Record,
37            Self::Tuple(_) => WasmTypeKind::Tuple,
38            Self::Variant(_) => WasmTypeKind::Variant,
39            Self::Enum(_) => WasmTypeKind::Enum,
40            Self::Option(_) => WasmTypeKind::Option,
41            Self::Result(_) => WasmTypeKind::Result,
42            Self::Flags(_) => WasmTypeKind::Flags,
43            Self::FixedLengthList(_) => WasmTypeKind::FixedLengthList,
44
45            Self::Own(_)
46            | Self::Borrow(_)
47            | Self::Stream(_)
48            | Self::Future(_)
49            | Self::ErrorContext
50            | Self::Map(_) => WasmTypeKind::Unsupported,
51        }
52    }
53
54    fn list_element_type(&self) -> Option<Self> {
55        Some(maybe_unwrap_type!(self, Self::List)?.ty())
56    }
57
58    fn record_fields(&self) -> Box<dyn Iterator<Item = (Cow<'_, str>, Self)> + '_> {
59        let Self::Record(record) = self else {
60            return Box::new(std::iter::empty());
61        };
62        Box::new(record.fields().map(|f| (f.name.into(), f.ty.clone())))
63    }
64
65    fn tuple_element_types(&self) -> Box<dyn Iterator<Item = Self> + '_> {
66        let Self::Tuple(tuple) = self else {
67            return Box::new(std::iter::empty());
68        };
69        Box::new(tuple.types())
70    }
71
72    fn variant_cases(&self) -> Box<dyn Iterator<Item = (Cow<'_, str>, Option<Self>)> + '_> {
73        let Self::Variant(variant) = self else {
74            return Box::new(std::iter::empty());
75        };
76        Box::new(variant.cases().map(|case| (case.name.into(), case.ty)))
77    }
78
79    fn enum_cases(&self) -> Box<dyn Iterator<Item = Cow<'_, str>> + '_> {
80        let Self::Enum(enum_) = self else {
81            return Box::new(std::iter::empty());
82        };
83        Box::new(enum_.names().map(Into::into))
84    }
85
86    fn option_some_type(&self) -> Option<Self> {
87        maybe_unwrap_type!(self, Self::Option).map(|o| o.ty())
88    }
89
90    fn result_types(&self) -> Option<(Option<Self>, Option<Self>)> {
91        let result = maybe_unwrap_type!(self, Self::Result)?;
92        Some((result.ok(), result.err()))
93    }
94
95    fn flags_names(&self) -> Box<dyn Iterator<Item = Cow<'_, str>> + '_> {
96        let Self::Flags(flags) = self else {
97            return Box::new(std::iter::empty());
98        };
99        Box::new(flags.names().map(Into::into))
100    }
101}
102
103macro_rules! impl_primitives {
104    ($Self:ident, $(($case:ident, $ty:ty, $make:ident, $unwrap:ident)),*) => {
105        $(
106            fn $make(val: $ty) -> $Self {
107                $Self::$case(val)
108            }
109
110            fn $unwrap(&self) -> $ty {
111                *unwrap_val!(self, $Self::$case, stringify!($case))
112            }
113        )*
114    };
115}
116
117impl WasmValue for component::Val {
118    type Type = component::Type;
119
120    fn kind(&self) -> WasmTypeKind {
121        match self {
122            Self::Bool(_) => WasmTypeKind::Bool,
123            Self::S8(_) => WasmTypeKind::S8,
124            Self::U8(_) => WasmTypeKind::U8,
125            Self::S16(_) => WasmTypeKind::S16,
126            Self::U16(_) => WasmTypeKind::U16,
127            Self::S32(_) => WasmTypeKind::S32,
128            Self::U32(_) => WasmTypeKind::U32,
129            Self::S64(_) => WasmTypeKind::S64,
130            Self::U64(_) => WasmTypeKind::U64,
131            Self::Float32(_) => WasmTypeKind::F32,
132            Self::Float64(_) => WasmTypeKind::F64,
133            Self::Char(_) => WasmTypeKind::Char,
134            Self::String(_) => WasmTypeKind::String,
135            Self::List(_) => WasmTypeKind::List,
136            Self::Record(_) => WasmTypeKind::Record,
137            Self::Tuple(_) => WasmTypeKind::Tuple,
138            Self::Variant(..) => WasmTypeKind::Variant,
139            Self::Enum(_) => WasmTypeKind::Enum,
140            Self::Option(_) => WasmTypeKind::Option,
141            Self::Result(_) => WasmTypeKind::Result,
142            Self::Flags(_) => WasmTypeKind::Flags,
143            Self::Resource(_)
144            | Self::Stream(_)
145            | Self::Future(_)
146            | Self::ErrorContext(_)
147            | Self::Map(_) => WasmTypeKind::Unsupported,
148            Self::FixedLengthList(_) => WasmTypeKind::FixedLengthList,
149        }
150    }
151
152    impl_primitives!(
153        Self,
154        (Bool, bool, make_bool, unwrap_bool),
155        (S8, i8, make_s8, unwrap_s8),
156        (S16, i16, make_s16, unwrap_s16),
157        (S32, i32, make_s32, unwrap_s32),
158        (S64, i64, make_s64, unwrap_s64),
159        (U8, u8, make_u8, unwrap_u8),
160        (U16, u16, make_u16, unwrap_u16),
161        (U32, u32, make_u32, unwrap_u32),
162        (U64, u64, make_u64, unwrap_u64),
163        (Char, char, make_char, unwrap_char)
164    );
165
166    fn make_f32(val: f32) -> Self {
167        let val = canonicalize_nan32(val);
168        Self::Float32(val)
169    }
170    fn make_f64(val: f64) -> Self {
171        let val = canonicalize_nan64(val);
172        Self::Float64(val)
173    }
174    fn make_string(val: Cow<str>) -> Self {
175        Self::String(val.into())
176    }
177    fn make_list(
178        ty: &Self::Type,
179        vals: impl IntoIterator<Item = Self>,
180    ) -> Result<Self, WasmValueError> {
181        ensure_type_kind(ty, WasmTypeKind::List)?;
182        let val = Self::List(vals.into_iter().collect());
183        ensure_type_val(ty, &val)?;
184        Ok(val)
185    }
186    fn make_record<'a>(
187        ty: &Self::Type,
188        fields: impl IntoIterator<Item = (&'a str, Self)>,
189    ) -> Result<Self, WasmValueError> {
190        ensure_type_kind(ty, WasmTypeKind::Record)?;
191        let values: Vec<(String, Self)> = fields
192            .into_iter()
193            .map(|(name, val)| (name.to_string(), val))
194            .collect();
195        let val = Self::Record(values);
196        ensure_type_val(ty, &val)?;
197        Ok(val)
198    }
199    fn make_tuple(
200        ty: &Self::Type,
201        vals: impl IntoIterator<Item = Self>,
202    ) -> Result<Self, WasmValueError> {
203        ensure_type_kind(ty, WasmTypeKind::Tuple)?;
204        let val = Self::Tuple(vals.into_iter().collect());
205        ensure_type_val(ty, &val)?;
206        Ok(val)
207    }
208    fn make_variant(
209        ty: &Self::Type,
210        case: &str,
211        val: Option<Self>,
212    ) -> Result<Self, WasmValueError> {
213        ensure_type_kind(ty, WasmTypeKind::Variant)?;
214        let val = Self::Variant(case.to_string(), val.map(Box::new));
215        ensure_type_val(ty, &val)?;
216        Ok(val)
217    }
218    fn make_enum(ty: &Self::Type, case: &str) -> Result<Self, WasmValueError> {
219        ensure_type_kind(ty, WasmTypeKind::Enum)?;
220        let val = Self::Enum(case.to_string());
221        ensure_type_val(ty, &val)?;
222        Ok(val)
223    }
224    fn make_option(ty: &Self::Type, val: Option<Self>) -> Result<Self, WasmValueError> {
225        ensure_type_kind(ty, WasmTypeKind::Option)?;
226        let val = Self::Option(val.map(Box::new));
227        ensure_type_val(ty, &val)?;
228        Ok(val)
229    }
230    fn make_result(
231        ty: &Self::Type,
232        val: Result<Option<Self>, Option<Self>>,
233    ) -> Result<Self, WasmValueError> {
234        ensure_type_kind(ty, WasmTypeKind::Result)?;
235        let val = match val {
236            Ok(val) => Self::Result(Ok(val.map(Box::new))),
237            Err(val) => Self::Result(Err(val.map(Box::new))),
238        };
239        ensure_type_val(ty, &val)?;
240        Ok(val)
241    }
242    fn make_flags<'a>(
243        ty: &Self::Type,
244        names: impl IntoIterator<Item = &'a str>,
245    ) -> Result<Self, WasmValueError> {
246        ensure_type_kind(ty, WasmTypeKind::Flags)?;
247        let val = Self::Flags(names.into_iter().map(|n| n.to_string()).collect());
248        ensure_type_val(ty, &val)?;
249        Ok(val)
250    }
251
252    fn unwrap_f32(&self) -> f32 {
253        let val = *unwrap_val!(self, Self::Float32, "f32");
254        canonicalize_nan32(val)
255    }
256    fn unwrap_f64(&self) -> f64 {
257        let val = *unwrap_val!(self, Self::Float64, "f64");
258        canonicalize_nan64(val)
259    }
260    fn unwrap_string(&self) -> Cow<'_, str> {
261        unwrap_val!(self, Self::String, "string").into()
262    }
263    fn unwrap_list(&self) -> Box<dyn Iterator<Item = Cow<'_, Self>> + '_> {
264        let list = unwrap_val!(self, Self::List, "list");
265        Box::new(list.iter().map(cow))
266    }
267    fn unwrap_record(&self) -> Box<dyn Iterator<Item = (Cow<'_, str>, Cow<'_, Self>)> + '_> {
268        let record = unwrap_val!(self, Self::Record, "record");
269        Box::new(record.iter().map(|(name, val)| (name.into(), cow(val))))
270    }
271    fn unwrap_tuple(&self) -> Box<dyn Iterator<Item = Cow<'_, Self>> + '_> {
272        let tuple = unwrap_val!(self, Self::Tuple, "tuple");
273        Box::new(tuple.iter().map(cow))
274    }
275    fn unwrap_variant(&self) -> (Cow<'_, str>, Option<Cow<'_, Self>>) {
276        let (discriminant, payload) = unwrap_2val!(self, Self::Variant, "variant");
277        (discriminant.into(), payload.as_deref().map(cow))
278    }
279    fn unwrap_enum(&self) -> Cow<'_, str> {
280        unwrap_val!(self, Self::Enum, "enum").into()
281    }
282    fn unwrap_option(&self) -> Option<Cow<'_, Self>> {
283        unwrap_val!(self, Self::Option, "option")
284            .as_deref()
285            .map(cow)
286    }
287    fn unwrap_result(&self) -> Result<Option<Cow<'_, Self>>, Option<Cow<'_, Self>>> {
288        match unwrap_val!(self, Self::Result, "result") {
289            Ok(t) => Ok(t.as_deref().map(cow)),
290            Err(e) => Err(e.as_deref().map(cow)),
291        }
292    }
293    fn unwrap_flags(&self) -> Box<dyn Iterator<Item = Cow<'_, str>> + '_> {
294        let flags = unwrap_val!(self, Self::Flags, "flags");
295        Box::new(flags.iter().map(Into::into))
296    }
297}
298
299// Returns an error if the given component::Val is not of the given component::Type.
300//
301// The component::Val::Resource(_) variant results in an unsupported error at this time.
302fn ensure_type_val(ty: &component::Type, val: &component::Val) -> Result<(), WasmValueError> {
303    let wrong_value_type = || -> Result<(), WasmValueError> {
304        Err(WasmValueError::WrongValueType {
305            ty: wasm_wave::wasm::DisplayType(ty).to_string(),
306            val: wasm_wave::wasm::DisplayValue(val).to_string(),
307        })
308    };
309
310    if ty.kind() != val.kind() {
311        return wrong_value_type();
312    }
313
314    match val {
315        component::Val::List(vals) => {
316            let list_type = ty.unwrap_list().ty();
317            for val in vals {
318                ensure_type_val(&list_type, val)?;
319            }
320        }
321        component::Val::Record(vals) => {
322            let record_handle = ty.unwrap_record();
323            // Check that every non option field type is found in the Vec
324            for field in record_handle.fields() {
325                if !matches!(field.ty, component::Type::Option(_))
326                    && !vals.iter().any(|(n, _)| n == field.name)
327                {
328                    return wrong_value_type();
329                }
330            }
331            // Check that every (String, Val) of the given Vec is a correct field_type
332            for (name, field_val) in vals.iter() {
333                // N.B. The `fields` call in each iteration is non-trivial, perhaps a cleaner way
334                // using the loop above will present itself.
335                if let Some(field) = record_handle.fields().find(|field| field.name == name) {
336                    ensure_type_val(&field.ty, field_val)?;
337                } else {
338                    return wrong_value_type();
339                }
340            }
341        }
342        component::Val::Tuple(vals) => {
343            let field_types = ty.unwrap_tuple().types();
344            if field_types.len() != vals.len() {
345                return wrong_value_type();
346            }
347            for (ty, val) in field_types.into_iter().zip(vals.iter()) {
348                ensure_type_val(&ty, val)?;
349            }
350        }
351        component::Val::Variant(name, optional_payload) => {
352            if let Some(case) = ty.unwrap_variant().cases().find(|case| case.name == name) {
353                match (optional_payload, case.ty) {
354                    (None, None) => {}
355                    (Some(payload), Some(payload_ty)) => ensure_type_val(&payload_ty, payload)?,
356                    _ => return wrong_value_type(),
357                }
358            } else {
359                return wrong_value_type();
360            }
361        }
362        component::Val::Enum(name) => {
363            if !ty.unwrap_enum().names().any(|n| n == name) {
364                return wrong_value_type();
365            }
366        }
367        component::Val::Option(Some(some_val)) => {
368            ensure_type_val(&ty.unwrap_option().ty(), some_val.as_ref())?;
369        }
370        component::Val::Result(res_val) => {
371            let result_handle = ty.unwrap_result();
372            match res_val {
373                Ok(ok) => match (ok, result_handle.ok()) {
374                    (None, None) => {}
375                    (Some(ok_val), Some(ok_ty)) => ensure_type_val(&ok_ty, ok_val.as_ref())?,
376                    _ => return wrong_value_type(),
377                },
378                Err(err) => match (err, result_handle.err()) {
379                    (None, None) => {}
380                    (Some(err_val), Some(err_ty)) => ensure_type_val(&err_ty, err_val.as_ref())?,
381                    _ => return wrong_value_type(),
382                },
383            }
384        }
385        component::Val::Flags(flags) => {
386            let flags_handle = ty.unwrap_flags();
387            for flag in flags {
388                if !flags_handle.names().any(|n| n == flag) {
389                    return wrong_value_type();
390                }
391            }
392        }
393        component::Val::Resource(_) => {
394            return Err(WasmValueError::UnsupportedType(
395                DisplayValue(val).to_string(),
396            ));
397        }
398
399        // Any leaf variant type has already had its kind compared above; nothing further to check.
400        // Likewise, the component::Option(None) arm would have nothing left to check.
401        _ => {}
402    }
403    Ok(())
404}
405
406impl WasmFunc for component::types::ComponentFunc {
407    type Type = component::Type;
408
409    fn params(&self) -> Box<dyn Iterator<Item = Self::Type> + '_> {
410        Box::new(self.params().map(|(_n, t)| t))
411    }
412
413    fn results(&self) -> Box<dyn Iterator<Item = Self::Type> + '_> {
414        Box::new(self.results())
415    }
416}
417
418fn cow<T: Clone>(t: &T) -> Cow<'_, T> {
419    Cow::Borrowed(t)
420}
421
422#[cfg(test)]
423mod tests {
424    #[test]
425    fn component_vals_smoke_test() {
426        use crate::component::Val;
427        for (val, want) in [
428            (Val::Bool(false), "false"),
429            (Val::Bool(true), "true"),
430            (Val::S8(10), "10"),
431            (Val::S16(-10), "-10"),
432            (Val::S32(1_000_000), "1000000"),
433            (Val::S64(0), "0"),
434            (Val::U8(255), "255"),
435            (Val::U16(0), "0"),
436            (Val::U32(1_000_000), "1000000"),
437            (Val::U64(9), "9"),
438            (Val::Float32(1.5), "1.5"),
439            (Val::Float32(f32::NAN), "nan"),
440            (Val::Float32(f32::INFINITY), "inf"),
441            (Val::Float32(f32::NEG_INFINITY), "-inf"),
442            (Val::Float64(-1.5e-10), "-0.00000000015"),
443            (Val::Float64(f64::NAN), "nan"),
444            (Val::Float64(f64::INFINITY), "inf"),
445            (Val::Float64(f64::NEG_INFINITY), "-inf"),
446            (Val::Char('x'), "'x'"),
447            (Val::Char('☃'), "'☃'"),
448            (Val::Char('\''), r"'\''"),
449            (Val::Char('\0'), r"'\u{0}'"),
450            (Val::Char('\x1b'), r"'\u{1b}'"),
451            (Val::Char('😂'), r"'😂'"),
452            (Val::String("abc".into()), r#""abc""#),
453            (Val::String(r#"\☃""#.into()), r#""\\☃\"""#),
454            (Val::String("\t\r\n\0".into()), r#""\t\r\n\u{0}""#),
455        ] {
456            let got = wasm_wave::to_string(&val)
457                .unwrap_or_else(|err| panic!("failed to serialize {val:?}: {err}"));
458            assert_eq!(got, want, "for {val:?}");
459        }
460    }
461
462    #[test]
463    fn test_round_trip_floats() {
464        use crate::component::{Type, Val};
465        use std::fmt::Debug;
466
467        fn round_trip<V: wasm_wave::wasm::WasmValue + PartialEq + Debug>(ty: &V::Type, val: &V) {
468            let val_str = wasm_wave::to_string(val).unwrap();
469            let result: V = wasm_wave::from_str::<V>(ty, &val_str).unwrap();
470            assert_eq!(val, &result);
471        }
472
473        for i in 0..100 {
474            for j in 0..100 {
475                round_trip(&Type::Float32, &Val::Float32(i as f32 / j as f32));
476                round_trip(&Type::Float64, &Val::Float64(i as f64 / j as f64));
477            }
478        }
479
480        round_trip(&Type::Float32, &Val::Float32(f32::EPSILON));
481        round_trip(&Type::Float64, &Val::Float64(f64::EPSILON));
482    }
483}