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