use cviz::model::{FuncSignature, InterfaceType, ValueTypeId};
use wasm_encoder::ValType;
use super::abi::WitBridge;
use super::build::MemoryLayoutBuilder;
pub(crate) struct AdapterFunc {
pub name: String,
pub is_async: bool,
pub param_names: Vec<String>,
pub param_type_ids: Vec<ValueTypeId>,
pub result_type_id: Option<ValueTypeId>,
pub result_is_complex: bool,
pub core_params: Vec<ValType>,
pub core_results: Vec<ValType>,
pub name_offset: u32,
pub name_len: u32,
pub async_result_mem_offset: Option<u32>,
pub sync_result_mem_offset: Option<u32>,
pub has_strings: bool,
pub has_lists: bool,
}
impl AdapterFunc {
pub fn canon_needs_memory(&self) -> bool {
self.has_strings
|| self.has_lists
|| self.result_is_complex
|| (self.is_async && self.result_type_id.is_some())
}
pub fn canon_needs_realloc(&self) -> bool {
self.has_strings || self.has_lists
}
pub fn canon_needs_utf8(&self) -> bool {
self.has_strings
}
}
pub(crate) fn extract_adapter_funcs(
iface_ty: &InterfaceType,
bridge: &WitBridge,
) -> anyhow::Result<(Vec<AdapterFunc>, MemoryLayoutBuilder)> {
let inst = match iface_ty {
InterfaceType::Instance(i) => i,
InterfaceType::Func(_) => anyhow::bail!(
"Expected an instance-type interface for tier-1 adapter generation; \
bare function-type interfaces are not yet supported. If you need this, \
please open an issue with a repro at https://github.com/ejrgilbert/splicer/issues"
),
};
let total_name_bytes: u32 = inst.functions.keys().map(|n| n.len() as u32).sum();
let mut layout = MemoryLayoutBuilder::new(total_name_bytes);
let mut funcs = Vec::with_capacity(inst.functions.len());
for (name, sig) in &inst.functions {
let extracted = extract_func_sig(name, sig, bridge)?;
let name_len = name.len() as u32;
let name_offset = layout.alloc_name(name_len);
let has_result = extracted.result_type_id.is_some();
let async_result_mem_offset = (extracted.is_async && has_result)
.then(|| layout.alloc_async_result(extracted.result_byte_size));
let sync_result_mem_offset = (!extracted.is_async && extracted.result_is_complex)
.then(|| layout.alloc_sync_result(extracted.result_byte_size));
let param_ids = extracted.param_type_ids.iter().copied();
let result_id = extracted.result_type_id.into_iter();
let all_ids: Vec<ValueTypeId> = param_ids.chain(result_id).collect();
let has_strings = all_ids.iter().any(|&id| bridge.has_strings(id));
let has_lists = all_ids.iter().any(|&id| bridge.has_lists(id));
funcs.push(AdapterFunc {
name: name.clone(),
is_async: extracted.is_async,
param_names: extracted.param_names,
param_type_ids: extracted.param_type_ids,
result_type_id: extracted.result_type_id,
result_is_complex: extracted.result_is_complex,
core_params: extracted.core_params,
core_results: extracted.core_results,
name_offset,
name_len,
async_result_mem_offset,
sync_result_mem_offset,
has_strings,
has_lists,
});
}
Ok((funcs, layout))
}
struct ExtractedSig {
is_async: bool,
param_names: Vec<String>,
param_type_ids: Vec<ValueTypeId>,
result_type_id: Option<ValueTypeId>,
result_is_complex: bool,
core_params: Vec<ValType>,
core_results: Vec<ValType>,
result_byte_size: u32,
}
fn extract_func_sig(
name: &str,
sig: &FuncSignature,
bridge: &WitBridge,
) -> anyhow::Result<ExtractedSig> {
const MAX_FLAT: usize = 16;
let mut param_names = Vec::with_capacity(sig.params.len());
let mut param_type_ids = Vec::with_capacity(sig.params.len());
let mut core_params = Vec::new();
for (i, &id) in sig.params.iter().enumerate() {
let pname = if i < sig.param_names.len() {
sig.param_names[i].clone()
} else {
format!("p{i}")
};
param_names.push(pname);
param_type_ids.push(id);
core_params.extend(bridge.flat_types(id));
}
if core_params.len() > MAX_FLAT {
anyhow::bail!(
"Function '{name}' has {} flat parameter values (exceeds the \
canonical-ABI limit of {MAX_FLAT}). The pointer-form lowering \
required for >{MAX_FLAT} flat params is not yet implemented.",
core_params.len()
);
}
if sig.results.len() > 1 {
anyhow::bail!(
"Function '{}' has {} results; only 0 or 1 results are supported \
for tier-1 adapter generation. If you need multi-result support, \
please open an issue with a repro at https://github.com/ejrgilbert/splicer/issues",
name,
sig.results.len()
);
}
let (result_type_id, result_is_complex, core_results, result_byte_size) =
if sig.results.is_empty() {
(None, false, vec![], 0)
} else {
let rid = sig.results[0];
let flat = bridge.flat_types(rid);
if flat.len() > MAX_FLAT {
anyhow::bail!(
"Function '{name}' has a result that flattens to {} core \
values (exceeds {MAX_FLAT}). The pointer-form lowering \
required for >{MAX_FLAT} flat results is not yet \
implemented.",
flat.len()
);
}
let is_complex = flat.len() > 1;
let total_bytes = bridge.size_bytes(rid);
(Some(rid), is_complex, flat, total_bytes)
};
Ok(ExtractedSig {
is_async: sig.is_async,
param_names,
param_type_ids,
result_type_id,
result_is_complex,
core_params,
core_results,
result_byte_size,
})
}