use crate::WitIdsToIndices;
use crate::{ImportId, TypeId, ValType, WasmInterfaceTypes, WitIndicesToIds};
use anyhow::Result;
use id_arena::{Arena, Id};
use walrus::IndicesToIds;
#[derive(Debug, Default)]
pub struct Funcs {
arena: Arena<Func>,
}
#[derive(Debug)]
pub struct Func {
id: FuncId,
pub ty: TypeId,
pub kind: FuncKind,
}
#[derive(Debug)]
pub enum FuncKind {
Import(ImportId),
Local(Vec<Instruction>),
}
#[derive(Debug, Clone)]
pub enum Instruction {
CallCore(walrus::FunctionId),
DeferCallCore(walrus::FunctionId),
CallAdapter(FuncId),
ArgGet(u32),
MemoryToString(walrus::MemoryId),
StringToMemory {
mem: walrus::MemoryId,
malloc: walrus::FunctionId,
},
IntToWasm {
input: ValType,
output: walrus::ValType,
trap: bool,
},
WasmToInt {
input: walrus::ValType,
output: ValType,
trap: bool,
},
}
pub type FuncId = Id<Func>;
impl WasmInterfaceTypes {
pub(crate) fn parse_funcs(
&mut self,
funcs: wit_parser::Funcs,
ids: &IndicesToIds,
wids: &mut WitIndicesToIds,
) -> Result<()> {
let mut instrs = Vec::new();
for func in funcs {
let func = func?;
let ty = wids.ty(func.ty)?;
let id = self.funcs.add_local(ty, Vec::new());
wids.funcs.push(id);
instrs.push((id, func.instrs()));
}
for (id, instrs) in instrs {
let mut list = Vec::new();
for instr in instrs {
use wit_parser::Instruction as R;
use Instruction as W;
use walrus::ValType as RVT;
use crate::ValType as VT;
fn w2i(input: walrus::ValType, output: ValType, trap: bool) -> W {
W::WasmToInt { input, output, trap }
}
fn i2w(input: ValType, output: walrus::ValType, trap: bool) -> W {
W::IntToWasm { input, output, trap }
}
list.push(match instr? {
R::CallCore(id) => W::CallCore(ids.get_func(id)?),
R::CallAdapter(id) => W::CallAdapter(wids.func(id)?),
R::ArgGet(idx) => W::ArgGet(idx),
R::MemoryToString(mem) => W::MemoryToString(ids.get_memory(mem)?),
R::StringToMemory(args) => W::StringToMemory {
mem: ids.get_memory(args.mem)?,
malloc: ids.get_func(args.malloc)?,
},
R::DeferCallCore(id) => W::DeferCallCore(ids.get_func(id)?),
R::I32ToS8 => w2i(RVT::I32, VT::S8, false),
R::I32ToS8X => w2i(RVT::I32, VT::S8, true),
R::I32ToU8 => w2i(RVT::I32, VT::U8, false),
R::I32ToS16 => w2i(RVT::I32, VT::S16, false),
R::I32ToS16X => w2i(RVT::I32, VT::S16, true),
R::I32ToU16 => w2i(RVT::I32, VT::U16, false),
R::I32ToS32 => w2i(RVT::I32, VT::S32, false),
R::I32ToU32 => w2i(RVT::I32, VT::U32, false),
R::I32ToS64 => w2i(RVT::I32, VT::S64, false),
R::I32ToU64 => w2i(RVT::I32, VT::U64, false),
R::I64ToS8 => w2i(RVT::I64, VT::S8, false),
R::I64ToS8X => w2i(RVT::I64, VT::S8, true),
R::I64ToU8 => w2i(RVT::I64, VT::U8, false),
R::I64ToS16 => w2i(RVT::I64, VT::S16, false),
R::I64ToS16X => w2i(RVT::I64, VT::S16, true),
R::I64ToU16 => w2i(RVT::I64, VT::U16, false),
R::I64ToS32 => w2i(RVT::I64, VT::S32, false),
R::I64ToS32X => w2i(RVT::I64, VT::S32, true),
R::I64ToU32 => w2i(RVT::I64, VT::U32, false),
R::I64ToS64 => w2i(RVT::I64, VT::S64, false),
R::I64ToU64 => w2i(RVT::I64, VT::U64, false),
R::S8ToI32 => i2w(VT::S8, RVT::I32, false),
R::U8ToI32 => i2w(VT::U8, RVT::I32, false),
R::S16ToI32 => i2w(VT::S16, RVT::I32, false),
R::U16ToI32 => i2w(VT::U16, RVT::I32, false),
R::S32ToI32 => i2w(VT::S32, RVT::I32, false),
R::U32ToI32 => i2w(VT::U32, RVT::I32, false),
R::S64ToI32 => i2w(VT::S64, RVT::I32, false),
R::S64ToI32X => i2w(VT::S64, RVT::I32, true),
R::U64ToI32 => i2w(VT::U64, RVT::I32, false),
R::U64ToI32X => i2w(VT::U64, RVT::I32, true),
R::S8ToI64 => i2w(VT::S8, RVT::I64, false),
R::U8ToI64 => i2w(VT::U8, RVT::I64, false),
R::S16ToI64 => i2w(VT::S16, RVT::I64, false),
R::U16ToI64 => i2w(VT::U16, RVT::I64, false),
R::S32ToI64 => i2w(VT::S32, RVT::I64, false),
R::U32ToI64 => i2w(VT::U32, RVT::I64, false),
R::S64ToI64 => i2w(VT::S64, RVT::I64, false),
R::U64ToI64 => i2w(VT::U64, RVT::I64, false),
R::End => continue,
});
}
match &mut self.funcs.arena.get_mut(id).unwrap().kind {
FuncKind::Local(i) => *i = list,
_ => unreachable!(),
}
}
Ok(())
}
pub(crate) fn encode_funcs(
&self,
writer: &mut wit_writer::Writer,
wids: &mut WitIdsToIndices,
ids: &walrus::IdsToIndices,
) {
let funcs = self
.funcs
.iter()
.filter_map(|f| match &f.kind {
FuncKind::Local(instrs) => Some((f, instrs)),
FuncKind::Import(_) => None,
})
.collect::<Vec<_>>();
for (func, _) in funcs.iter() {
wids.push_func(func.id());
}
let mut w = writer.funcs(funcs.len() as u32);
for (func, instrs) in funcs {
let mut w = w.add(wids.ty(func.ty));
for instr in instrs {
use Instruction::*;
match *instr {
ArgGet(n) => w.arg_get(n),
CallCore(f) => w.call_core(ids.get_func_index(f)),
DeferCallCore(f) => w.defer_call_core(ids.get_func_index(f)),
CallAdapter(f) => w.call_adapter(wids.func(f)),
MemoryToString(m) => w.memory_to_string(ids.get_memory_index(m)),
StringToMemory { mem, malloc } => {
w.string_to_memory(ids.get_func_index(malloc), ids.get_memory_index(mem));
}
IntToWasm {
input,
output,
trap,
} => i2w(&mut w, input, output, trap),
WasmToInt {
input,
output,
trap,
} => w2i(&mut w, input, output, trap),
}
}
}
fn w2i(
w: &mut wit_writer::Instructions<'_, '_>,
input: walrus::ValType,
output: ValType,
trap: bool,
) {
match (input, output, trap) {
(walrus::ValType::I32, ValType::S8, false) => w.i32_to_s8(),
(walrus::ValType::I32, ValType::S8, true) => w.i32_to_s8x(),
(walrus::ValType::I32, ValType::U8, _) => w.i32_to_u8(),
(walrus::ValType::I32, ValType::S16, false) => w.i32_to_s16(),
(walrus::ValType::I32, ValType::S16, true) => w.i32_to_s16x(),
(walrus::ValType::I32, ValType::U16, _) => w.i32_to_u16(),
(walrus::ValType::I32, ValType::S32, _) => w.i32_to_s32(),
(walrus::ValType::I32, ValType::U32, _) => w.i32_to_u32(),
(walrus::ValType::I32, ValType::S64, _) => w.i32_to_s64(),
(walrus::ValType::I32, ValType::U64, _) => w.i32_to_u64(),
(walrus::ValType::I64, ValType::S8, false) => w.i64_to_s8(),
(walrus::ValType::I64, ValType::S8, true) => w.i64_to_s8x(),
(walrus::ValType::I64, ValType::U8, _) => w.i64_to_u8(),
(walrus::ValType::I64, ValType::S16, false) => w.i64_to_s16(),
(walrus::ValType::I64, ValType::S16, true) => w.i64_to_s16x(),
(walrus::ValType::I64, ValType::U16, _) => w.i64_to_u16(),
(walrus::ValType::I64, ValType::S32, false) => w.i64_to_s32(),
(walrus::ValType::I64, ValType::S32, true) => w.i64_to_s32x(),
(walrus::ValType::I64, ValType::U32, _) => w.i64_to_u32(),
(walrus::ValType::I64, ValType::S64, _) => w.i64_to_s64(),
(walrus::ValType::I64, ValType::U64, _) => w.i64_to_u64(),
_ => unreachable!(),
}
}
fn i2w(
w: &mut wit_writer::Instructions<'_, '_>,
input: ValType,
output: walrus::ValType,
trap: bool,
) {
match (input, output, trap) {
(ValType::S8, walrus::ValType::I32, _) => w.s8_to_i32(),
(ValType::U8, walrus::ValType::I32, _) => w.u8_to_i32(),
(ValType::S16, walrus::ValType::I32, _) => w.s16_to_i32(),
(ValType::U16, walrus::ValType::I32, _) => w.u16_to_i32(),
(ValType::S32, walrus::ValType::I32, _) => w.s32_to_i32(),
(ValType::U32, walrus::ValType::I32, _) => w.u32_to_i32(),
(ValType::S64, walrus::ValType::I32, false) => w.s64_to_i32(),
(ValType::S64, walrus::ValType::I32, true) => w.s64_to_i32x(),
(ValType::U64, walrus::ValType::I32, false) => w.u64_to_i32(),
(ValType::U64, walrus::ValType::I32, true) => w.u64_to_i32x(),
(ValType::S8, walrus::ValType::I64, _) => w.s8_to_i64(),
(ValType::U8, walrus::ValType::I64, _) => w.u8_to_i64(),
(ValType::S16, walrus::ValType::I64, _) => w.s16_to_i64(),
(ValType::U16, walrus::ValType::I64, _) => w.u16_to_i64(),
(ValType::S32, walrus::ValType::I64, _) => w.s32_to_i64(),
(ValType::U32, walrus::ValType::I64, _) => w.u32_to_i64(),
(ValType::S64, walrus::ValType::I64, _) => w.s64_to_i64(),
(ValType::U64, walrus::ValType::I64, _) => w.u64_to_i64(),
_ => unreachable!(),
}
}
}
}
impl Funcs {
pub fn get(&self, id: FuncId) -> &Func {
&self.arena[id]
}
pub fn get_mut(&mut self, id: FuncId) -> &mut Func {
&mut self.arena[id]
}
pub fn iter(&self) -> impl Iterator<Item = &Func> {
self.arena.iter().map(|(_, f)| f)
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Func> {
self.arena.iter_mut().map(|(_, f)| f)
}
pub fn add_import(&mut self, ty: TypeId, import: ImportId) -> FuncId {
self.arena.alloc_with_id(|id| Func {
id,
ty,
kind: FuncKind::Import(import),
})
}
pub fn add_local(&mut self, ty: TypeId, instrs: Vec<Instruction>) -> FuncId {
self.arena.alloc_with_id(|id| Func {
id,
ty,
kind: FuncKind::Local(instrs),
})
}
}
impl Func {
pub fn id(&self) -> FuncId {
self.id
}
}