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