use super::*;
pub(crate) fn emit_mir_interpolated_str(
func: &mut Function,
parts: &[MirStrPart],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<Option<bool>, WasmGcError> {
let string_type_idx = ctx
.registry
.string_array_type_idx
.ok_or(WasmGcError::Validation(
"InterpolatedStr reachable but no String type slot allocated".into(),
))?;
if parts.is_empty() {
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::ArrayNewDefault(string_type_idx));
return Ok(Some(true));
}
let vec_idx = ctx
.registry
.vector_type_idx("Vector<String>")
.ok_or(WasmGcError::Validation(
"InterpolatedStr requires Vector<String> slot but it wasn't registered".into(),
))?;
let concat_idx = ctx
.fn_map
.builtins
.get("__wasmgc_concat_n")
.copied()
.ok_or(WasmGcError::Validation(
"InterpolatedStr requires __wasmgc_concat_n builtin but it wasn't registered".into(),
))?;
for part in parts {
match part {
MirStrPart::Literal(s) => {
let bytes = s.as_bytes();
let seg_idx =
ctx.registry
.string_literal_segment(bytes)
.ok_or(WasmGcError::Validation(format!(
"Interpolation literal `{s:?}` not in segment table"
)))?;
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I32Const(bytes.len() as i32));
func.instruction(&Instruction::ArrayNewData {
array_type_index: string_type_idx,
array_data_index: seg_idx,
});
}
MirStrPart::Expr(inner) => {
let aver_ty = aver_type_str_of(inner);
if aver_ty.trim() == "Int" {
match emit_mir_int_stringify(func, inner, slots, ctx)? {
Some(()) => continue,
None => return Ok(None),
}
}
if emit_mir_expr(func, inner, slots, ctx)?.is_none() {
return Ok(None);
}
match aver_ty.trim() {
"String" => { }
"Float" => {
let to_string_idx =
ctx.fn_map.builtins.get("String.fromFloat").copied().ok_or(
WasmGcError::Validation(
"interpolation of Float requires String.fromFloat builtin"
.into(),
),
)?;
func.instruction(&Instruction::Call(to_string_idx));
}
"Bool" => {
let to_string_idx =
ctx.fn_map.builtins.get("String.fromBool").copied().ok_or(
WasmGcError::Validation(
"interpolation of Bool requires String.fromBool builtin".into(),
),
)?;
func.instruction(&Instruction::Call(to_string_idx));
}
_ => return Ok(None),
}
}
}
}
func.instruction(&Instruction::ArrayNewFixed {
array_type_index: vec_idx,
array_size: parts.len() as u32,
});
func.instruction(&Instruction::Call(concat_idx));
Ok(Some(true))
}
pub(crate) fn emit_mir_string_binop(
func: &mut Function,
bop: &crate::ir::mir::MirBinOp,
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<Option<()>, WasmGcError> {
let l = &bop.lhs;
let r = &bop.rhs;
macro_rules! e {
($x:expr) => {
if emit_mir_expr(func, $x, slots, ctx)?.is_none() {
return Ok(None);
}
};
}
match bop.op {
BinOp::Add => {
let vec_idx =
ctx.registry
.vector_type_idx("Vector<String>")
.ok_or(WasmGcError::Validation(
"String `+` requires Vector<String> slot but it wasn't registered".into(),
))?;
let concat_idx = ctx
.fn_map
.builtins
.get("__wasmgc_concat_n")
.copied()
.ok_or(WasmGcError::Validation(
"String `+` requires __wasmgc_concat_n builtin but it wasn't registered".into(),
))?;
e!(l);
e!(r);
func.instruction(&Instruction::ArrayNewFixed {
array_type_index: vec_idx,
array_size: 2,
});
func.instruction(&Instruction::Call(concat_idx));
}
BinOp::Eq | BinOp::Neq => {
let eq_idx = ctx
.fn_map
.builtins
.get("__wasmgc_string_eq")
.copied()
.ok_or(WasmGcError::Validation(
"String `==`/`!=` requires __wasmgc_string_eq builtin but it wasn't registered"
.into(),
))?;
e!(l);
e!(r);
func.instruction(&Instruction::Call(eq_idx));
if matches!(bop.op, BinOp::Neq) {
func.instruction(&Instruction::I32Eqz);
}
}
BinOp::Lt | BinOp::Gt | BinOp::Lte | BinOp::Gte => {
let cmp_idx = ctx
.fn_map
.builtins
.get("__wasmgc_string_compare")
.copied()
.ok_or(WasmGcError::Validation(
"String comparison requires __wasmgc_string_compare builtin".into(),
))?;
e!(l);
e!(r);
func.instruction(&Instruction::Call(cmp_idx));
func.instruction(&Instruction::I32Const(0));
let post = match bop.op {
BinOp::Lt => Instruction::I32LtS,
BinOp::Gt => Instruction::I32GtS,
BinOp::Lte => Instruction::I32LeS,
BinOp::Gte => Instruction::I32GeS,
_ => unreachable!("outer match restricts op"),
};
func.instruction(&post);
}
_ => return Ok(None),
}
Ok(Some(()))
}