use wasm_encoder::{Function, Instruction, ValType};
use super::super::WasmGcError;
use super::super::types::{TypeRegistry, aver_to_wasm};
use super::EmitCtx;
pub(super) fn emit_default_value(
func: &mut Function,
aver_ty: &str,
registry: &TypeRegistry,
) -> Result<(), WasmGcError> {
match aver_ty.trim() {
"Int" => {
func.instruction(&Instruction::I64Const(0));
Ok(())
}
"Float" => {
func.instruction(&Instruction::F64Const(0.0_f64.into()));
Ok(())
}
"Bool" => {
func.instruction(&Instruction::I32Const(0));
Ok(())
}
"Unit" => {
func.instruction(&Instruction::I32Const(0));
Ok(())
}
other => {
let val = aver_to_wasm(other, Some(registry))?;
match val {
Some(ValType::Ref(rt)) => {
func.instruction(&Instruction::RefNull(rt.heap_type));
Ok(())
}
Some(ValType::I64) => {
func.instruction(&Instruction::I64Const(0));
Ok(())
}
Some(ValType::F64) => {
func.instruction(&Instruction::F64Const(0.0_f64.into()));
Ok(())
}
Some(ValType::I32) => {
func.instruction(&Instruction::I32Const(0));
Ok(())
}
Some(other_ty) => Err(WasmGcError::Validation(format!(
"Option.None default for `{other}` resolved to {other_ty:?} — no default emitter for that wasm type"
))),
None => Err(WasmGcError::Validation(format!(
"Option.None over `{other}` has no wasm representation"
))),
}
}
}
}
pub(super) fn sum_or_record_eq_fn(ty: &crate::types::Type, ctx: &EmitCtx<'_>) -> Option<u32> {
match ty {
crate::types::Type::Named { id, name } => {
let key: String = match id {
Some(type_id) => ctx.symbol_table.type_entry(*type_id).key.canonical(),
None => name.clone(),
};
if ctx.registry.newtype_underlying(&key).is_some() {
return None;
}
let is_record = ctx.registry.record_fields.contains_key(&key);
let is_sum = ctx
.registry
.variants
.values()
.flat_map(|v| v.iter())
.any(|v| v.parent == key);
if !is_record && !is_sum {
return None;
}
ctx.fn_map.eq_helpers.get(&key).copied()
}
crate::types::Type::Option(_)
| crate::types::Type::Result(_, _)
| crate::types::Type::Tuple(_) => {
let canonical: String = ty
.display()
.chars()
.filter(|c| !c.is_whitespace())
.collect();
ctx.fn_map.eq_helpers.get(&canonical).copied()
}
crate::types::Type::List(_) => {
let canonical: String = ty
.display()
.chars()
.filter(|c| !c.is_whitespace())
.collect();
ctx.fn_map.list_ops.get(&canonical).and_then(|ops| ops.eq)
}
crate::types::Type::Vector(_) => {
let canonical: String = ty
.display()
.chars()
.filter(|c| !c.is_whitespace())
.collect();
ctx.fn_map.vfl_ops.get(&canonical).and_then(|ops| ops.eq)
}
crate::types::Type::Map(_, _) => {
let canonical: String = ty
.display()
.chars()
.filter(|c| !c.is_whitespace())
.collect();
ctx.fn_map.eq_helpers.get(&canonical).copied()
}
_ => None,
}
}
pub(super) fn emit_group_call(func: &mut Function, ctx: &EmitCtx<'_>, op: &str) {
if let Some(idx) = ctx.effect_idx_lookup.get(op) {
if emit_caller_fn_idx(func, ctx).is_err() {
return;
}
func.instruction(&Instruction::Call(*idx));
}
}
pub(super) fn emit_branch_marker(func: &mut Function, ctx: &EmitCtx<'_>, branch_idx: u32) {
if let Some(idx) = ctx.effect_idx_lookup.get("__record_set_branch") {
func.instruction(&Instruction::I64Const(branch_idx as i64));
if emit_caller_fn_idx(func, ctx).is_err() {
return;
}
func.instruction(&Instruction::Call(*idx));
}
}
pub(super) fn emit_caller_fn_idx(
func: &mut Function,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let idx = ctx
.caller_fn_collector
.borrow_mut()
.register(ctx.self_fn_name);
func.instruction(&Instruction::I32Const(idx as i32));
Ok(())
}
pub(super) fn emit_string_literal_bytes(
func: &mut Function,
bytes: &[u8],
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let segment_idx = ctx
.registry
.string_literal_segment(bytes)
.ok_or(WasmGcError::Validation(format!(
"String literal `{:?}` was not registered in the data segment table",
std::str::from_utf8(bytes).unwrap_or("<non-utf8>")
)))?;
let s_idx = ctx
.registry
.string_array_type_idx
.ok_or(WasmGcError::Validation(
"String literal needs string slot allocated".into(),
))?;
func.instruction(&Instruction::I32Const(0)); func.instruction(&Instruction::I32Const(bytes.len() as i32));
func.instruction(&Instruction::ArrayNewData {
array_type_index: s_idx,
array_data_index: segment_idx,
});
Ok(())
}
pub(super) fn emit_return_call_insn(func: &mut Function, target_wasm_idx: u32, self_wasm_idx: u32) {
let target_idx = if target_wasm_idx == self_wasm_idx {
self_wasm_idx
} else {
target_wasm_idx
};
if std::env::var_os("AVER_WASM_GC_NO_TAIL_CALL").is_some() {
func.instruction(&Instruction::Call(target_idx));
} else {
func.instruction(&Instruction::ReturnCall(target_idx));
}
}