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
3339
pub(in crate::compiler) fn compile_nil(ctx: &mut CompileContext, emit: &mut FunctionEmitter) {
16
3339
    ctx.serializer().write_debug_nil(emit);
17
3339
}
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
3346
pub(in crate::compiler) fn emit_nil_default(
24
3346
    ctx: &CompileContext,
25
3346
    emit: &mut FunctionEmitter,
26
3346
    ty: WasmType,
27
3346
) -> Result<()> {
28
3346
    match ty {
29
144
        WasmType::I32 | WasmType::Bool => emit.i32_const(0),
30
498
        WasmType::Ratio => push_ratio(ctx, emit, 0, 1),
31
1
        WasmType::StringRef => emit.ref_null(ctx.ids.ty_i8_array),
32
2699
        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
3345
    Ok(())
45
3346
}
46

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

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

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

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

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

            
97
19101
pub(in crate::compiler) fn push_ratio(
98
19101
    ctx: &CompileContext,
99
19101
    emit: &mut FunctionEmitter,
100
19101
    numer: i64,
101
19101
    denom: i64,
102
19101
) {
103
19101
    emit.i64_const(numer);
104
19101
    emit.i64_const(denom);
105
19101
    emit.call(ctx.ids.ratio_new);
106
19101
}
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
}