use crate::{Function, Handle, Int, Resolve, Type, TypeDefKind};
#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct WasmSignature {
pub params: Vec<WasmType>,
pub results: Vec<WasmType>,
pub indirect_params: bool,
pub retptr: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum WasmType {
I32,
I64,
F32,
F64,
Pointer,
PointerOrI64,
Length,
}
fn join(a: WasmType, b: WasmType) -> WasmType {
use WasmType::*;
match (a, b) {
(I32, I32)
| (I64, I64)
| (F32, F32)
| (F64, F64)
| (Pointer, Pointer)
| (PointerOrI64, PointerOrI64)
| (Length, Length) => a,
(I32, F32) | (F32, I32) => I32,
(Length, I32 | F32) => Length,
(I32 | F32, Length) => Length,
(Length, I64 | F64) => I64,
(I64 | F64, Length) => I64,
(Pointer, I32 | F32 | Length) => Pointer,
(I32 | F32 | Length, Pointer) => Pointer,
(Pointer, I64 | F64) => PointerOrI64,
(I64 | F64, Pointer) => PointerOrI64,
(PointerOrI64, _) => PointerOrI64,
(_, PointerOrI64) => PointerOrI64,
(_, I64 | F64) | (I64 | F64, _) => I64,
}
}
impl From<Int> for WasmType {
fn from(i: Int) -> WasmType {
match i {
Int::U8 | Int::U16 | Int::U32 => WasmType::I32,
Int::U64 => WasmType::I64,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum AbiVariant {
GuestImport,
GuestExport,
GuestImportAsync,
GuestExportAsync,
GuestExportAsyncStackful,
}
impl Resolve {
pub fn wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature {
if let AbiVariant::GuestImportAsync = variant {
return WasmSignature {
params: vec![WasmType::Pointer; 2],
indirect_params: true,
results: vec![WasmType::I32],
retptr: true,
};
}
const MAX_FLAT_PARAMS: usize = 16;
const MAX_FLAT_RESULTS: usize = 1;
let mut params = Vec::new();
let mut indirect_params = false;
for (_, param) in func.params.iter() {
self.push_flat(param, &mut params);
}
if params.len() > MAX_FLAT_PARAMS {
params.truncate(0);
params.push(WasmType::Pointer);
indirect_params = true;
} else {
if matches!(
(&func.kind, variant),
(
crate::FunctionKind::Method(_) | crate::FunctionKind::AsyncMethod(_),
AbiVariant::GuestExport
| AbiVariant::GuestExportAsync
| AbiVariant::GuestExportAsyncStackful
)
) {
assert!(matches!(params[0], WasmType::I32));
params[0] = WasmType::Pointer;
}
}
match variant {
AbiVariant::GuestExportAsync => {
return WasmSignature {
params,
indirect_params,
results: vec![WasmType::Pointer],
retptr: false,
};
}
AbiVariant::GuestExportAsyncStackful => {
return WasmSignature {
params,
indirect_params,
results: Vec::new(),
retptr: false,
};
}
_ => {}
}
let mut results = Vec::new();
if let Some(ty) = &func.result {
self.push_flat(ty, &mut results)
}
let mut retptr = false;
if results.len() > MAX_FLAT_RESULTS {
retptr = true;
results.truncate(0);
match variant {
AbiVariant::GuestImport => {
params.push(WasmType::Pointer);
}
AbiVariant::GuestExport => {
results.push(WasmType::Pointer);
}
_ => unreachable!(),
}
}
WasmSignature {
params,
indirect_params,
results,
retptr,
}
}
pub fn push_flat(&self, ty: &Type, result: &mut Vec<WasmType>) {
match ty {
Type::Bool
| Type::S8
| Type::U8
| Type::S16
| Type::U16
| Type::S32
| Type::U32
| Type::Char
| Type::ErrorContext => result.push(WasmType::I32),
Type::U64 | Type::S64 => result.push(WasmType::I64),
Type::F32 => result.push(WasmType::F32),
Type::F64 => result.push(WasmType::F64),
Type::String => {
result.push(WasmType::Pointer);
result.push(WasmType::Length);
}
Type::Id(id) => match &self.types[*id].kind {
TypeDefKind::Type(t) => self.push_flat(t, result),
TypeDefKind::Handle(Handle::Own(_) | Handle::Borrow(_)) => {
result.push(WasmType::I32);
}
TypeDefKind::Resource => todo!(),
TypeDefKind::Record(r) => {
for field in r.fields.iter() {
self.push_flat(&field.ty, result);
}
}
TypeDefKind::Tuple(t) => {
for ty in t.types.iter() {
self.push_flat(ty, result);
}
}
TypeDefKind::Flags(r) => {
for _ in 0..r.repr().count() {
result.push(WasmType::I32);
}
}
TypeDefKind::List(_) => {
result.push(WasmType::Pointer);
result.push(WasmType::Length);
}
TypeDefKind::Variant(v) => {
result.push(v.tag().into());
self.push_flat_variants(v.cases.iter().map(|c| c.ty.as_ref()), result);
}
TypeDefKind::Enum(e) => result.push(e.tag().into()),
TypeDefKind::Option(t) => {
result.push(WasmType::I32);
self.push_flat_variants([None, Some(t)], result);
}
TypeDefKind::Result(r) => {
result.push(WasmType::I32);
self.push_flat_variants([r.ok.as_ref(), r.err.as_ref()], result);
}
TypeDefKind::Future(_) => {
result.push(WasmType::I32);
}
TypeDefKind::Stream(_) => {
result.push(WasmType::I32);
}
TypeDefKind::Unknown => unreachable!(),
},
}
}
fn push_flat_variants<'a>(
&self,
tys: impl IntoIterator<Item = Option<&'a Type>>,
result: &mut Vec<WasmType>,
) {
let mut temp = Vec::new();
let start = result.len();
for ty in tys {
if let Some(ty) = ty {
self.push_flat(ty, &mut temp);
for (i, ty) in temp.drain(..).enumerate() {
match result.get_mut(start + i) {
Some(prev) => *prev = join(*prev, ty),
None => result.push(ty),
}
}
}
}
}
}