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) fn compile_wat_helper(
wat_source: &str,
) -> Result<Function, WasmGcError> {
let module_bytes = wat::parse_str(wat_source)
.map_err(|e| WasmGcError::Validation(format!("wat parse: {e}")))?;
let mut found_locals: Option<Vec<ValType>> = None;
let mut found_body: Option<Vec<u8>> = 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::CodeSectionEntry(body) = payload {
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);
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,
}))
}
}
}