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
72573
    fn begin_debug_value(&mut self, emit: &mut FunctionEmitter) -> u32 {
29
72573
        self.append_entity_header(
30
72573
            emit,
31
72573
            EntityType::DebugValue,
32
72573
            Operation::Nop,
33
            0,
34
            -1,
35
72573
            DEBUG_VALUE_DATA_SIZE as u32,
36
        )
37
72573
    }
38

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

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

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

            
84
29965
    pub fn write_debug_string_gc(
85
29965
        &mut self,
86
29965
        emit: &mut FunctionEmitter,
87
29965
        value_type: ValueType,
88
29965
        data_idx: u32,
89
29965
        len: u32,
90
29965
        gc: &GcLocals,
91
29965
    ) {
92
29965
        let data = self.begin_debug_value(emit);
93
29965
        let strings_at = data + DEBUG_VALUE_DATA_SIZE as u32;
94
29965
        emit.store_u8_dynamic(
95
            LOCAL_ENTITY_BASE,
96
29965
            data + offset_of!(DebugValueData, value_type) as u32,
97
29965
            value_type as u8,
98
        );
99
29965
        emit.store_i64_dynamic(
100
            LOCAL_ENTITY_BASE,
101
29965
            data + offset_of!(DebugValueData, data1) as u32,
102
            0,
103
        );
104
29965
        emit.store_i64_dynamic(
105
            LOCAL_ENTITY_BASE,
106
29965
            data + offset_of!(DebugValueData, data2) as u32,
107
29965
            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
29965
        emit.store_i32_dynamic(
113
            LOCAL_ENTITY_BASE,
114
29965
            offset_of!(EntityHeader, data_size) as u32,
115
29965
            (DEBUG_VALUE_DATA_SIZE as u32 + len) as i32,
116
        );
117
29965
        super::tag_record::copy_data_gc(emit, LOCAL_ENTITY_BASE, data_idx, len, strings_at, gc);
118
29965
        self.advance_past(emit, DEBUG_ENTITY_BYTES + len);
119
29965
    }
120

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

            
143
2343
        let strings_at = data + DEBUG_VALUE_DATA_SIZE as u32;
144
2343
        emit.i32_const(0);
145
2343
        emit.local_set(gc.idx);
146
2343
        emit.block_start();
147
2343
        emit.loop_start();
148
2343
        emit.local_get(gc.idx);
149
2343
        emit.local_get(gc.arr);
150
2343
        emit.array_len();
151
2343
        emit.i32_ge_u();
152
2343
        emit.br_if(1);
153
2343
        emit.local_get(LOCAL_ENTITY_BASE);
154
2343
        emit.i32_const(strings_at as i32);
155
2343
        emit.i32_add();
156
2343
        emit.local_get(gc.idx);
157
2343
        emit.i32_add();
158
2343
        emit.local_get(gc.arr);
159
2343
        emit.local_get(gc.idx);
160
2343
        emit.array_get_u(gc.type_idx);
161
2343
        emit.i32_store8_raw();
162
2343
        emit.local_get(gc.idx);
163
2343
        emit.i32_const(1);
164
2343
        emit.i32_add();
165
2343
        emit.local_set(gc.idx);
166
2343
        emit.br(0);
167
2343
        emit.block_end();
168
2343
        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
2343
        emit.local_get(LOCAL_ENTITY_BASE);
173
2343
        emit.i32_const(offset_of!(EntityHeader, data_size) as i32);
174
2343
        emit.i32_add();
175
2343
        emit.i32_const(DEBUG_VALUE_DATA_SIZE as i32);
176
2343
        emit.local_get(gc.arr);
177
2343
        emit.array_len();
178
2343
        emit.i32_add();
179
2343
        emit.i32_store_raw();
180

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

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

            
217
15336
    pub fn write_debug_i32_from_stack(&mut self, emit: &mut FunctionEmitter, i32_local: u32) {
218
15336
        emit.local_set(i32_local);
219
15336
        let data = self.begin_debug_value(emit);
220
15336
        emit.store_u8_dynamic(
221
            LOCAL_ENTITY_BASE,
222
15336
            data + offset_of!(DebugValueData, value_type) as u32,
223
15336
            ValueType::Number as u8,
224
        );
225
15336
        emit.local_get(LOCAL_ENTITY_BASE);
226
15336
        emit.i32_const((data + offset_of!(DebugValueData, data1) as u32) as i32);
227
15336
        emit.i32_add();
228
15336
        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
15336
        emit.i64_extend_i32_s();
233
15336
        emit.i64_store_raw();
234
15336
        emit.store_i64_dynamic(
235
            LOCAL_ENTITY_BASE,
236
15336
            data + offset_of!(DebugValueData, data2) as u32,
237
            1,
238
        );
239
15336
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
240
15336
    }
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
426
    pub fn write_debug_closure_from_stack(
245
426
        &mut self,
246
426
        emit: &mut FunctionEmitter,
247
426
        literal_data_idx: u32,
248
426
        literal_len: u32,
249
426
        gc: &GcLocals,
250
426
    ) {
251
426
        emit.drop_value();
252
426
        self.write_debug_string_gc(emit, ValueType::String, literal_data_idx, literal_len, gc);
253
426
    }
254

            
255
3337
    pub fn write_debug_bool_from_stack(&mut self, emit: &mut FunctionEmitter, bool_local: u32) {
256
3337
        emit.local_set(bool_local);
257
3337
        let data = self.begin_debug_value(emit);
258
3337
        emit.store_u8_dynamic(
259
            LOCAL_ENTITY_BASE,
260
3337
            data + offset_of!(DebugValueData, value_type) as u32,
261
3337
            ValueType::Bool as u8,
262
        );
263
3337
        emit.local_get(LOCAL_ENTITY_BASE);
264
3337
        emit.i32_const((data + offset_of!(DebugValueData, data1) as u32) as i32);
265
3337
        emit.i32_add();
266
3337
        emit.local_get(bool_local);
267
3337
        emit.i64_extend_i32_u();
268
3337
        emit.i64_store_raw();
269
3337
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
270
3337
    }
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
6887
    pub fn write_debug_bool_value_from_stack(
277
6887
        &mut self,
278
6887
        emit: &mut FunctionEmitter,
279
6887
        bool_local: u32,
280
6887
    ) {
281
6887
        emit.i32_const(0);
282
6887
        emit.i32_ne();
283
6887
        emit.local_set(bool_local);
284
6887
        let data = self.begin_debug_value(emit);
285
6887
        emit.local_get(LOCAL_ENTITY_BASE);
286
6887
        emit.i32_const((data + offset_of!(DebugValueData, value_type) as u32) as i32);
287
6887
        emit.i32_add();
288
6887
        emit.local_get(bool_local);
289
6887
        emit.i32_store8_raw();
290
6887
        emit.local_get(LOCAL_ENTITY_BASE);
291
6887
        emit.i32_const((data + offset_of!(DebugValueData, data1) as u32) as i32);
292
6887
        emit.i32_add();
293
6887
        emit.local_get(bool_local);
294
6887
        emit.i64_extend_i32_u();
295
6887
        emit.i64_store_raw();
296
6887
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
297
6887
    }
298
}