use wasm_encoder::{Function, ValType};
use wasmparser::{Parser, Payload};
use super::WasmGcError;
pub(in crate::codegen::wasm_gc) fn padding_types(n: u32) -> String {
let mut s = String::with_capacity(n as usize * 16);
for _ in 0..n {
s.push_str("(type (struct))\n");
}
s
}
pub(in crate::codegen::wasm_gc) struct CalleeStub {
pub abs_idx: u32,
pub sig: &'static str,
}
pub(in crate::codegen::wasm_gc) fn func_placeholders(callees: &[CalleeStub]) -> String {
let max_idx = match callees.iter().map(|c| c.abs_idx).max() {
Some(m) => m,
None => return String::new(),
};
let mut sig_at: std::collections::HashMap<u32, &'static str> = std::collections::HashMap::new();
for c in callees {
sig_at.insert(c.abs_idx, c.sig);
}
let mut s = String::with_capacity((max_idx as usize + 1) * 24);
for idx in 0..=max_idx {
match sig_at.get(&idx) {
Some(sig) => s.push_str(&format!("(func {sig} unreachable)\n")),
None => s.push_str("(func unreachable)\n"),
}
}
s
}
pub(in crate::codegen::wasm_gc) fn compile_wat_helper(
wat_source: &str,
) -> Result<Function, WasmGcError> {
let module_bytes = wat::parse_str(wat_source).map_err(|e| {
if std::env::var_os("AVER_WASMGC_DUMP_WAT").is_some() {
eprintln!("--- WAT parse failed ---\n{wat_source}\n--- end WAT ---");
}
WasmGcError::Validation(format!("wat parse: {e}"))
})?;
let mut helper_fn_idx: Option<u32> = None;
for payload in Parser::new(0).parse_all(&module_bytes) {
let payload =
payload.map_err(|e| WasmGcError::Validation(format!("wasm parse helper: {e}")))?;
if let Payload::ExportSection(reader) = payload {
for export in reader {
let export =
export.map_err(|e| WasmGcError::Validation(format!("export read: {e}")))?;
if export.name == "helper" && export.kind == wasmparser::ExternalKind::Func {
helper_fn_idx = Some(export.index);
}
}
}
}
let mut found_locals: Option<Vec<ValType>> = None;
let mut found_body: Option<Vec<u8>> = None;
let mut code_fn_idx: u32 = 0;
for payload in Parser::new(0).parse_all(&module_bytes) {
let payload =
payload.map_err(|e| WasmGcError::Validation(format!("wasm parse helper: {e}")))?;
if let Payload::CodeSectionEntry(body) = payload {
let this_idx = code_fn_idx;
code_fn_idx += 1;
let want = match helper_fn_idx {
Some(idx) => this_idx == idx,
None => found_body.is_none(),
};
if !want {
continue;
}
let mut locals: Vec<ValType> = Vec::new();
let mut locals_reader = body
.get_locals_reader()
.map_err(|e| WasmGcError::Validation(format!("locals reader: {e}")))?;
let count = locals_reader.get_count();
for _ in 0..count {
let (n, ty) = locals_reader
.read()
.map_err(|e| WasmGcError::Validation(format!("locals read: {e}")))?;
let val_ty = wasmparser_to_encoder_valtype(ty)?;
for _ in 0..n {
locals.push(val_ty);
}
}
let ops_reader = body
.get_operators_reader()
.map_err(|e| WasmGcError::Validation(format!("ops reader: {e}")))?;
let expr_start = ops_reader.original_position();
let body_range = body.range();
let expr_bytes = module_bytes[expr_start..body_range.end].to_vec();
found_locals = Some(locals);
found_body = Some(expr_bytes);
if helper_fn_idx.is_some() {
break;
}
}
}
let locals = found_locals.ok_or(WasmGcError::Validation(
"wat helper has no function body".into(),
))?;
let body = found_body.ok_or(WasmGcError::Validation(
"wat helper missing body bytes".into(),
))?;
let local_groups: Vec<(u32, ValType)> = compress_locals(&locals);
let mut func = Function::new(local_groups);
func.raw(body);
Ok(func)
}
fn compress_locals(locals: &[ValType]) -> Vec<(u32, ValType)> {
let mut out: Vec<(u32, ValType)> = Vec::new();
for ty in locals {
if let Some(last) = out.last_mut()
&& last.1 == *ty
{
last.0 += 1;
} else {
out.push((1, *ty));
}
}
out
}
fn wasmparser_to_encoder_valtype(ty: wasmparser::ValType) -> Result<ValType, WasmGcError> {
use wasmparser::ValType as PT;
match ty {
PT::I32 => Ok(ValType::I32),
PT::I64 => Ok(ValType::I64),
PT::F32 => Ok(ValType::F32),
PT::F64 => Ok(ValType::F64),
PT::V128 => Ok(ValType::V128),
PT::Ref(rt) => {
let nullable = rt.is_nullable();
let heap_type = match rt.heap_type() {
wasmparser::HeapType::Abstract { shared, ty } => {
use wasm_encoder::AbstractHeapType as E;
use wasmparser::AbstractHeapType as P;
let mapped = match ty {
P::Func => E::Func,
P::Extern => E::Extern,
P::Any => E::Any,
P::None => E::None,
P::NoExtern => E::NoExtern,
P::NoFunc => E::NoFunc,
P::Eq => E::Eq,
P::Struct => E::Struct,
P::Array => E::Array,
P::I31 => E::I31,
P::Exn => E::Exn,
P::NoExn => E::NoExn,
P::Cont => E::Cont,
P::NoCont => E::NoCont,
};
wasm_encoder::HeapType::Abstract { shared, ty: mapped }
}
wasmparser::HeapType::Concrete(idx) => match idx {
wasmparser::UnpackedIndex::Module(i) => wasm_encoder::HeapType::Concrete(i),
other => {
return Err(WasmGcError::Validation(format!(
"wat helper local has non-module concrete heap idx: {other:?}"
)));
}
},
wasmparser::HeapType::Exact(idx) => {
return Err(WasmGcError::Validation(format!(
"wat helper local uses exact-typed heap ref `{idx:?}` — not supported \
(wasm-encoder HeapType has no Exact variant; use Concrete)"
)));
}
};
Ok(ValType::Ref(wasm_encoder::RefType {
nullable,
heap_type,
}))
}
}
}