1
//! `DebugValueData` writers for the script-mode output stream:
2
//! nil / bool / number / string at constant-fold time, and the
3
//! from-stack variants for runtime values.
4
//!
5
//! Each appends one entity at the runtime `next_write_pos` via
6
//! [`OutputSerializer::append_entity_header`] (data fields relative to
7
//! [`LOCAL_ENTITY_BASE`]) then advances `next_write_pos`. String payloads grow
8
//! UP immediately after the value; since a script's program-result debug value
9
//! is the last thing written, that never disturbs the contiguous entity walk.
10

            
11
use std::mem::offset_of;
12

            
13
use scripting_format::{
14
    DEBUG_VALUE_DATA_SIZE, DebugValueData, ENTITY_HEADER_SIZE, EntityHeader, EntityType, Operation,
15
    ValueType,
16
};
17

            
18
use crate::compiler::emit::FunctionEmitter;
19
use crate::compiler::expr::LOCAL_ENTITY_BASE;
20

            
21
use super::GcLocals;
22
use super::headers::OutputSerializer;
23

            
24
/// Bytes a fixed (string-free) debug-value entity occupies.
25
const DEBUG_ENTITY_BYTES: u32 = ENTITY_HEADER_SIZE as u32 + DEBUG_VALUE_DATA_SIZE as u32;
26

            
27
impl OutputSerializer {
28
69507
    fn begin_debug_value(&mut self, emit: &mut FunctionEmitter) -> u32 {
29
69507
        self.append_entity_header(
30
69507
            emit,
31
69507
            EntityType::DebugValue,
32
69507
            Operation::Nop,
33
            0,
34
            -1,
35
69507
            DEBUG_VALUE_DATA_SIZE as u32,
36
        )
37
69507
    }
38

            
39
3198
    pub fn write_debug_nil(&mut self, emit: &mut FunctionEmitter) {
40
3198
        let data = self.begin_debug_value(emit);
41
3198
        emit.store_u8_dynamic(
42
            LOCAL_ENTITY_BASE,
43
3198
            data + offset_of!(DebugValueData, value_type) as u32,
44
3198
            ValueType::Nil as u8,
45
        );
46
3198
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
47
3198
    }
48

            
49
1636
    pub fn write_debug_bool(&mut self, emit: &mut FunctionEmitter, value: bool) {
50
1636
        let data = self.begin_debug_value(emit);
51
1636
        emit.store_u8_dynamic(
52
            LOCAL_ENTITY_BASE,
53
1636
            data + offset_of!(DebugValueData, value_type) as u32,
54
1636
            ValueType::Bool as u8,
55
        );
56
1636
        emit.store_i64_dynamic(
57
            LOCAL_ENTITY_BASE,
58
1636
            data + offset_of!(DebugValueData, data1) as u32,
59
1636
            i64::from(value),
60
        );
61
1636
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
62
1636
    }
63

            
64
4354
    pub fn write_debug_number(&mut self, emit: &mut FunctionEmitter, numer: i64, denom: i64) {
65
4354
        let data = self.begin_debug_value(emit);
66
4354
        emit.store_u8_dynamic(
67
            LOCAL_ENTITY_BASE,
68
4354
            data + offset_of!(DebugValueData, value_type) as u32,
69
4354
            ValueType::Number as u8,
70
        );
71
4354
        emit.store_i64_dynamic(
72
            LOCAL_ENTITY_BASE,
73
4354
            data + offset_of!(DebugValueData, data1) as u32,
74
4354
            numer,
75
        );
76
4354
        emit.store_i64_dynamic(
77
            LOCAL_ENTITY_BASE,
78
4354
            data + offset_of!(DebugValueData, data2) as u32,
79
4354
            denom,
80
        );
81
4354
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
82
4354
    }
83

            
84
28699
    pub fn write_debug_string_gc(
85
28699
        &mut self,
86
28699
        emit: &mut FunctionEmitter,
87
28699
        value_type: ValueType,
88
28699
        data_idx: u32,
89
28699
        len: u32,
90
28699
        gc: &GcLocals,
91
28699
    ) {
92
28699
        let data = self.begin_debug_value(emit);
93
28699
        let strings_at = data + DEBUG_VALUE_DATA_SIZE as u32;
94
28699
        emit.store_u8_dynamic(
95
            LOCAL_ENTITY_BASE,
96
28699
            data + offset_of!(DebugValueData, value_type) as u32,
97
28699
            value_type as u8,
98
        );
99
28699
        emit.store_i64_dynamic(
100
            LOCAL_ENTITY_BASE,
101
28699
            data + offset_of!(DebugValueData, data1) as u32,
102
            0,
103
        );
104
28699
        emit.store_i64_dynamic(
105
            LOCAL_ENTITY_BASE,
106
28699
            data + offset_of!(DebugValueData, data2) as u32,
107
28699
            i64::from(len),
108
        );
109
        // Count the inline string bytes in data_size so the entity parser walk
110
        // (ENTITY_HEADER_SIZE + data_size) skips them; the string is decoded
111
        // per-entity at data_offset + DEBUG_VALUE_DATA_SIZE.
112
28699
        emit.store_i32_dynamic(
113
            LOCAL_ENTITY_BASE,
114
28699
            offset_of!(EntityHeader, data_size) as u32,
115
28699
            (DEBUG_VALUE_DATA_SIZE as u32 + len) as i32,
116
        );
117
28699
        super::tag_record::copy_data_gc(emit, LOCAL_ENTITY_BASE, data_idx, len, strings_at, gc);
118
28699
        self.advance_past(emit, DEBUG_ENTITY_BYTES + len);
119
28699
    }
120

            
121
2244
    pub fn write_debug_string_from_stack(&mut self, emit: &mut FunctionEmitter, gc: &GcLocals) {
122
2244
        emit.local_set(gc.arr);
123
2244
        let data = self.begin_debug_value(emit);
124
2244
        emit.store_u8_dynamic(
125
            LOCAL_ENTITY_BASE,
126
2244
            data + offset_of!(DebugValueData, value_type) as u32,
127
2244
            ValueType::String as u8,
128
        );
129
2244
        emit.store_i64_dynamic(
130
            LOCAL_ENTITY_BASE,
131
2244
            data + offset_of!(DebugValueData, data1) as u32,
132
            0,
133
        );
134
        // data2 = array.len (runtime)
135
2244
        emit.local_get(LOCAL_ENTITY_BASE);
136
2244
        emit.i32_const((data + offset_of!(DebugValueData, data2) as u32) as i32);
137
2244
        emit.i32_add();
138
2244
        emit.local_get(gc.arr);
139
2244
        emit.array_len();
140
2244
        emit.i64_extend_i32_u();
141
2244
        emit.i64_store_raw();
142

            
143
2244
        let strings_at = data + DEBUG_VALUE_DATA_SIZE as u32;
144
2244
        emit.i32_const(0);
145
2244
        emit.local_set(gc.idx);
146
2244
        emit.block_start();
147
2244
        emit.loop_start();
148
2244
        emit.local_get(gc.idx);
149
2244
        emit.local_get(gc.arr);
150
2244
        emit.array_len();
151
2244
        emit.i32_ge_u();
152
2244
        emit.br_if(1);
153
2244
        emit.local_get(LOCAL_ENTITY_BASE);
154
2244
        emit.i32_const(strings_at as i32);
155
2244
        emit.i32_add();
156
2244
        emit.local_get(gc.idx);
157
2244
        emit.i32_add();
158
2244
        emit.local_get(gc.arr);
159
2244
        emit.local_get(gc.idx);
160
2244
        emit.array_get_u(gc.type_idx);
161
2244
        emit.i32_store8_raw();
162
2244
        emit.local_get(gc.idx);
163
2244
        emit.i32_const(1);
164
2244
        emit.i32_add();
165
2244
        emit.local_set(gc.idx);
166
2244
        emit.br(0);
167
2244
        emit.block_end();
168
2244
        emit.block_end();
169

            
170
        // data_size = DEBUG_VALUE_DATA_SIZE + array.len (runtime), so the entity
171
        // walk skips the inline string bytes.
172
2244
        emit.local_get(LOCAL_ENTITY_BASE);
173
2244
        emit.i32_const(offset_of!(EntityHeader, data_size) as i32);
174
2244
        emit.i32_add();
175
2244
        emit.i32_const(DEBUG_VALUE_DATA_SIZE as i32);
176
2244
        emit.local_get(gc.arr);
177
2244
        emit.array_len();
178
2244
        emit.i32_add();
179
2244
        emit.i32_store_raw();
180

            
181
        // next_write_pos += DEBUG_ENTITY_BYTES + array.len (runtime)
182
2244
        emit.i32_const(DEBUG_ENTITY_BYTES as i32);
183
2244
        emit.local_get(gc.arr);
184
2244
        emit.array_len();
185
2244
        emit.i32_add();
186
2244
        self.advance_past_runtime(emit);
187
2244
    }
188

            
189
4896
    pub fn write_debug_number_from_stack(
190
4896
        &mut self,
191
4896
        emit: &mut FunctionEmitter,
192
4896
        ratio_type_idx: u32,
193
4896
        ratio_local: u32,
194
4896
    ) {
195
4896
        emit.local_set(ratio_local);
196
4896
        let data = self.begin_debug_value(emit);
197
4896
        emit.store_u8_dynamic(
198
            LOCAL_ENTITY_BASE,
199
4896
            data + offset_of!(DebugValueData, value_type) as u32,
200
4896
            ValueType::Number as u8,
201
        );
202
4896
        emit.local_get(LOCAL_ENTITY_BASE);
203
4896
        emit.i32_const((data + offset_of!(DebugValueData, data1) as u32) as i32);
204
4896
        emit.i32_add();
205
4896
        emit.local_get(ratio_local);
206
4896
        emit.struct_get(ratio_type_idx, 0);
207
4896
        emit.i64_store_raw();
208
4896
        emit.local_get(LOCAL_ENTITY_BASE);
209
4896
        emit.i32_const((data + offset_of!(DebugValueData, data2) as u32) as i32);
210
4896
        emit.i32_add();
211
4896
        emit.local_get(ratio_local);
212
4896
        emit.struct_get(ratio_type_idx, 1);
213
4896
        emit.i64_store_raw();
214
4896
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
215
4896
    }
216

            
217
14688
    pub fn write_debug_i32_from_stack(&mut self, emit: &mut FunctionEmitter, i32_local: u32) {
218
14688
        emit.local_set(i32_local);
219
14688
        let data = self.begin_debug_value(emit);
220
14688
        emit.store_u8_dynamic(
221
            LOCAL_ENTITY_BASE,
222
14688
            data + offset_of!(DebugValueData, value_type) as u32,
223
14688
            ValueType::Number as u8,
224
        );
225
14688
        emit.local_get(LOCAL_ENTITY_BASE);
226
14688
        emit.i32_const((data + offset_of!(DebugValueData, data1) as u32) as i32);
227
14688
        emit.i32_add();
228
14688
        emit.local_get(i32_local);
229
        // SIGNED extend: an Index/count is a signed i32 (ADR-0028). A negative
230
        // arithmetic result must decode as a negative `Number`, not its unsigned
231
        // 2^32 image. Length / bool sites stay `_u` (provably ≥ 0).
232
14688
        emit.i64_extend_i32_s();
233
14688
        emit.i64_store_raw();
234
14688
        emit.store_i64_dynamic(
235
            LOCAL_ENTITY_BASE,
236
14688
            data + offset_of!(DebugValueData, data2) as u32,
237
            1,
238
        );
239
14688
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
240
14688
    }
241

            
242
    /// Drops the closure ref on the stack and writes a fixed `<closure>` debug
243
    /// value (closures aren't decoded yet). Mirrors [`Self::write_debug_string_gc`].
244
408
    pub fn write_debug_closure_from_stack(
245
408
        &mut self,
246
408
        emit: &mut FunctionEmitter,
247
408
        literal_data_idx: u32,
248
408
        literal_len: u32,
249
408
        gc: &GcLocals,
250
408
    ) {
251
408
        emit.drop_value();
252
408
        self.write_debug_string_gc(emit, ValueType::String, literal_data_idx, literal_len, gc);
253
408
    }
254

            
255
3196
    pub fn write_debug_bool_from_stack(&mut self, emit: &mut FunctionEmitter, bool_local: u32) {
256
3196
        emit.local_set(bool_local);
257
3196
        let data = self.begin_debug_value(emit);
258
3196
        emit.store_u8_dynamic(
259
            LOCAL_ENTITY_BASE,
260
3196
            data + offset_of!(DebugValueData, value_type) as u32,
261
3196
            ValueType::Bool as u8,
262
        );
263
3196
        emit.local_get(LOCAL_ENTITY_BASE);
264
3196
        emit.i32_const((data + offset_of!(DebugValueData, data1) as u32) as i32);
265
3196
        emit.i32_add();
266
3196
        emit.local_get(bool_local);
267
3196
        emit.i64_extend_i32_u();
268
3196
        emit.i64_store_raw();
269
3196
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
270
3196
    }
271

            
272
    /// Serialize a runtime nomiscript boolean (`WasmType::Bool` i32: 0 = false,
273
    /// nonzero = true) so it decodes like the const-fold path: `#f` → `Nil`,
274
    /// `#t` → `Bool(true)`. The value-type tag is `(v != 0)` (0 = Nil tag, 1 =
275
    /// Bool tag) — also the `data1` truth value.
276
6596
    pub fn write_debug_bool_value_from_stack(
277
6596
        &mut self,
278
6596
        emit: &mut FunctionEmitter,
279
6596
        bool_local: u32,
280
6596
    ) {
281
6596
        emit.i32_const(0);
282
6596
        emit.i32_ne();
283
6596
        emit.local_set(bool_local);
284
6596
        let data = self.begin_debug_value(emit);
285
6596
        emit.local_get(LOCAL_ENTITY_BASE);
286
6596
        emit.i32_const((data + offset_of!(DebugValueData, value_type) as u32) as i32);
287
6596
        emit.i32_add();
288
6596
        emit.local_get(bool_local);
289
6596
        emit.i32_store8_raw();
290
6596
        emit.local_get(LOCAL_ENTITY_BASE);
291
6596
        emit.i32_const((data + offset_of!(DebugValueData, data1) as u32) as i32);
292
6596
        emit.i32_add();
293
6596
        emit.local_get(bool_local);
294
6596
        emit.i64_extend_i32_u();
295
6596
        emit.i64_store_raw();
296
6596
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
297
6596
    }
298
}