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,
};
#[derive(PartialEq, Eq, Clone)]
pub struct List {
ty: types::List,
values: Box<[Val]>,
}
impl List {
pub fn new(ty: &types::List, values: Box<[Val]>) -> Result<Self> {
let element_type = ty.ty();
for (index, value) in values.iter().enumerate() {
element_type
.check(value)
.with_context(|| format!("type mismatch for element {index} of list"))?;
}
Ok(Self {
ty: ty.clone(),
values,
})
}
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()
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct Record {
ty: types::Record,
values: Box<[Val]>,
}
impl Record {
pub fn new<'a>(
ty: &types::Record,
values: impl IntoIterator<Item = (&'a str, Val)>,
) -> Result<Self> {
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
.check(&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(),
})
}
pub fn ty(&self) -> &types::Record {
&self.ty
}
pub fn fields(&self) -> impl Iterator<Item = (&str, &Val)> {
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()
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct Tuple {
ty: types::Tuple,
values: Box<[Val]>,
}
impl Tuple {
pub fn new(ty: &types::Tuple, values: Box<[Val]>) -> Result<Self> {
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.check(value)
.with_context(|| format!("type mismatch for field {index} of tuple"))?;
}
Ok(Self {
ty: ty.clone(),
values,
})
}
pub fn ty(&self) -> &types::Tuple {
&self.ty
}
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()
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct Variant {
ty: types::Variant,
discriminant: u32,
value: Option<Box<Val>>,
}
impl Variant {
pub fn new(ty: &types::Variant, name: &str, value: Option<Val>) -> Result<Self> {
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),
})
}
pub fn ty(&self) -> &types::Variant {
&self.ty
}
pub fn discriminant(&self) -> &str {
self.ty
.cases()
.nth(self.discriminant as usize)
.unwrap()
.name
}
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
.check(&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()
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct Enum {
ty: types::Enum,
discriminant: u32,
}
impl Enum {
pub fn new(ty: &types::Enum, name: &str) -> Result<Self> {
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,
})
}
pub fn ty(&self) -> &types::Enum {
&self.ty
}
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)
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct Union {
ty: types::Union,
discriminant: u32,
value: Option<Box<Val>>,
}
impl Union {
pub fn new(ty: &types::Union, discriminant: u32, value: Val) -> Result<Self> {
if let Some(case_ty) = ty.types().nth(usize::try_from(discriminant)?) {
case_ty
.check(&value)
.with_context(|| format!("type mismatch for case {discriminant} of union"))?;
Ok(Self {
ty: ty.clone(),
discriminant,
value: Some(Box::new(value)),
})
} else {
Err(anyhow!(
"discriminant {discriminant} out of range: [0,{})",
ty.types().len()
))
}
}
pub fn ty(&self) -> &types::Union {
&self.ty
}
pub fn discriminant(&self) -> u32 {
self.discriminant
}
pub fn payload(&self) -> &Val {
self.value.as_ref().unwrap()
}
fn as_generic<'a>(
&'a self,
types: &'a ComponentTypes,
ty: InterfaceType,
) -> GenericVariant<'a> {
let ty = match ty {
InterfaceType::Union(i) => &types[i],
_ => bad_type_info(),
};
GenericVariant {
discriminant: self.discriminant,
abi: &ty.abi,
info: &ty.info,
payload: self
.value
.as_deref()
.zip(Some(ty.types[self.discriminant as usize])),
}
}
}
impl fmt::Debug for Union {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(&format!("U{}", self.discriminant()))
.field(self.payload())
.finish()
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct OptionVal {
ty: types::OptionType,
discriminant: u32,
value: Option<Box<Val>>,
}
impl OptionVal {
pub fn new(ty: &types::OptionType, value: Option<Val>) -> Result<Self> {
let value = value
.map(|value| {
ty.ty().check(&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),
})
}
pub fn ty(&self) -> &types::OptionType {
&self.ty
}
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)
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct ResultVal {
ty: types::ResultType,
discriminant: u32,
value: Option<Box<Val>>,
}
impl ResultVal {
pub fn new(ty: &types::ResultType, value: Result<Option<Val>, Option<Val>>) -> Result<Self> {
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)
}
},
})
}
pub fn ty(&self) -> &types::ResultType {
&self.ty
}
pub fn value(&self) -> Result<Option<&Val>, 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)
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct Flags {
ty: types::Flags,
count: u32,
value: Box<[u32]>,
}
impl Flags {
pub fn new(ty: &types::Flags, names: &[&str]) -> Result<Self> {
let map = ty
.names()
.enumerate()
.map(|(index, name)| (name, index))
.collect::<HashMap<_, _>>();
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(),
})
}
pub fn ty(&self) -> &types::Flags {
&self.ty
}
pub fn flags(&self) -> impl Iterator<Item = &str> {
(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()
}
}
#[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<str>),
List(List),
Record(Record),
Tuple(Tuple),
Variant(Variant),
Enum(Enum),
Union(Union),
Option(OptionVal),
Result(ResultVal),
Flags(Flags),
Resource(ResourceAny),
}
impl Val {
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::Union(Union { ty, .. }) => Type::Union(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())
}
}
}
}
pub(crate) fn lift(
cx: &mut LiftContext<'_>,
ty: InterfaceType,
src: &mut std::slice::Iter<'_, ValRaw>,
) -> Result<Val> {
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::<str>::lift(cx, ty, &[*next(src), *next(src)])?)
}
InterfaceType::List(i) => {
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::<Result<_>>()?,
}),
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::<Result<_>>()?,
}),
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::Union(i) => {
let (discriminant, value) = lift_variant(
cx,
cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(),
cx.types[i].types.iter().copied().map(Some),
src,
)?;
Val::Union(Union {
ty: types::Union::from(i, &cx.instance_type()),
discriminant,
value,
})
}
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::<Result<_>>()?;
Val::Flags(Flags {
ty: types::Flags::from(i, &cx.instance_type()),
count,
value,
})
}
})
}
pub(crate) fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Val> {
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(<Box<str>>::load(cx, ty, bytes)?),
InterfaceType::Own(_) | InterfaceType::Borrow(_) => {
Val::Resource(ResourceAny::load(cx, ty, bytes)?)
}
InterfaceType::List(i) => {
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::Union(i) => {
let ty = &cx.types[i];
let (discriminant, value) =
load_variant(cx, &ty.info, ty.types.iter().copied().map(Some), bytes)?;
Val::Union(Union {
ty: types::Union::from(i, &cx.instance_type()),
discriminant,
value,
})
}
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::<Result<_>>()?,
},
}),
})
}
pub(crate) fn lower<T>(
&self,
cx: &mut LowerContext<'_, T>,
ty: InterfaceType,
dst: &mut std::slice::IterMut<'_, MaybeUninit<ValRaw>>,
) -> 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::Union(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(())
}
pub(crate) fn store<T>(
&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)?;
*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::Union(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) {
(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::Union(l), Self::Union(r)) => l == r,
(Self::Union(_), _) => 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<T>(
&self,
cx: &mut LowerContext<'_, T>,
dst: &mut std::slice::IterMut<'_, MaybeUninit<ValRaw>>,
) -> Result<()> {
next_mut(dst).write(ValRaw::u32(self.discriminant));
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<T>(&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<Val> {
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::<Result<_>>()?,
}))
}
fn load_record(
cx: &mut LiftContext<'_>,
types: impl Iterator<Item = InterfaceType>,
bytes: &[u8],
) -> Result<Box<[Val]>> {
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<Item = Option<InterfaceType>>,
bytes: &[u8],
) -> Result<(u32, Option<Box<Val>>)> {
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<Item = Option<InterfaceType>>,
src: &mut std::slice::Iter<'_, ValRaw>,
) -> Result<(u32, Option<Box<Val>>)> {
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))
}
fn lower_list<T>(
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<ValRaw>>,
) -> &'a mut MaybeUninit<ValRaw> {
dst.next().unwrap()
}