1
//! `DebugValueData` writers for the script-mode output stream:
2
//! nil / t / 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
85003
    fn begin_debug_value(&mut self, emit: &mut FunctionEmitter) -> u32 {
29
85003
        self.append_entity_header(
30
85003
            emit,
31
85003
            EntityType::DebugValue,
32
85003
            Operation::Nop,
33
            0,
34
            -1,
35
85003
            DEBUG_VALUE_DATA_SIZE as u32,
36
        )
37
85003
    }
38

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

            
49
1996
    pub fn write_debug_t(&mut self, emit: &mut FunctionEmitter) {
50
1996
        let data = self.begin_debug_value(emit);
51
1996
        emit.store_u8_dynamic(
52
            LOCAL_ENTITY_BASE,
53
1996
            data + offset_of!(DebugValueData, value_type) as u32,
54
1996
            ValueType::T as u8,
55
        );
56
1996
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
57
1996
    }
58

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

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

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

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

            
165
        // data_size = DEBUG_VALUE_DATA_SIZE + array.len (runtime), so the entity
166
        // walk skips the inline string bytes.
167
2739
        emit.local_get(LOCAL_ENTITY_BASE);
168
2739
        emit.i32_const(offset_of!(EntityHeader, data_size) as i32);
169
2739
        emit.i32_add();
170
2739
        emit.i32_const(DEBUG_VALUE_DATA_SIZE as i32);
171
2739
        emit.local_get(gc.arr);
172
2739
        emit.array_len();
173
2739
        emit.i32_add();
174
2739
        emit.i32_store_raw();
175

            
176
        // next_write_pos += DEBUG_ENTITY_BYTES + array.len (runtime)
177
2739
        emit.i32_const(DEBUG_ENTITY_BYTES as i32);
178
2739
        emit.local_get(gc.arr);
179
2739
        emit.array_len();
180
2739
        emit.i32_add();
181
2739
        self.advance_past_runtime(emit);
182
2739
    }
183

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

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

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

            
250
    /// Serialize a runtime i32 truth value (0 = nil, nonzero = t) so it decodes
251
    /// like the const-fold path. The value-type tag is `(v != 0)` (0 = Nil tag,
252
    /// 1 = T tag).
253
12284
    pub fn write_debug_bool_from_stack(&mut self, emit: &mut FunctionEmitter, bool_local: u32) {
254
12284
        emit.i32_const(0);
255
12284
        emit.i32_ne();
256
12284
        emit.local_set(bool_local);
257
12284
        let data = self.begin_debug_value(emit);
258
12284
        emit.local_get(LOCAL_ENTITY_BASE);
259
12284
        emit.i32_const((data + offset_of!(DebugValueData, value_type) as u32) as i32);
260
12284
        emit.i32_add();
261
12284
        emit.local_get(bool_local);
262
12284
        emit.i32_store8_raw();
263
12284
        emit.local_get(LOCAL_ENTITY_BASE);
264
12284
        emit.i32_const((data + offset_of!(DebugValueData, data1) as u32) as i32);
265
12284
        emit.i32_add();
266
12284
        emit.local_get(bool_local);
267
12284
        emit.i64_extend_i32_u();
268
12284
        emit.i64_store_raw();
269
12284
        self.advance_past(emit, DEBUG_ENTITY_BYTES);
270
12284
    }
271
}