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