1
//! Primitive emit helpers — nil, bool, number, string, symbol — plus
2
//! the `push_ratio` numeric kernel and the `compile_text` machinery
3
//! `compile_string` and `compile_symbol` share.
4

            
5
use scripting_format::ValueType;
6

            
7
use crate::ast::WasmType;
8
use crate::compiler::context::CompileContext;
9
use crate::compiler::emit::FunctionEmitter;
10
use crate::compiler::layout::GcLocals;
11
use crate::error::{Error, Result};
12

            
13
use super::{LOCAL_GC_ARR, LOCAL_IDX};
14

            
15
3198
pub(in crate::compiler) fn compile_nil(ctx: &mut CompileContext, emit: &mut FunctionEmitter) {
16
3198
    ctx.serializer().write_debug_nil(emit);
17
3198
}
18

            
19
/// Emits a typed zero/null stack value for `ty` — the canonical nil for a
20
/// binding/accumulator declared with that wasm type. Every type with a valid
21
/// nil produces one; `Commodity` is the sole exception (a money zero has no
22
/// currency), so it errors rather than fabricate a currency-less amount.
23
3221
pub(in crate::compiler) fn emit_nil_default(
24
3221
    ctx: &CompileContext,
25
3221
    emit: &mut FunctionEmitter,
26
3221
    ty: WasmType,
27
3221
) -> Result<()> {
28
3221
    match ty {
29
138
        WasmType::I32 | WasmType::Bool => emit.i32_const(0),
30
477
        WasmType::Ratio => push_ratio(ctx, emit, 0, 1),
31
1
        WasmType::StringRef => emit.ref_null(ctx.ids.ty_i8_array),
32
2601
        WasmType::PairRef(_) => emit.ref_null(ctx.ids.ty_pair),
33
1
        WasmType::EntityRef(kind) => emit.ref_null(ctx.ids.entity_type(kind)),
34
1
        WasmType::Closure(sig) => emit.ref_null(ctx.closure_sig(sig).closure_type_idx),
35
1
        WasmType::AnyRef => emit.ref_null_any(),
36
        WasmType::Commodity => {
37
1
            return Err(Error::Compile(
38
1
                "cannot nil-initialize a commodity-bearing value: a money zero \
39
1
                 has no currency"
40
1
                    .to_string(),
41
1
            ));
42
        }
43
    }
44
3220
    Ok(())
45
3221
}
46

            
47
1636
pub(in crate::compiler) fn compile_bool(
48
1636
    ctx: &mut CompileContext,
49
1636
    emit: &mut FunctionEmitter,
50
1636
    value: bool,
51
1636
) {
52
1636
    ctx.serializer().write_debug_bool(emit, value);
53
1636
}
54

            
55
4354
pub(in crate::compiler) fn compile_number(
56
4354
    ctx: &mut CompileContext,
57
4354
    emit: &mut FunctionEmitter,
58
4354
    numer: i64,
59
4354
    denom: i64,
60
4354
) {
61
4354
    ctx.serializer().write_debug_number(emit, numer, denom);
62
4354
}
63

            
64
28291
fn compile_text(
65
28291
    ctx: &mut CompileContext,
66
28291
    emit: &mut FunctionEmitter,
67
28291
    value_type: ValueType,
68
28291
    s: &str,
69
28291
) -> Result<()> {
70
28291
    let data_idx = ctx.add_data(s.as_bytes())?;
71
28291
    let gc = GcLocals {
72
28291
        type_idx: ctx.ids.ty_i8_array,
73
28291
        arr: LOCAL_GC_ARR,
74
28291
        idx: LOCAL_IDX,
75
28291
    };
76
28291
    ctx.serializer()
77
28291
        .write_debug_string_gc(emit, value_type, data_idx, s.len() as u32, &gc);
78
28291
    Ok(())
79
28291
}
80

            
81
5917
pub(in crate::compiler) fn compile_string(
82
5917
    ctx: &mut CompileContext,
83
5917
    emit: &mut FunctionEmitter,
84
5917
    s: &str,
85
5917
) -> Result<()> {
86
5917
    compile_text(ctx, emit, ValueType::String, s)
87
5917
}
88

            
89
22374
pub(in crate::compiler) fn compile_symbol(
90
22374
    ctx: &mut CompileContext,
91
22374
    emit: &mut FunctionEmitter,
92
22374
    s: &str,
93
22374
) -> Result<()> {
94
22374
    compile_text(ctx, emit, ValueType::Symbol, s)
95
22374
}
96

            
97
18298
pub(in crate::compiler) fn push_ratio(
98
18298
    ctx: &CompileContext,
99
18298
    emit: &mut FunctionEmitter,
100
18298
    numer: i64,
101
18298
    denom: i64,
102
18298
) {
103
18298
    emit.i64_const(numer);
104
18298
    emit.i64_const(denom);
105
18298
    emit.call(ctx.ids.ratio_new);
106
18298
}
107

            
108
#[cfg(test)]
109
mod tests {
110
    use super::*;
111
    use crate::ast::{EntityKind, PairElement, WasmType};
112

            
113
    /// `emit_nil_default` produces a typed default for every wasm type that has
114
    /// a valid nil, and errors only for `Commodity` (a money zero has no
115
    /// currency). Covers all match arms, including the defensive `Commodity`
116
    /// guard that no current script can reach (no Commodity-producing literal).
117
    #[test]
118
1
    fn emit_nil_default_errors_only_for_commodity() {
119
1
        let mut ctx = CompileContext::new().expect("ctx");
120
1
        let sig = ctx
121
1
            .intern_closure_signature(&[], WasmType::Ratio)
122
1
            .expect("intern closure sig");
123
1
        let with_nil = [
124
1
            WasmType::I32,
125
1
            WasmType::Bool,
126
1
            WasmType::Ratio,
127
1
            WasmType::StringRef,
128
1
            WasmType::PairRef(PairElement::I32),
129
1
            WasmType::AnyRef,
130
1
            WasmType::EntityRef(EntityKind::Split),
131
1
            WasmType::Closure(sig),
132
1
        ];
133
8
        for ty in with_nil {
134
8
            let mut emit = FunctionEmitter::new();
135
8
            emit_nil_default(&ctx, &mut emit, ty)
136
8
                .unwrap_or_else(|e| panic!("emit_nil_default({ty:?}) should succeed: {e}"));
137
        }
138
1
        let mut emit = FunctionEmitter::new();
139
1
        assert!(
140
1
            emit_nil_default(&ctx, &mut emit, WasmType::Commodity).is_err(),
141
            "a commodity-bearing value has no valid nil default"
142
        );
143
1
    }
144
}