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