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

            
47
1996
pub(in crate::compiler) fn compile_t(ctx: &mut CompileContext, emit: &mut FunctionEmitter) {
48
1996
    ctx.serializer().write_debug_t(emit);
49
1996
}
50

            
51
5397
pub(in crate::compiler) fn compile_number(
52
5397
    ctx: &mut CompileContext,
53
5397
    emit: &mut FunctionEmitter,
54
5397
    numer: i64,
55
5397
    denom: i64,
56
5397
) {
57
5397
    ctx.serializer().write_debug_number(emit, numer, denom);
58
5397
}
59

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

            
77
7139
pub(in crate::compiler) fn compile_string(
78
7139
    ctx: &mut CompileContext,
79
7139
    emit: &mut FunctionEmitter,
80
7139
    s: &str,
81
7139
) -> Result<()> {
82
7139
    compile_text(ctx, emit, ValueType::String, s)
83
7139
}
84

            
85
27309
pub(in crate::compiler) fn compile_symbol(
86
27309
    ctx: &mut CompileContext,
87
27309
    emit: &mut FunctionEmitter,
88
27309
    s: &str,
89
27309
) -> Result<()> {
90
27309
    compile_text(ctx, emit, ValueType::Symbol, s)
91
27309
}
92

            
93
26064
pub(in crate::compiler) fn push_ratio(
94
26064
    ctx: &CompileContext,
95
26064
    emit: &mut FunctionEmitter,
96
26064
    numer: i64,
97
26064
    denom: i64,
98
26064
) {
99
26064
    emit.i64_const(numer);
100
26064
    emit.i64_const(denom);
101
26064
    emit.call(ctx.ids.ratio_new);
102
26064
}
103

            
104
#[cfg(test)]
105
mod tests {
106
    use super::*;
107
    use crate::ast::{EntityKind, PairElement, WasmType};
108

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