use crate::component::func::{bad_type_info, Lift, LiftContext, Lower, LowerContext}; use crate::component::types::{self, Type}; use crate::component::ResourceAny; use crate::ValRaw; use anyhow::{anyhow, bail, Context, Error, Result}; use std::collections::HashMap; use std::fmt; use std::iter; use std::mem::MaybeUninit; use std::ops::Deref; use wasmtime_component_util::{DiscriminantSize, FlagsSize}; use wasmtime_environ::component::{ CanonicalAbiInfo, ComponentTypes, InterfaceType, TypeListIndex, VariantInfo, }; /// Dynamic representation of `list` in the component model. /// /// This structure is part of the dynamically-typed [`Val`] which represents /// all possible values in the component model. This is often used in /// conjunction with [`Func::call`](crate::component::Func::call). /// /// Note that `Vec` is also supported for component model lists when /// using [`TypedFunc`](crate::component::TypedFunc). #[derive(PartialEq, Eq, Clone)] pub struct List { ty: types::List, values: Box<[Val]>, } impl List { /// Creates a new list with the specified type with the specified `values`. /// /// # Errors /// /// Returns an error if any value in `values` does not have the element /// type of `ty` specified. pub fn new(ty: &types::List, values: Box<[Val]>) -> Result { let element_type = ty.ty(); for (index, value) in values.iter().enumerate() { element_type .is_supertype_of(value) .with_context(|| format!("type mismatch for element {index} of list"))?; } Ok(Self { ty: ty.clone(), values, }) } /// Returns the corresponding type of this list pub fn ty(&self) -> &types::List { &self.ty } } impl Deref for List { type Target = [Val]; fn deref(&self) -> &[Val] { &self.values } } impl fmt::Debug for List { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_list(); for val in self.iter() { f.entry(val); } f.finish() } } /// Dynamic representation of `record` in the component model. /// /// This structure is part of the dynamically-typed [`Val`] which represents /// all possible values in the component model. This is often used in /// conjunction with [`Func::call`](crate::component::Func::call). /// /// Note that records can also be mapped to Rust `struct`s through the /// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro /// and using [`TypedFunc`](crate::component::TypedFunc). #[derive(PartialEq, Eq, Clone)] pub struct Record { ty: types::Record, values: Box<[Val]>, } impl Record { /// Creates a new record with the specified `ty` and the `values` for fields. /// /// Note that the `values` specified for fields must list fields in the /// same order that the `ty` lists them in. /// /// # Errors /// /// Returns an error if any of the `values` specified are in the wrong order /// field-wise or have the wrong type relative to what's specified in `ty`. pub fn new<'a>( ty: &types::Record, values: impl IntoIterator, ) -> Result { let mut fields = ty.fields(); let expected_len = fields.len(); let mut iter = values.into_iter(); let mut values = Vec::with_capacity(expected_len); loop { match (fields.next(), iter.next()) { (Some(field), Some((name, value))) => { if name == field.name { field .ty .is_supertype_of(&value) .with_context(|| format!("type mismatch for field {name} of record"))?; values.push(value); } else { bail!("field name mismatch: expected {}; got {name}", field.name) } } (None, Some((_, value))) => values.push(value), _ => break, } } if values.len() != expected_len { bail!("expected {} value(s); got {}", expected_len, values.len()); } Ok(Self { ty: ty.clone(), values: values.into(), }) } /// Returns the corresponding type of this record. pub fn ty(&self) -> &types::Record { &self.ty } /// Returns the list of fields that this struct contains with their name /// and value. pub fn fields(&self) -> impl ExactSizeIterator { assert_eq!(self.values.len(), self.ty.fields().len()); self.ty .fields() .zip(self.values.iter()) .map(|(ty, val)| (ty.name, val)) } } impl fmt::Debug for Record { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("Record"); for (name, val) in self.fields() { f.field(name, val); } f.finish() } } /// Dynamic representation of `tuple<...>` in the component model. /// /// This structure is part of the dynamically-typed [`Val`] which represents /// all possible values in the component model. This is often used in /// conjunction with [`Func::call`](crate::component::Func::call). /// /// Note that tuples are also mapped to Rust tuples when using /// [`TypedFunc`](crate::component::TypedFunc). #[derive(PartialEq, Eq, Clone)] pub struct Tuple { ty: types::Tuple, values: Box<[Val]>, } impl Tuple { /// Creates a new tuple with the `ty` specified and `values` for each /// entry. /// /// # Errors /// /// Returns an error if `values` does not match the `ty` specified, such as /// if it has the wrong length or if any field has the wrong type. pub fn new(ty: &types::Tuple, values: Box<[Val]>) -> Result { if values.len() != ty.types().len() { bail!( "expected {} value(s); got {}", ty.types().len(), values.len() ); } for (index, (value, ty)) in values.iter().zip(ty.types()).enumerate() { ty.is_supertype_of(value) .with_context(|| format!("type mismatch for field {index} of tuple"))?; } Ok(Self { ty: ty.clone(), values, }) } /// Returns the type of this tuple. pub fn ty(&self) -> &types::Tuple { &self.ty } /// Returns the list of values that this tuple contains. pub fn values(&self) -> &[Val] { &self.values } } impl fmt::Debug for Tuple { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut tuple = f.debug_tuple(""); for val in self.values() { tuple.field(val); } tuple.finish() } } /// Dynamic representation of `variant` in the component model. /// /// This structure is part of the dynamically-typed [`Val`] which represents /// all possible values in the component model. This is often used in /// conjunction with [`Func::call`](crate::component::Func::call). /// /// Note that variants can also be mapped to Rust `enum`s through the /// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro /// and using [`TypedFunc`](crate::component::TypedFunc). #[derive(PartialEq, Eq, Clone)] pub struct Variant { ty: types::Variant, discriminant: u32, value: Option>, } impl Variant { /// Creates a new variant with the specified `ty` and the case `name` and /// `value` provided. /// /// This will create a instance of a variant with the case `name` which has /// the optional payload of `value`. /// /// # Errors /// /// Returns an error if `name` doesn't match any case of `ty` or if /// `value` is of the wrong type. Note that `ty` must match the payload /// type exactly, in that if the case for `name` doesn't have a type then /// `value` must be `None`. pub fn new(ty: &types::Variant, name: &str, value: Option) -> Result { let (discriminant, case_type) = ty .cases() .enumerate() .find_map(|(index, case)| { if case.name == name { Some((index, case.ty)) } else { None } }) .ok_or_else(|| anyhow!("unknown variant case: {name}"))?; typecheck_payload(name, case_type.as_ref(), value.as_ref())?; Ok(Self { ty: ty.clone(), discriminant: u32::try_from(discriminant)?, value: value.map(Box::new), }) } /// Returns the type of this variant. pub fn ty(&self) -> &types::Variant { &self.ty } /// Returns name of the discriminant of this value within the variant type. pub fn discriminant(&self) -> &str { self.ty .cases() .nth(self.discriminant as usize) .unwrap() .name } /// Returns the payload value for this variant. pub fn payload(&self) -> Option<&Val> { self.value.as_deref() } fn as_generic<'a>( &'a self, types: &'a ComponentTypes, ty: InterfaceType, ) -> GenericVariant<'a> { let ty = match ty { InterfaceType::Variant(i) => &types[i], _ => bad_type_info(), }; GenericVariant { discriminant: self.discriminant, abi: &ty.abi, info: &ty.info, payload: self .value .as_deref() .zip(ty.cases[self.discriminant as usize].ty), } } } fn typecheck_payload(name: &str, case_type: Option<&Type>, value: Option<&Val>) -> Result<()> { match (case_type, value) { (Some(expected), Some(actual)) => expected .is_supertype_of(&actual) .with_context(|| format!("type mismatch for case {name} of variant")), (None, None) => Ok(()), (Some(_), None) => bail!("expected a payload for case `{name}`"), (None, Some(_)) => bail!("did not expect payload for case `{name}`"), } } impl fmt::Debug for Variant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(self.discriminant()) .field(&self.payload()) .finish() } } /// Dynamic representation of `enum` in the component model. /// /// This structure is part of the dynamically-typed [`Val`] which represents /// all possible values in the component model. This is often used in /// conjunction with [`Func::call`](crate::component::Func::call). /// /// Note that enums can also be mapped to Rust `enum`s through the /// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro /// and using [`TypedFunc`](crate::component::TypedFunc). #[derive(PartialEq, Eq, Clone)] pub struct Enum { ty: types::Enum, discriminant: u32, } impl Enum { /// Creates a new enum value with the `ty` specified selecting the /// specified variant called `name`. /// /// # Errors /// /// Returns an error if `name` isn't listed within `ty`. pub fn new(ty: &types::Enum, name: &str) -> Result { let discriminant = u32::try_from( ty.names() .position(|n| n == name) .ok_or_else(|| anyhow!("unknown enum case: {name}"))?, )?; Ok(Self { ty: ty.clone(), discriminant, }) } /// Returns the type of this value. pub fn ty(&self) -> &types::Enum { &self.ty } /// Returns name of this enum value. pub fn discriminant(&self) -> &str { self.ty.names().nth(self.discriminant as usize).unwrap() } fn as_generic<'a>( &'a self, types: &'a ComponentTypes, ty: InterfaceType, ) -> GenericVariant<'a> { let ty = match ty { InterfaceType::Enum(i) => &types[i], _ => bad_type_info(), }; GenericVariant { discriminant: self.discriminant, abi: &ty.abi, info: &ty.info, payload: None, } } } impl fmt::Debug for Enum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.discriminant(), f) } } /// Dynamic representation of `option` in the component model. /// /// This structure is part of the dynamically-typed [`Val`] which represents /// all possible values in the component model. This is often used in /// conjunction with [`Func::call`](crate::component::Func::call). /// /// Note that options are also mapped to Rust `Option` when using /// [`TypedFunc`](crate::component::TypedFunc). #[derive(PartialEq, Eq, Clone)] pub struct OptionVal { ty: types::OptionType, discriminant: u32, value: Option>, } impl OptionVal { /// Creates a new value with the `ty` specified holding the payload `value`. /// /// # Errors /// /// Returns an error if `value` is `Some` and the payload has the wrong /// type for `ty`. pub fn new(ty: &types::OptionType, value: Option) -> Result { let value = value .map(|value| { ty.ty() .is_supertype_of(&value) .context("type mismatch for option")?; Ok::<_, Error>(value) }) .transpose()?; Ok(Self { ty: ty.clone(), discriminant: if value.is_none() { 0 } else { 1 }, value: value.map(Box::new), }) } /// Returns the type of this value. pub fn ty(&self) -> &types::OptionType { &self.ty } /// Returns the optional value contained within. pub fn value(&self) -> Option<&Val> { self.value.as_deref() } fn as_generic<'a>( &'a self, types: &'a ComponentTypes, ty: InterfaceType, ) -> GenericVariant<'a> { let ty = match ty { InterfaceType::Option(i) => &types[i], _ => bad_type_info(), }; GenericVariant { discriminant: self.discriminant, abi: &ty.abi, info: &ty.info, payload: self.value.as_deref().zip(Some(ty.ty)), } } } impl fmt::Debug for OptionVal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.value().fmt(f) } } /// Dynamic representation of `result` in the component model. /// /// This structure is part of the dynamically-typed [`Val`] which represents /// all possible values in the component model. This is often used in /// conjunction with [`Func::call`](crate::component::Func::call). /// /// Note that tuples are also mapped to Rust's `Result` when using /// [`TypedFunc`](crate::component::TypedFunc). #[derive(PartialEq, Eq, Clone)] pub struct ResultVal { ty: types::ResultType, discriminant: u32, value: Option>, } impl ResultVal { /// Creates a new `Result` from the specified `ty` and `value`. /// /// Note that the `value` uses `Option` for payloads to map to the /// optional payload value of the component model within each variant. /// /// # Errors /// /// Returns an error if the `value` payload has the wrong type for `ty`. pub fn new(ty: &types::ResultType, value: Result, Option>) -> Result { Ok(Self { ty: ty.clone(), discriminant: if value.is_ok() { 0 } else { 1 }, value: match value { Ok(value) => { typecheck_payload("ok", ty.ok().as_ref(), value.as_ref())?; value.map(Box::new) } Err(value) => { typecheck_payload("err", ty.err().as_ref(), value.as_ref())?; value.map(Box::new) } }, }) } /// Returns the type of this value. pub fn ty(&self) -> &types::ResultType { &self.ty } /// Returns the result value contained within. pub fn value(&self) -> Result, Option<&Val>> { if self.discriminant == 0 { Ok(self.value.as_deref()) } else { Err(self.value.as_deref()) } } fn as_generic<'a>( &'a self, types: &'a ComponentTypes, ty: InterfaceType, ) -> GenericVariant<'a> { let ty = match ty { InterfaceType::Result(i) => &types[i], _ => bad_type_info(), }; GenericVariant { discriminant: self.discriminant, abi: &ty.abi, info: &ty.info, payload: self.value.as_deref().zip(if self.discriminant == 0 { ty.ok } else { ty.err }), } } } impl fmt::Debug for ResultVal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.value().fmt(f) } } /// Dynamic representation of `flags` in the component model. /// /// This structure is part of the dynamically-typed [`Val`] which represents /// all possible values in the component model. This is often used in /// conjunction with [`Func::call`](crate::component::Func::call). /// /// Note that flags can also be mapped to Rust structures through the /// [`flags!`](crate::component::flags) macro and using /// [`TypedFunc`](crate::component::TypedFunc). #[derive(PartialEq, Eq, Clone)] pub struct Flags { ty: types::Flags, count: u32, value: Box<[u32]>, } impl Flags { /// Creates a new set of flags with the `ty` specified and the `names` as /// members of the flags. /// /// # Errors /// /// An error is returned if any of `names` aren't present within the `ty` /// specified. pub fn new(ty: &types::Flags, names: &[&str]) -> Result { let map = ty .names() .enumerate() .map(|(index, name)| (name, index)) .collect::>(); let count = usize::from(ty.canonical_abi().flat_count.unwrap()); let mut values = vec![0_u32; count]; for name in names { let index = map .get(name) .ok_or_else(|| anyhow!("unknown flag: {name}"))?; values[index / 32] |= 1 << (index % 32); } Ok(Self { ty: ty.clone(), count: u32::try_from(map.len())?, value: values.into(), }) } /// Returns the type of this value. pub fn ty(&self) -> &types::Flags { &self.ty } /// Returns an iterator over the set of names that this flags set contains. pub fn flags(&self) -> impl Iterator { (0..self.count).filter_map(|i| { let (idx, bit) = ((i / 32) as usize, i % 32); if self.value[idx] & (1 << bit) != 0 { Some(self.ty.names().nth(i as usize).unwrap()) } else { None } }) } } impl fmt::Debug for Flags { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut set = f.debug_set(); for flag in self.flags() { set.entry(&flag); } set.finish() } } /// Represents possible runtime values which a component function can either /// consume or produce /// /// This is a dynamic representation of possible values in the component model. /// Note that this is not an efficient representation but is instead intended to /// be a flexible and somewhat convenient representation. The most efficient /// representation of component model types is to use the `bindgen!` macro to /// generate native Rust types with specialized liftings and lowerings. /// /// This type is used in conjunction with [`Func::call`] for example if the /// signature of a component is not statically known ahead of time. /// /// # Notes on Equality /// /// This type implements both the Rust `PartialEq` and `Eq` traits. This type /// additionally contains values which are not necessarily easily equated, /// however, such as floats (`Float32` and `Float64`) and resources. Equality /// does require that two values have the same type, and then these cases are /// handled as: /// /// * Floats are tested if they are "semantically the same" meaning all NaN /// values are equal to all other NaN values. Additionally zero values must be /// exactly the same, so positive zero is not equal to negative zero. The /// primary use case at this time is fuzzing-related equality which this is /// sufficient for. /// /// * Resources are tested if their types and indices into the host table are /// equal. This does not compare the underlying representation so borrows of /// the same guest resource are not considered equal. This additionally /// doesn't go further and test for equality in the guest itself (for example /// two different heap allocations of `Box` can be equal in normal Rust /// if they contain the same value, but will never be considered equal when /// compared as `Val::Resource`s). /// /// In general if a strict guarantee about equality is required here it's /// recommended to "build your own" as this equality intended for fuzzing /// Wasmtime may not be suitable for you. /// /// [`Func::call`]: crate::component::Func::call #[derive(Debug, Clone)] #[allow(missing_docs)] pub enum Val { Bool(bool), S8(i8), U8(u8), S16(i16), U16(u16), S32(i32), U32(u32), S64(i64), U64(u64), Float32(f32), Float64(f64), Char(char), String(Box), List(List), Record(Record), Tuple(Tuple), Variant(Variant), Enum(Enum), Option(OptionVal), Result(ResultVal), Flags(Flags), Resource(ResourceAny), } impl Val { /// Retrieve the [`Type`] of this value. pub fn ty(&self) -> Type { match self { Val::Bool(_) => Type::Bool, Val::S8(_) => Type::S8, Val::U8(_) => Type::U8, Val::S16(_) => Type::S16, Val::U16(_) => Type::U16, Val::S32(_) => Type::S32, Val::U32(_) => Type::U32, Val::S64(_) => Type::S64, Val::U64(_) => Type::U64, Val::Float32(_) => Type::Float32, Val::Float64(_) => Type::Float64, Val::Char(_) => Type::Char, Val::String(_) => Type::String, Val::List(List { ty, .. }) => Type::List(ty.clone()), Val::Record(Record { ty, .. }) => Type::Record(ty.clone()), Val::Tuple(Tuple { ty, .. }) => Type::Tuple(ty.clone()), Val::Variant(Variant { ty, .. }) => Type::Variant(ty.clone()), Val::Enum(Enum { ty, .. }) => Type::Enum(ty.clone()), Val::Option(OptionVal { ty, .. }) => Type::Option(ty.clone()), Val::Result(ResultVal { ty, .. }) => Type::Result(ty.clone()), Val::Flags(Flags { ty, .. }) => Type::Flags(ty.clone()), Val::Resource(r) => { if r.owned() { Type::Own(r.ty()) } else { Type::Borrow(r.ty()) } } } } /// Deserialize a value of this type from core Wasm stack values. pub(crate) fn lift( cx: &mut LiftContext<'_>, ty: InterfaceType, src: &mut std::slice::Iter<'_, ValRaw>, ) -> Result { Ok(match ty { InterfaceType::Bool => Val::Bool(bool::lift(cx, ty, next(src))?), InterfaceType::S8 => Val::S8(i8::lift(cx, ty, next(src))?), InterfaceType::U8 => Val::U8(u8::lift(cx, ty, next(src))?), InterfaceType::S16 => Val::S16(i16::lift(cx, ty, next(src))?), InterfaceType::U16 => Val::U16(u16::lift(cx, ty, next(src))?), InterfaceType::S32 => Val::S32(i32::lift(cx, ty, next(src))?), InterfaceType::U32 => Val::U32(u32::lift(cx, ty, next(src))?), InterfaceType::S64 => Val::S64(i64::lift(cx, ty, next(src))?), InterfaceType::U64 => Val::U64(u64::lift(cx, ty, next(src))?), InterfaceType::Float32 => Val::Float32(f32::lift(cx, ty, next(src))?), InterfaceType::Float64 => Val::Float64(f64::lift(cx, ty, next(src))?), InterfaceType::Char => Val::Char(char::lift(cx, ty, next(src))?), InterfaceType::Own(_) | InterfaceType::Borrow(_) => { Val::Resource(ResourceAny::lift(cx, ty, next(src))?) } InterfaceType::String => { Val::String(Box::::lift(cx, ty, &[*next(src), *next(src)])?) } InterfaceType::List(i) => { // FIXME: needs memory64 treatment let ptr = u32::lift(cx, InterfaceType::U32, next(src))? as usize; let len = u32::lift(cx, InterfaceType::U32, next(src))? as usize; load_list(cx, i, ptr, len)? } InterfaceType::Record(i) => Val::Record(Record { ty: types::Record::from(i, &cx.instance_type()), values: cx.types[i] .fields .iter() .map(|field| Self::lift(cx, field.ty, src)) .collect::>()?, }), InterfaceType::Tuple(i) => Val::Tuple(Tuple { ty: types::Tuple::from(i, &cx.instance_type()), values: cx.types[i] .types .iter() .map(|ty| Self::lift(cx, *ty, src)) .collect::>()?, }), InterfaceType::Variant(i) => { let (discriminant, value) = lift_variant( cx, cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(), cx.types[i].cases.iter().map(|case| case.ty), src, )?; Val::Variant(Variant { ty: types::Variant::from(i, &cx.instance_type()), discriminant, value, }) } InterfaceType::Enum(i) => { let (discriminant, _) = lift_variant( cx, cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(), cx.types[i].names.iter().map(|_| None), src, )?; Val::Enum(Enum { ty: types::Enum::from(i, &cx.instance_type()), discriminant, }) } InterfaceType::Option(i) => { let (discriminant, value) = lift_variant( cx, cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(), [None, Some(cx.types[i].ty)].into_iter(), src, )?; Val::Option(OptionVal { ty: types::OptionType::from(i, &cx.instance_type()), discriminant, value, }) } InterfaceType::Result(i) => { let result_ty = &cx.types[i]; let (discriminant, value) = lift_variant( cx, cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(), [result_ty.ok, result_ty.err].into_iter(), src, )?; Val::Result(ResultVal { ty: types::ResultType::from(i, &cx.instance_type()), discriminant, value, }) } InterfaceType::Flags(i) => { let count = u32::try_from(cx.types[i].names.len()).unwrap(); let u32_count = cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(); let value = iter::repeat_with(|| u32::lift(cx, InterfaceType::U32, next(src))) .take(u32_count) .collect::>()?; Val::Flags(Flags { ty: types::Flags::from(i, &cx.instance_type()), count, value, }) } }) } /// Deserialize a value of this type from the heap. pub(crate) fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result { Ok(match ty { InterfaceType::Bool => Val::Bool(bool::load(cx, ty, bytes)?), InterfaceType::S8 => Val::S8(i8::load(cx, ty, bytes)?), InterfaceType::U8 => Val::U8(u8::load(cx, ty, bytes)?), InterfaceType::S16 => Val::S16(i16::load(cx, ty, bytes)?), InterfaceType::U16 => Val::U16(u16::load(cx, ty, bytes)?), InterfaceType::S32 => Val::S32(i32::load(cx, ty, bytes)?), InterfaceType::U32 => Val::U32(u32::load(cx, ty, bytes)?), InterfaceType::S64 => Val::S64(i64::load(cx, ty, bytes)?), InterfaceType::U64 => Val::U64(u64::load(cx, ty, bytes)?), InterfaceType::Float32 => Val::Float32(f32::load(cx, ty, bytes)?), InterfaceType::Float64 => Val::Float64(f64::load(cx, ty, bytes)?), InterfaceType::Char => Val::Char(char::load(cx, ty, bytes)?), InterfaceType::String => Val::String(>::load(cx, ty, bytes)?), InterfaceType::Own(_) | InterfaceType::Borrow(_) => { Val::Resource(ResourceAny::load(cx, ty, bytes)?) } InterfaceType::List(i) => { // FIXME: needs memory64 treatment let ptr = u32::from_le_bytes(bytes[..4].try_into().unwrap()) as usize; let len = u32::from_le_bytes(bytes[4..].try_into().unwrap()) as usize; load_list(cx, i, ptr, len)? } InterfaceType::Record(i) => Val::Record(Record { ty: types::Record::from(i, &cx.instance_type()), values: load_record(cx, cx.types[i].fields.iter().map(|field| field.ty), bytes)?, }), InterfaceType::Tuple(i) => Val::Tuple(Tuple { ty: types::Tuple::from(i, &cx.instance_type()), values: load_record(cx, cx.types[i].types.iter().copied(), bytes)?, }), InterfaceType::Variant(i) => { let ty = &cx.types[i]; let (discriminant, value) = load_variant(cx, &ty.info, ty.cases.iter().map(|case| case.ty), bytes)?; Val::Variant(Variant { ty: types::Variant::from(i, &cx.instance_type()), discriminant, value, }) } InterfaceType::Enum(i) => { let ty = &cx.types[i]; let (discriminant, _) = load_variant(cx, &ty.info, ty.names.iter().map(|_| None), bytes)?; Val::Enum(Enum { ty: types::Enum::from(i, &cx.instance_type()), discriminant, }) } InterfaceType::Option(i) => { let ty = &cx.types[i]; let (discriminant, value) = load_variant(cx, &ty.info, [None, Some(ty.ty)].into_iter(), bytes)?; Val::Option(OptionVal { ty: types::OptionType::from(i, &cx.instance_type()), discriminant, value, }) } InterfaceType::Result(i) => { let ty = &cx.types[i]; let (discriminant, value) = load_variant(cx, &ty.info, [ty.ok, ty.err].into_iter(), bytes)?; Val::Result(ResultVal { ty: types::ResultType::from(i, &cx.instance_type()), discriminant, value, }) } InterfaceType::Flags(i) => Val::Flags(Flags { ty: types::Flags::from(i, &cx.instance_type()), count: u32::try_from(cx.types[i].names.len())?, value: match FlagsSize::from_count(cx.types[i].names.len()) { FlagsSize::Size0 => Box::new([]), FlagsSize::Size1 => { iter::once(u8::load(cx, InterfaceType::U8, bytes)?.into()).collect() } FlagsSize::Size2 => { iter::once(u16::load(cx, InterfaceType::U16, bytes)?.into()).collect() } FlagsSize::Size4Plus(n) => (0..n) .map(|index| { u32::load( cx, InterfaceType::U32, &bytes[usize::from(index) * 4..][..4], ) }) .collect::>()?, }, }), }) } /// Serialize this value as core Wasm stack values. pub(crate) fn lower( &self, cx: &mut LowerContext<'_, T>, ty: InterfaceType, dst: &mut std::slice::IterMut<'_, MaybeUninit>, ) -> Result<()> { match self { Val::Bool(value) => value.lower(cx, ty, next_mut(dst))?, Val::S8(value) => value.lower(cx, ty, next_mut(dst))?, Val::U8(value) => value.lower(cx, ty, next_mut(dst))?, Val::S16(value) => value.lower(cx, ty, next_mut(dst))?, Val::U16(value) => value.lower(cx, ty, next_mut(dst))?, Val::S32(value) => value.lower(cx, ty, next_mut(dst))?, Val::U32(value) => value.lower(cx, ty, next_mut(dst))?, Val::S64(value) => value.lower(cx, ty, next_mut(dst))?, Val::U64(value) => value.lower(cx, ty, next_mut(dst))?, Val::Float32(value) => value.lower(cx, ty, next_mut(dst))?, Val::Float64(value) => value.lower(cx, ty, next_mut(dst))?, Val::Char(value) => value.lower(cx, ty, next_mut(dst))?, Val::Resource(value) => value.lower(cx, ty, next_mut(dst))?, Val::String(value) => { let my_dst = &mut MaybeUninit::<[ValRaw; 2]>::uninit(); value.lower(cx, ty, my_dst)?; let my_dst = unsafe { my_dst.assume_init() }; next_mut(dst).write(my_dst[0]); next_mut(dst).write(my_dst[1]); } Val::List(List { values, .. }) => { let ty = match ty { InterfaceType::List(i) => &cx.types[i], _ => bad_type_info(), }; let (ptr, len) = lower_list(cx, ty.element, values)?; next_mut(dst).write(ValRaw::i64(ptr as i64)); next_mut(dst).write(ValRaw::i64(len as i64)); } Val::Record(Record { values, .. }) => { let ty = match ty { InterfaceType::Record(i) => &cx.types[i], _ => bad_type_info(), }; for (value, field) in values.iter().zip(ty.fields.iter()) { value.lower(cx, field.ty, dst)?; } } Val::Tuple(Tuple { values, .. }) => { let ty = match ty { InterfaceType::Tuple(i) => &cx.types[i], _ => bad_type_info(), }; for (value, ty) in values.iter().zip(ty.types.iter()) { value.lower(cx, *ty, dst)?; } } Val::Variant(v) => v.as_generic(cx.types, ty).lower(cx, dst)?, Val::Option(v) => v.as_generic(cx.types, ty).lower(cx, dst)?, Val::Result(v) => v.as_generic(cx.types, ty).lower(cx, dst)?, Val::Enum(Enum { discriminant, .. }) => { next_mut(dst).write(ValRaw::u32(*discriminant)); } Val::Flags(Flags { value, .. }) => { for value in value.deref() { next_mut(dst).write(ValRaw::u32(*value)); } } } Ok(()) } /// Serialize this value to the heap at the specified memory location. pub(crate) fn store( &self, cx: &mut LowerContext<'_, T>, ty: InterfaceType, offset: usize, ) -> Result<()> { debug_assert!(offset % usize::try_from(cx.types.canonical_abi(&ty).align32)? == 0); match self { Val::Bool(value) => value.store(cx, ty, offset)?, Val::U8(value) => value.store(cx, ty, offset)?, Val::S8(value) => value.store(cx, ty, offset)?, Val::U16(value) => value.store(cx, ty, offset)?, Val::S16(value) => value.store(cx, ty, offset)?, Val::U32(value) => value.store(cx, ty, offset)?, Val::S32(value) => value.store(cx, ty, offset)?, Val::U64(value) => value.store(cx, ty, offset)?, Val::S64(value) => value.store(cx, ty, offset)?, Val::Float32(value) => value.store(cx, ty, offset)?, Val::Float64(value) => value.store(cx, ty, offset)?, Val::Char(value) => value.store(cx, ty, offset)?, Val::String(value) => value.store(cx, ty, offset)?, Val::Resource(value) => value.store(cx, ty, offset)?, Val::List(List { values, .. }) => { let ty = match ty { InterfaceType::List(i) => &cx.types[i], _ => bad_type_info(), }; let (ptr, len) = lower_list(cx, ty.element, values)?; // FIXME: needs memory64 handling *cx.get(offset + 0) = (ptr as i32).to_le_bytes(); *cx.get(offset + 4) = (len as i32).to_le_bytes(); } Val::Record(Record { values, .. }) => { let ty = match ty { InterfaceType::Record(i) => &cx.types[i], _ => bad_type_info(), }; let mut offset = offset; for (value, field) in values.iter().zip(ty.fields.iter()) { value.store( cx, field.ty, cx.types .canonical_abi(&field.ty) .next_field32_size(&mut offset), )?; } } Val::Tuple(Tuple { values, .. }) => { let ty = match ty { InterfaceType::Tuple(i) => &cx.types[i], _ => bad_type_info(), }; let mut offset = offset; for (value, ty) in values.iter().zip(ty.types.iter()) { value.store( cx, *ty, cx.types.canonical_abi(ty).next_field32_size(&mut offset), )?; } } Val::Variant(v) => v.as_generic(cx.types, ty).store(cx, offset)?, Val::Enum(v) => v.as_generic(cx.types, ty).store(cx, offset)?, Val::Option(v) => v.as_generic(cx.types, ty).store(cx, offset)?, Val::Result(v) => v.as_generic(cx.types, ty).store(cx, offset)?, Val::Flags(Flags { count, value, .. }) => { match FlagsSize::from_count(*count as usize) { FlagsSize::Size0 => {} FlagsSize::Size1 => { u8::try_from(value[0]) .unwrap() .store(cx, InterfaceType::U8, offset)? } FlagsSize::Size2 => { u16::try_from(value[0]) .unwrap() .store(cx, InterfaceType::U16, offset)? } FlagsSize::Size4Plus(_) => { let mut offset = offset; for value in value.deref() { value.store(cx, InterfaceType::U32, offset)?; offset += 4; } } } } } Ok(()) } } impl PartialEq for Val { fn eq(&self, other: &Self) -> bool { match (self, other) { // IEEE 754 equality considers NaN inequal to NaN and negative zero // equal to positive zero, however we do the opposite here, because // this logic is used by testing and fuzzing, which want to know // whether two values are semantically the same, rather than // numerically equal. (Self::Float32(l), Self::Float32(r)) => { (*l != 0.0 && l == r) || (*l == 0.0 && l.to_bits() == r.to_bits()) || (l.is_nan() && r.is_nan()) } (Self::Float32(_), _) => false, (Self::Float64(l), Self::Float64(r)) => { (*l != 0.0 && l == r) || (*l == 0.0 && l.to_bits() == r.to_bits()) || (l.is_nan() && r.is_nan()) } (Self::Float64(_), _) => false, (Self::Bool(l), Self::Bool(r)) => l == r, (Self::Bool(_), _) => false, (Self::S8(l), Self::S8(r)) => l == r, (Self::S8(_), _) => false, (Self::U8(l), Self::U8(r)) => l == r, (Self::U8(_), _) => false, (Self::S16(l), Self::S16(r)) => l == r, (Self::S16(_), _) => false, (Self::U16(l), Self::U16(r)) => l == r, (Self::U16(_), _) => false, (Self::S32(l), Self::S32(r)) => l == r, (Self::S32(_), _) => false, (Self::U32(l), Self::U32(r)) => l == r, (Self::U32(_), _) => false, (Self::S64(l), Self::S64(r)) => l == r, (Self::S64(_), _) => false, (Self::U64(l), Self::U64(r)) => l == r, (Self::U64(_), _) => false, (Self::Char(l), Self::Char(r)) => l == r, (Self::Char(_), _) => false, (Self::String(l), Self::String(r)) => l == r, (Self::String(_), _) => false, (Self::List(l), Self::List(r)) => l == r, (Self::List(_), _) => false, (Self::Record(l), Self::Record(r)) => l == r, (Self::Record(_), _) => false, (Self::Tuple(l), Self::Tuple(r)) => l == r, (Self::Tuple(_), _) => false, (Self::Variant(l), Self::Variant(r)) => l == r, (Self::Variant(_), _) => false, (Self::Enum(l), Self::Enum(r)) => l == r, (Self::Enum(_), _) => false, (Self::Option(l), Self::Option(r)) => l == r, (Self::Option(_), _) => false, (Self::Result(l), Self::Result(r)) => l == r, (Self::Result(_), _) => false, (Self::Flags(l), Self::Flags(r)) => l == r, (Self::Flags(_), _) => false, (Self::Resource(l), Self::Resource(r)) => l == r, (Self::Resource(_), _) => false, } } } impl Eq for Val {} struct GenericVariant<'a> { discriminant: u32, payload: Option<(&'a Val, InterfaceType)>, abi: &'a CanonicalAbiInfo, info: &'a VariantInfo, } impl GenericVariant<'_> { fn lower( &self, cx: &mut LowerContext<'_, T>, dst: &mut std::slice::IterMut<'_, MaybeUninit>, ) -> Result<()> { next_mut(dst).write(ValRaw::u32(self.discriminant)); // For the remaining lowered representation of this variant that // the payload didn't write we write out zeros here to ensure // the entire variant is written. let value_flat = match self.payload { Some((value, ty)) => { value.lower(cx, ty, dst)?; cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap() } None => 0, }; let variant_flat = self.abi.flat_count(usize::MAX).unwrap(); for _ in (1 + value_flat)..variant_flat { next_mut(dst).write(ValRaw::u64(0)); } Ok(()) } fn store(&self, cx: &mut LowerContext<'_, T>, offset: usize) -> Result<()> { match self.info.size { DiscriminantSize::Size1 => { u8::try_from(self.discriminant) .unwrap() .store(cx, InterfaceType::U8, offset)? } DiscriminantSize::Size2 => { u16::try_from(self.discriminant) .unwrap() .store(cx, InterfaceType::U16, offset)? } DiscriminantSize::Size4 => self.discriminant.store(cx, InterfaceType::U32, offset)?, } if let Some((value, ty)) = self.payload { let offset = offset + usize::try_from(self.info.payload_offset32).unwrap(); value.store(cx, ty, offset)?; } Ok(()) } } fn load_list(cx: &mut LiftContext<'_>, ty: TypeListIndex, ptr: usize, len: usize) -> Result { let elem = cx.types[ty].element; let abi = cx.types.canonical_abi(&elem); let element_size = usize::try_from(abi.size32).unwrap(); let element_alignment = abi.align32; match len .checked_mul(element_size) .and_then(|len| ptr.checked_add(len)) { Some(n) if n <= cx.memory().len() => {} _ => bail!("list pointer/length out of bounds of memory"), } if ptr % usize::try_from(element_alignment)? != 0 { bail!("list pointer is not aligned") } Ok(Val::List(List { ty: types::List::from(ty, &cx.instance_type()), values: (0..len) .map(|index| { Val::load( cx, elem, &cx.memory()[ptr + (index * element_size)..][..element_size], ) }) .collect::>()?, })) } fn load_record( cx: &mut LiftContext<'_>, types: impl Iterator, bytes: &[u8], ) -> Result> { let mut offset = 0; types .map(|ty| { let abi = cx.types.canonical_abi(&ty); let offset = abi.next_field32(&mut offset); let offset = usize::try_from(offset).unwrap(); let size = usize::try_from(abi.size32).unwrap(); Val::load(cx, ty, &bytes[offset..][..size]) }) .collect() } fn load_variant( cx: &mut LiftContext<'_>, info: &VariantInfo, mut types: impl ExactSizeIterator>, bytes: &[u8], ) -> Result<(u32, Option>)> { let discriminant = match info.size { DiscriminantSize::Size1 => u32::from(u8::load(cx, InterfaceType::U8, &bytes[..1])?), DiscriminantSize::Size2 => u32::from(u16::load(cx, InterfaceType::U16, &bytes[..2])?), DiscriminantSize::Size4 => u32::load(cx, InterfaceType::U32, &bytes[..4])?, }; let case_ty = types.nth(discriminant as usize).ok_or_else(|| { anyhow!( "discriminant {} out of range [0..{})", discriminant, types.len() ) })?; let value = match case_ty { Some(case_ty) => { let payload_offset = usize::try_from(info.payload_offset32).unwrap(); let case_abi = cx.types.canonical_abi(&case_ty); let case_size = usize::try_from(case_abi.size32).unwrap(); Some(Box::new(Val::load( cx, case_ty, &bytes[payload_offset..][..case_size], )?)) } None => None, }; Ok((discriminant, value)) } fn lift_variant( cx: &mut LiftContext<'_>, flatten_count: usize, mut types: impl ExactSizeIterator>, src: &mut std::slice::Iter<'_, ValRaw>, ) -> Result<(u32, Option>)> { let len = types.len(); let discriminant = next(src).get_u32(); let ty = types .nth(discriminant as usize) .ok_or_else(|| anyhow!("discriminant {} out of range [0..{})", discriminant, len))?; let (value, value_flat) = match ty { Some(ty) => ( Some(Box::new(Val::lift(cx, ty, src)?)), cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(), ), None => (None, 0), }; for _ in (1 + value_flat)..flatten_count { next(src); } Ok((discriminant, value)) } /// Lower a list with the specified element type and values. fn lower_list( cx: &mut LowerContext<'_, T>, element_type: InterfaceType, items: &[Val], ) -> Result<(usize, usize)> { let abi = cx.types.canonical_abi(&element_type); let elt_size = usize::try_from(abi.size32)?; let elt_align = abi.align32; let size = items .len() .checked_mul(elt_size) .ok_or_else(|| anyhow::anyhow!("size overflow copying a list"))?; let ptr = cx.realloc(0, 0, elt_align, size)?; let mut element_ptr = ptr; for item in items { item.store(cx, element_type, element_ptr)?; element_ptr += elt_size; } Ok((ptr, items.len())) } fn next<'a>(src: &mut std::slice::Iter<'a, ValRaw>) -> &'a ValRaw { src.next().unwrap() } fn next_mut<'a>( dst: &mut std::slice::IterMut<'a, MaybeUninit>, ) -> &'a mut MaybeUninit { dst.next().unwrap() }