use wasm_encoder::Instruction;
use crate::ast::Spanned;
use crate::ir::mir::MirExpr;
use super::super::WasmGcError;
use super::from_mir::emit_mir_expr;
use super::{EmitCtx, SlotTable};
fn emit_aint_arg_as_i64_wasip2(
func: &mut wasm_encoder::Function,
arg: &Spanned<MirExpr>,
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
emit_mir_expr(func, arg, slots, ctx)?;
if ctx.registry.bignum {
let to_checked = ctx
.fn_map
.builtins
.get("__aint_to_i64_checked")
.copied()
.ok_or(WasmGcError::Validation(
"bignum active but __aint_to_i64_checked helper not registered".into(),
))?;
func.instruction(&Instruction::Call(to_checked));
}
Ok(())
}
fn lift_i64_result_to_aint_wasip2(
func: &mut wasm_encoder::Function,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
if ctx.registry.bignum {
let from_i64 =
ctx.fn_map
.builtins
.get("__aint_from_i64")
.copied()
.ok_or(WasmGcError::Validation(
"bignum active but __aint_from_i64 helper not registered".into(),
))?;
func.instruction(&Instruction::Call(from_i64));
}
Ok(())
}
pub(super) fn emit_console_print_wasip2(
func: &mut wasm_encoder::Function,
method: &str,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation(
"emit_console_print_wasip2 invoked without wasip2 lowering ctx".into(),
)
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Console.{method} on `--target wasip2` expects 1 arg (the String), got {}",
args.len()
)));
}
let scratch = slots.console_print_wasip2_scratch.ok_or_else(|| {
WasmGcError::Validation(
"Console.* on wasip2: [len, offset] scratch slots were not allocated by \
SlotTable — `fn_needs_console_print_wasip2_scratch` did not flag this fn"
.into(),
)
})?;
let len_local = scratch[0];
let off_local = scratch[1];
let (handle_global, get_fn) = match method {
"print" => (
lowering.stdout_handle_global.ok_or_else(|| {
WasmGcError::Validation(
"Console.print on wasip2: stdout_handle global missing — \
wasip2_imports did not register CliGetStdout"
.into(),
)
})?,
lowering.get_stdout_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Console.print on wasip2: get_stdout fn idx missing".into())
})?,
),
"error" | "warn" => (
lowering.stderr_handle_global.ok_or_else(|| {
WasmGcError::Validation(
"Console.error/warn on wasip2: stderr_handle global missing".into(),
)
})?,
lowering.get_stderr_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Console.error/warn on wasip2: get_stderr fn idx missing".into(),
)
})?,
),
_ => {
return Err(WasmGcError::Validation(format!(
"Console.{method} is not lowered on `--target wasip2`"
)));
}
};
func.instruction(&Instruction::GlobalGet(handle_global));
func.instruction(&Instruction::I32Const(-1));
func.instruction(&Instruction::I32Eq);
func.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
func.instruction(&Instruction::Call(get_fn));
func.instruction(&Instruction::GlobalSet(handle_global));
func.instruction(&Instruction::End);
let println_to_lm = lowering.println_to_lm_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Console.* on wasip2: __rt_println_to_lm fn idx missing — bridge not allocated".into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::Call(println_to_lm));
func.instruction(&Instruction::LocalSet(len_local));
func.instruction(&Instruction::I32Const(1));
func.instruction(&Instruction::MemoryGrow(0));
func.instruction(&Instruction::Drop);
let write_fn = lowering.blocking_write_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Console.* on wasip2: blocking-write-and-flush fn idx missing".into(),
)
})?;
super::super::wasip2_helpers::emit_chunked_blocking_write(
func,
len_local,
off_local,
write_fn,
&|f| {
f.instruction(&Instruction::GlobalGet(handle_global));
},
&|f| {
f.instruction(&Instruction::LocalGet(len_local));
f.instruction(&Instruction::I32Const(15));
f.instruction(&Instruction::I32Add);
f.instruction(&Instruction::I32Const(-16));
f.instruction(&Instruction::I32And);
},
None,
);
Ok(())
}
pub(super) fn emit_args_get_wasip2(
func: &mut wasm_encoder::Function,
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Args.get on wasip2: lowering ctx missing".into())
})?;
let cabi_realloc = lowering.cabi_realloc_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Args.get on wasip2: cabi_realloc fn idx missing — wasip2_imports must register \
at least one slot for cabi_realloc to be allocated"
.into(),
)
})?;
let get_arguments = lowering.get_arguments_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Args.get on wasip2: wasi:cli/environment.get-arguments fn idx missing".into(),
)
})?;
let decoder = lowering.decode_list_string_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Args.get on wasip2: __rt_canonical_decode_list_string fn idx missing".into(),
)
})?;
let retptr_local = slots.args_get_wasip2_retptr_scratch.ok_or_else(|| {
WasmGcError::Validation(
"Args.get on wasip2: i32 retptr scratch slot missing — \
SlotTable should have allocated via fn_needs_args_get_scratch"
.into(),
)
})?;
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I32Const(4));
func.instruction(&Instruction::I32Const(8));
func.instruction(&Instruction::Call(cabi_realloc));
func.instruction(&Instruction::LocalSet(retptr_local));
func.instruction(&Instruction::LocalGet(retptr_local));
func.instruction(&Instruction::Call(get_arguments));
func.instruction(&Instruction::LocalGet(retptr_local));
func.instruction(&Instruction::Call(decoder));
Ok(())
}
pub(super) fn emit_env_get_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx
.wasip2_lowering
.ok_or_else(|| WasmGcError::Validation("Env.get on wasip2: lowering ctx missing".into()))?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Env.get on `--target wasip2` expects 1 arg (the key String), got {}",
args.len()
)));
}
let str_to_lm = lowering.str_to_lm_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Env.get on wasip2: __rt_string_to_lm fn idx missing — bridge not allocated".into(),
)
})?;
let cabi_realloc = lowering.cabi_realloc_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Env.get on wasip2: cabi_realloc fn idx missing".into())
})?;
let get_environment = lowering.get_environment_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Env.get on wasip2: wasi:cli/environment.get-environment fn idx missing".into(),
)
})?;
let lookup = lowering.env_get_lookup_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Env.get on wasip2: __rt_canonical_env_lookup fn idx missing".into(),
)
})?;
let scratch = slots.env_get_wasip2_scratch.ok_or_else(|| {
WasmGcError::Validation(
"Env.get on wasip2: [retptr, key_len] scratch pair missing — \
SlotTable should have allocated via fn_needs_env_get_wasip2_scratch"
.into(),
)
})?;
let retptr_local = scratch[0];
let key_len_local = scratch[1];
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::Call(str_to_lm));
func.instruction(&Instruction::LocalSet(key_len_local));
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I32Const(4));
func.instruction(&Instruction::I32Const(8));
func.instruction(&Instruction::Call(cabi_realloc));
func.instruction(&Instruction::LocalSet(retptr_local));
func.instruction(&Instruction::LocalGet(retptr_local));
func.instruction(&Instruction::Call(get_environment));
func.instruction(&Instruction::LocalGet(retptr_local));
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::LocalGet(key_len_local));
func.instruction(&Instruction::Call(lookup));
Ok(())
}
pub(super) fn emit_time_unix_ms_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Time.unixMs on wasip2: lowering ctx missing".into())
})?;
if !args.is_empty() {
return Err(WasmGcError::Validation(format!(
"Time.unixMs on `--target wasip2` expects 0 args, got {}",
args.len()
)));
}
let now_fn = lowering.clocks_now_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Time.unixMs on wasip2: clocks-now fn idx missing".into())
})?;
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::Call(now_fn));
let mem_arg = wasm_encoder::MemArg {
offset: 0,
align: 3, memory_index: 0,
};
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I64Load(mem_arg));
func.instruction(&Instruction::I64Const(1000));
func.instruction(&Instruction::I64Mul);
let ns_mem_arg = wasm_encoder::MemArg {
offset: 8,
align: 2, memory_index: 0,
};
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I32Load(ns_mem_arg));
func.instruction(&Instruction::I64ExtendI32U);
func.instruction(&Instruction::I64Const(1_000_000));
func.instruction(&Instruction::I64DivU);
func.instruction(&Instruction::I64Add);
lift_i64_result_to_aint_wasip2(func, ctx)?;
Ok(())
}
pub(super) fn emit_time_now_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Time.now on wasip2: lowering ctx missing".into())
})?;
if !args.is_empty() {
return Err(WasmGcError::Validation(format!(
"Time.now on `--target wasip2` expects 0 args, got {}",
args.len()
)));
}
let now_fn = lowering.clocks_now_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Time.now on wasip2: clocks-now fn idx missing — slot not registered".into(),
)
})?;
let fmt_fn = lowering.fmt_iso8601_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Time.now on wasip2: __rt_format_iso8601 fn idx missing — helper not allocated".into(),
)
})?;
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::Call(now_fn));
let secs_mem = wasm_encoder::MemArg {
offset: 0,
align: 3, memory_index: 0,
};
let nanos_mem = wasm_encoder::MemArg {
offset: 8,
align: 2, memory_index: 0,
};
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I64Load(secs_mem));
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I32Load(nanos_mem));
func.instruction(&Instruction::Call(fmt_fn));
Ok(())
}
pub(super) fn emit_console_read_line_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Console.readLine on wasip2: lowering ctx missing".into())
})?;
if !args.is_empty() {
return Err(WasmGcError::Validation(format!(
"Console.readLine on `--target wasip2` expects 0 args, got {}",
args.len()
)));
}
let read_fn = lowering.console_read_line_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Console.readLine on wasip2: __rt_console_read_line fn idx missing — \
helper not allocated"
.into(),
)
})?;
func.instruction(&Instruction::Call(read_fn));
Ok(())
}
pub(super) fn emit_time_sleep_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Time.sleep on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Time.sleep on `--target wasip2` expects 1 arg (ms: Int), got {}",
args.len()
)));
}
let sleep_fn = lowering.time_sleep_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Time.sleep on wasip2: __rt_time_sleep fn idx missing — helper not allocated".into(),
)
})?;
emit_aint_arg_as_i64_wasip2(func, &args[0], slots, ctx)?; func.instruction(&Instruction::Call(sleep_fn));
Ok(())
}
pub(super) fn emit_disk_exists_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Disk.exists on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Disk.exists on `--target wasip2` expects 1 arg (path: String), got {}",
args.len()
)));
}
let exists_fn = lowering.disk_exists_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Disk.exists on wasip2: __rt_disk_exists fn idx missing — \
helper not allocated"
.into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?; func.instruction(&Instruction::Call(exists_fn));
Ok(())
}
pub(super) fn emit_tcp_ping_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Tcp.ping on wasip2: lowering ctx missing".into())
})?;
if args.len() != 2 {
return Err(WasmGcError::Validation(format!(
"Tcp.ping on `--target wasip2` expects 2 args (host, port), got {}",
args.len()
)));
}
let ping_fn = lowering.tcp_ping_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Tcp.ping on wasip2: __rt_tcp_ping fn idx missing".into())
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
emit_aint_arg_as_i64_wasip2(func, &args[1], slots, ctx)?;
func.instruction(&Instruction::Call(ping_fn));
Ok(())
}
pub(super) fn emit_tcp_send_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Tcp.send on wasip2: lowering ctx missing".into())
})?;
if args.len() != 3 {
return Err(WasmGcError::Validation(format!(
"Tcp.send on `--target wasip2` expects 3 args (host, port, data), got {}",
args.len()
)));
}
let send_fn = lowering.tcp_send_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Tcp.send on wasip2: __rt_tcp_send fn idx missing".into())
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
emit_aint_arg_as_i64_wasip2(func, &args[1], slots, ctx)?;
emit_mir_expr(func, &args[2], slots, ctx)?;
func.instruction(&Instruction::Call(send_fn));
Ok(())
}
pub(super) fn emit_tcp_read_line_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Tcp.readLine on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Tcp.readLine on `--target wasip2` expects 1 arg (conn), got {}",
args.len()
)));
}
let read_line_fn = lowering.tcp_read_line_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Tcp.readLine on wasip2: __rt_tcp_read_line fn idx missing".into())
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::Call(read_line_fn));
Ok(())
}
pub(super) fn emit_tcp_write_line_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Tcp.writeLine on wasip2: lowering ctx missing".into())
})?;
if args.len() != 2 {
return Err(WasmGcError::Validation(format!(
"Tcp.writeLine on `--target wasip2` expects 2 args (conn, line), got {}",
args.len()
)));
}
let write_line_fn = lowering.tcp_write_line_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Tcp.writeLine on wasip2: __rt_tcp_write_line fn idx missing".into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
emit_mir_expr(func, &args[1], slots, ctx)?;
func.instruction(&Instruction::Call(write_line_fn));
Ok(())
}
pub(super) fn emit_tcp_close_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Tcp.close on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Tcp.close on `--target wasip2` expects 1 arg (conn: Tcp.Connection), got {}",
args.len()
)));
}
let close_fn = lowering.tcp_close_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Tcp.close on wasip2: __rt_tcp_close fn idx missing — helper not allocated".into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::Call(close_fn));
Ok(())
}
pub(super) fn emit_tcp_connect_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Tcp.connect on wasip2: lowering ctx missing".into())
})?;
if args.len() != 2 {
return Err(WasmGcError::Validation(format!(
"Tcp.connect on `--target wasip2` expects 2 args (host: String, port: Int), got {}",
args.len()
)));
}
let connect_fn = lowering.tcp_connect_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Tcp.connect on wasip2: __rt_tcp_connect fn idx missing — \
helper not allocated (check Result<Tcp.Connection,String> + \
String slot + stub-error literal registration)"
.into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?; emit_aint_arg_as_i64_wasip2(func, &args[1], slots, ctx)?; func.instruction(&Instruction::Call(connect_fn));
Ok(())
}
pub(super) fn emit_disk_read_text_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Disk.readText on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Disk.readText on `--target wasip2` expects 1 arg (path: String), got {}",
args.len()
)));
}
let read_fn = lowering.disk_read_text_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Disk.readText on wasip2: __rt_disk_read_text fn idx missing — \
helper not allocated"
.into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?; func.instruction(&Instruction::Call(read_fn));
Ok(())
}
pub(super) fn emit_random_int_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Random.int on wasip2: lowering ctx missing".into())
})?;
if args.len() != 2 {
return Err(WasmGcError::Validation(format!(
"Random.int on `--target wasip2` expects 2 args (min, max), got {}",
args.len()
)));
}
let rand_fn = lowering.random_u64_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Random.int on wasip2: random get-random-u64 fn idx missing".into())
})?;
let min_scratch = slots.random_int_wasip2_min_scratch.ok_or_else(|| {
WasmGcError::Validation(
"Random.int on wasip2: i64 min scratch slot missing — \
SlotTable should have allocated via fn_needs_random_int_wasip2_scratch"
.into(),
)
})?;
emit_aint_arg_as_i64_wasip2(func, &args[0], slots, ctx)?; func.instruction(&Instruction::LocalSet(min_scratch));
func.instruction(&Instruction::LocalGet(min_scratch));
func.instruction(&Instruction::Call(rand_fn)); emit_aint_arg_as_i64_wasip2(func, &args[1], slots, ctx)?; func.instruction(&Instruction::LocalGet(min_scratch));
func.instruction(&Instruction::I64Sub);
func.instruction(&Instruction::I64Const(1));
func.instruction(&Instruction::I64Add);
func.instruction(&Instruction::I64RemU); func.instruction(&Instruction::I64Add);
lift_i64_result_to_aint_wasip2(func, ctx)?;
Ok(())
}
pub(super) fn emit_random_float_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Random.float on wasip2: lowering ctx missing".into())
})?;
if !args.is_empty() {
return Err(WasmGcError::Validation(format!(
"Random.float on `--target wasip2` expects 0 args, got {}",
args.len()
)));
}
let rand_fn = lowering.random_u64_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Random.float on wasip2: random get-random-u64 fn idx missing".into(),
)
})?;
func.instruction(&Instruction::Call(rand_fn));
func.instruction(&Instruction::I64Const(11));
func.instruction(&Instruction::I64ShrU);
func.instruction(&Instruction::F64ConvertI64U);
func.instruction(&Instruction::F64Const(
(1.0_f64 / (1u64 << 53) as f64).into(),
));
func.instruction(&Instruction::F64Mul);
Ok(())
}
pub(super) fn emit_disk_write_text_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Disk.writeText on wasip2: lowering ctx missing".into())
})?;
if args.len() != 2 {
return Err(WasmGcError::Validation(format!(
"Disk.writeText on `--target wasip2` expects 2 args (path, content), got {}",
args.len()
)));
}
let write_fn = lowering.disk_write_text_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Disk.writeText on wasip2: __rt_disk_write_text fn idx missing — \
helper not allocated"
.into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?; emit_mir_expr(func, &args[1], slots, ctx)?; func.instruction(&Instruction::Call(write_fn));
Ok(())
}
pub(super) fn emit_disk_delete_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Disk.delete on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Disk.delete on `--target wasip2` expects 1 arg (path), got {}",
args.len()
)));
}
let fn_idx = lowering.disk_delete_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Disk.delete on wasip2: __rt_disk_delete fn idx missing — helper not allocated".into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::Call(fn_idx));
Ok(())
}
pub(super) fn emit_disk_delete_dir_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Disk.deleteDir on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Disk.deleteDir on `--target wasip2` expects 1 arg (path), got {}",
args.len()
)));
}
let fn_idx = lowering.disk_delete_dir_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Disk.deleteDir on wasip2: __rt_disk_delete_dir fn idx missing".into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::Call(fn_idx));
Ok(())
}
pub(super) fn emit_disk_make_dir_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Disk.makeDir on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Disk.makeDir on `--target wasip2` expects 1 arg (path), got {}",
args.len()
)));
}
let fn_idx = lowering.disk_make_dir_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Disk.makeDir on wasip2: __rt_disk_make_dir fn idx missing".into())
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::Call(fn_idx));
Ok(())
}
pub(super) fn emit_disk_append_text_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Disk.appendText on wasip2: lowering ctx missing".into())
})?;
if args.len() != 2 {
return Err(WasmGcError::Validation(format!(
"Disk.appendText on `--target wasip2` expects 2 args (path, content), got {}",
args.len()
)));
}
let fn_idx = lowering.disk_append_text_fn_idx.ok_or_else(|| {
WasmGcError::Validation(
"Disk.appendText on wasip2: __rt_disk_append_text fn idx missing".into(),
)
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
emit_mir_expr(func, &args[1], slots, ctx)?;
func.instruction(&Instruction::Call(fn_idx));
Ok(())
}
pub(super) fn emit_disk_list_dir_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation("Disk.listDir on wasip2: lowering ctx missing".into())
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"Disk.listDir on `--target wasip2` expects 1 arg (path), got {}",
args.len()
)));
}
let fn_idx = lowering.disk_list_dir_fn_idx.ok_or_else(|| {
WasmGcError::Validation("Disk.listDir on wasip2: __rt_disk_list_dir fn idx missing".into())
})?;
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::Call(fn_idx));
Ok(())
}
fn emit_http_simple_method_wasip2(
method_name: &str,
method_tag: i32,
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation(format!("{method_name} on wasip2: lowering ctx missing"))
})?;
if args.len() != 1 {
return Err(WasmGcError::Validation(format!(
"{method_name} on `--target wasip2` expects 1 arg (url), got {}",
args.len()
)));
}
let fn_idx = lowering.http_get_fn_idx.ok_or_else(|| {
WasmGcError::Validation(format!(
"{method_name} on wasip2: __rt_http_request fn idx missing"
))
})?;
let registry = ctx.registry;
let string_idx = registry.string_array_type_idx.ok_or_else(|| {
WasmGcError::Validation(format!("{method_name} on wasip2: string type idx missing"))
})?;
let map_slots = registry
.map_slots("Map<String,List<String>>")
.ok_or_else(|| {
WasmGcError::Validation(format!(
"{method_name} on wasip2: Map<String, List<String>> slots missing"
))
})?;
func.instruction(&Instruction::I32Const(method_tag));
emit_mir_expr(func, &args[0], slots, ctx)?;
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::ArrayNewDefault(string_idx));
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::ArrayNewDefault(string_idx));
const INITIAL_CAP: i32 = 16384;
func.instruction(&Instruction::I32Const(0));
func.instruction(&Instruction::I32Const(INITIAL_CAP));
func.instruction(&Instruction::I32Const(INITIAL_CAP));
func.instruction(&Instruction::ArrayNewDefault(map_slots.keys_array));
func.instruction(&Instruction::I32Const(INITIAL_CAP));
func.instruction(&Instruction::ArrayNewDefault(map_slots.values_array));
func.instruction(&Instruction::StructNew(map_slots.map));
func.instruction(&Instruction::Call(fn_idx));
Ok(())
}
fn emit_http_body_method_wasip2(
method_name: &str,
method_tag: i32,
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
let lowering = ctx.wasip2_lowering.ok_or_else(|| {
WasmGcError::Validation(format!("{method_name} on wasip2: lowering ctx missing"))
})?;
if args.len() != 4 {
return Err(WasmGcError::Validation(format!(
"{method_name} on `--target wasip2` expects 4 args (url, content_type, body, headers), got {}",
args.len()
)));
}
let fn_idx = lowering.http_get_fn_idx.ok_or_else(|| {
WasmGcError::Validation(format!(
"{method_name} on wasip2: __rt_http_request fn idx missing"
))
})?;
func.instruction(&Instruction::I32Const(method_tag));
emit_mir_expr(func, &args[0], slots, ctx)?; emit_mir_expr(func, &args[1], slots, ctx)?; emit_mir_expr(func, &args[2], slots, ctx)?; emit_mir_expr(func, &args[3], slots, ctx)?; func.instruction(&Instruction::Call(fn_idx));
Ok(())
}
pub(super) fn emit_http_get_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
emit_http_simple_method_wasip2("Http.get", 0, func, args, slots, ctx)
}
pub(super) fn emit_http_head_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
emit_http_simple_method_wasip2("Http.head", 1, func, args, slots, ctx)
}
pub(super) fn emit_http_delete_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
emit_http_simple_method_wasip2("Http.delete", 4, func, args, slots, ctx)
}
pub(super) fn emit_http_post_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
emit_http_body_method_wasip2("Http.post", 2, func, args, slots, ctx)
}
pub(super) fn emit_http_put_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
emit_http_body_method_wasip2("Http.put", 3, func, args, slots, ctx)
}
pub(super) fn emit_http_patch_wasip2(
func: &mut wasm_encoder::Function,
args: &[Spanned<MirExpr>],
slots: &SlotTable,
ctx: &EmitCtx<'_>,
) -> Result<(), WasmGcError> {
emit_http_body_method_wasip2("Http.patch", 8, func, args, slots, ctx)
}