use anyhow::{anyhow, Result};
use wit_parser::{Function as WitFunction, Resolve, Type};
use super::super::super::abi::emit::BlobSlice;
use super::super::super::abi::flat_types;
use super::super::blob::NameInterner;
use super::plan::{Cell, LiftPlan, MapAliases};
use super::sidetable::CellSideData;
pub(crate) struct ResultLift {
pub source: ResultSource,
}
pub(crate) enum ResultSource {
Direct(Cell),
Compound(CompoundResult),
}
pub(crate) struct CompoundResult {
pub ty: Type,
pub plan: LiftPlan,
}
impl ResultLift {
pub(crate) fn compound(&self) -> Option<&CompoundResult> {
match &self.source {
ResultSource::Compound(c) => Some(c),
_ => None,
}
}
pub(crate) fn compound_mut(&mut self) -> Option<&mut CompoundResult> {
match &mut self.source {
ResultSource::Compound(c) => Some(c),
_ => None,
}
}
}
pub(crate) struct ParamLift {
pub name: BlobSlice,
pub plan: LiftPlan,
}
#[derive(Clone, Copy, Default)]
pub(crate) struct InfoCounts {
pub handle: u32,
pub flags: u32,
pub record: u32,
pub variant: u32,
}
pub(crate) struct ParamLayout {
pub lift: ParamLift,
pub cell_side: Vec<CellSideData>,
pub info_counts: InfoCounts,
}
pub(crate) struct ResultLayout {
pub source: ResultSourceLayout,
pub info_counts: InfoCounts,
}
pub(crate) enum ResultSourceLayout {
Direct { cell: Cell, side_data: CellSideData },
Compound {
compound: CompoundResult,
retptr_offset: Option<i32>,
cell_side: Vec<CellSideData>,
},
}
pub(crate) fn classify_func_params(
resolve: &Resolve,
func: &WitFunction,
names: &mut NameInterner,
map_aliases: &MapAliases,
) -> Result<Vec<ParamLift>> {
let mut params_lift: Vec<ParamLift> = Vec::with_capacity(func.params.len());
for param in &func.params {
let name = names.intern(¶m.name);
params_lift.push(ParamLift {
name,
plan: LiftPlan::for_type(¶m.ty, resolve, names, map_aliases)?,
});
}
Ok(params_lift)
}
pub(crate) fn classify_result_lift(
resolve: &Resolve,
func: &WitFunction,
result_at_retptr: bool,
names: &mut NameInterner,
map_aliases: &MapAliases,
) -> Result<Option<ResultLift>> {
let Some(ty) = func.result.as_ref() else {
return Ok(None);
};
if result_at_retptr || is_compound_result(ty, resolve) {
if !is_supported_result(ty, resolve) {
return Ok(None);
}
if flat_types(resolve, ty, None).is_none() {
return Err(anyhow!(
"compound result flat representation exceeds MAX_FLAT_PARAMS ({})",
Resolve::MAX_FLAT_PARAMS,
));
}
let plan = LiftPlan::for_type(ty, resolve, names, map_aliases)?;
return Ok(Some(ResultLift {
source: ResultSource::Compound(CompoundResult { ty: *ty, plan }),
}));
}
let Some(cell) = single_cell_for_result(ty, resolve, names, map_aliases)? else {
return Ok(None);
};
Ok(Some(ResultLift {
source: ResultSource::Direct(cell),
}))
}
fn is_supported_result(ty: &Type, resolve: &Resolve) -> bool {
is_compound_result(ty, resolve) || is_supported_direct_result(ty, resolve)
}
fn is_compound_result(ty: &Type, resolve: &Resolve) -> bool {
let Type::Id(id) = ty else {
return false;
};
match &resolve.types[*id].kind {
wit_parser::TypeDefKind::Record(_)
| wit_parser::TypeDefKind::Tuple(_)
| wit_parser::TypeDefKind::Option(_)
| wit_parser::TypeDefKind::Result(_)
| wit_parser::TypeDefKind::Variant(_)
| wit_parser::TypeDefKind::Map(_, _)
| wit_parser::TypeDefKind::FixedLengthList(_, _) => true,
wit_parser::TypeDefKind::List(elem) => !matches!(elem, Type::U8),
wit_parser::TypeDefKind::Type(t) => is_compound_result(t, resolve),
_ => false,
}
}
fn single_cell_for_result(
ty: &Type,
resolve: &Resolve,
names: &mut NameInterner,
map_aliases: &MapAliases,
) -> Result<Option<Cell>> {
if !is_supported_direct_result(ty, resolve) {
return Ok(None);
}
let plan = LiftPlan::for_type(ty, resolve, names, map_aliases)?;
Ok(Some(
plan.cells.into_iter().next().expect("push appended a cell"),
))
}
fn is_supported_direct_result(ty: &Type, resolve: &Resolve) -> bool {
match ty {
Type::Bool
| Type::S8
| Type::S16
| Type::S32
| Type::U8
| Type::U16
| Type::U32
| Type::S64
| Type::U64
| Type::F32
| Type::F64
| Type::String
| Type::Char
| Type::ErrorContext => true,
Type::Id(id) => match &resolve.types[*id].kind {
wit_parser::TypeDefKind::List(Type::U8) => true,
wit_parser::TypeDefKind::Enum(_) => true,
wit_parser::TypeDefKind::Flags(_) => true,
wit_parser::TypeDefKind::Handle(_) => true,
wit_parser::TypeDefKind::Stream(_) => true,
wit_parser::TypeDefKind::Future(_) => true,
wit_parser::TypeDefKind::Type(t) => is_supported_direct_result(t, resolve),
_ => false,
},
}
}