1
use std::mem::offset_of;
2

            
3
use scripting_format::{
4
    DEBUG_VALUE_DATA_SIZE, DebugValueData, ENTITY_HEADER_SIZE, EntityHeader, EntityType,
5
    MAGIC_OUTP, OUTPUT_HEADER_SIZE, Operation, OutputHeader, TAG_DATA_SIZE, TagData, ValueType,
6
};
7
use tracing::debug;
8

            
9
use super::emit::FunctionEmitter;
10

            
11
pub struct GcLocals {
12
    pub type_idx: u32,
13
    pub arr: u32,
14
    pub idx: u32,
15
}
16

            
17
pub struct OutputSerializer {
18
    cursor: u32,
19
    entity_count: u32,
20
    output_start_idx: u32,
21
    output_base_local: u32,
22
}
23

            
24
impl OutputSerializer {
25
11058
    pub fn new(output_base_local: u32) -> Self {
26
11058
        Self {
27
11058
            cursor: 0,
28
11058
            entity_count: 0,
29
11058
            output_start_idx: 0,
30
11058
            output_base_local,
31
11058
        }
32
11058
    }
33

            
34
11058
    pub fn begin_output(&mut self, emit: &mut FunctionEmitter) {
35
11058
        debug!(cursor = self.cursor, "writing output header");
36
11058
        let local = self.output_base_local;
37
11058
        emit.store_i32_dynamic(
38
11058
            local,
39
11058
            self.cursor + offset_of!(OutputHeader, magic) as u32,
40
11058
            MAGIC_OUTP as i32,
41
        );
42
11058
        emit.store_i32_dynamic(
43
11058
            local,
44
11058
            self.cursor + offset_of!(OutputHeader, output_entity_count) as u32,
45
            0,
46
        );
47
11058
        emit.store_i32_dynamic(
48
11058
            local,
49
11058
            self.cursor + offset_of!(OutputHeader, output_start_idx) as u32,
50
11058
            self.output_start_idx as i32,
51
        );
52
11058
        self.cursor += OUTPUT_HEADER_SIZE as u32;
53
11058
    }
54

            
55
11058
    fn append_entity_header(
56
11058
        &mut self,
57
11058
        emit: &mut FunctionEmitter,
58
11058
        entity_type: EntityType,
59
11058
        operation: Operation,
60
11058
        flags: u8,
61
11058
        parent_idx: i32,
62
11058
        data_size: u32,
63
11058
    ) -> u32 {
64
11058
        debug!(
65
            cursor = self.cursor,
66
            ?entity_type,
67
            "appending entity header"
68
        );
69
11058
        let local = self.output_base_local;
70
11058
        let data_offset = self.cursor + ENTITY_HEADER_SIZE as u32;
71

            
72
11058
        emit.store_u8_dynamic(
73
11058
            local,
74
11058
            self.cursor + offset_of!(EntityHeader, entity_type) as u32,
75
11058
            entity_type as u8,
76
        );
77
11058
        emit.store_u8_dynamic(
78
11058
            local,
79
11058
            self.cursor + offset_of!(EntityHeader, operation) as u32,
80
11058
            operation as u8,
81
        );
82
11058
        emit.store_u8_dynamic(
83
11058
            local,
84
11058
            self.cursor + offset_of!(EntityHeader, flags) as u32,
85
11058
            flags,
86
        );
87
11058
        emit.store_i32_dynamic(
88
11058
            local,
89
11058
            self.cursor + offset_of!(EntityHeader, parent_idx) as u32,
90
11058
            parent_idx,
91
        );
92
        // data_offset is relative — the runtime adds output_base
93
11058
        emit.store_i32_dynamic(
94
11058
            local,
95
11058
            self.cursor + offset_of!(EntityHeader, data_offset) as u32,
96
11058
            data_offset as i32,
97
        );
98
11058
        emit.store_i32_dynamic(
99
11058
            local,
100
11058
            self.cursor + offset_of!(EntityHeader, data_size) as u32,
101
11058
            data_size as i32,
102
        );
103

            
104
11058
        self.cursor += ENTITY_HEADER_SIZE as u32;
105
11058
        self.entity_count += 1;
106
11058
        emit.store_i32_dynamic(
107
11058
            local,
108
11058
            offset_of!(OutputHeader, output_entity_count) as u32,
109
11058
            self.entity_count as i32,
110
        );
111
11058
        data_offset
112
11058
    }
113

            
114
11024
    fn begin_debug_value_entity(&mut self, emit: &mut FunctionEmitter) -> u32 {
115
11024
        self.append_entity_header(
116
11024
            emit,
117
11024
            EntityType::DebugValue,
118
11024
            Operation::Nop,
119
            0,
120
            -1,
121
11024
            DEBUG_VALUE_DATA_SIZE as u32,
122
        )
123
11024
    }
124

            
125
818
    pub fn write_debug_nil(&mut self, emit: &mut FunctionEmitter) {
126
818
        let data_start = self.begin_debug_value_entity(emit);
127
818
        debug!(data_start, "writing nil value");
128
818
        let local = self.output_base_local;
129
818
        emit.store_u8_dynamic(
130
818
            local,
131
818
            data_start + offset_of!(DebugValueData, value_type) as u32,
132
818
            ValueType::Nil as u8,
133
        );
134
818
        self.cursor = data_start + DEBUG_VALUE_DATA_SIZE as u32;
135
818
        self.finalize_cursor(emit);
136
818
    }
137

            
138
614
    pub fn write_debug_bool(&mut self, emit: &mut FunctionEmitter, value: bool) {
139
614
        let data_start = self.begin_debug_value_entity(emit);
140
614
        debug!(data_start, value, "writing bool value");
141
614
        let local = self.output_base_local;
142
614
        emit.store_u8_dynamic(
143
614
            local,
144
614
            data_start + offset_of!(DebugValueData, value_type) as u32,
145
614
            ValueType::Bool as u8,
146
        );
147
614
        emit.store_i64_dynamic(
148
614
            local,
149
614
            data_start + offset_of!(DebugValueData, data1) as u32,
150
614
            i64::from(value),
151
        );
152
614
        self.cursor = data_start + DEBUG_VALUE_DATA_SIZE as u32;
153
614
        self.finalize_cursor(emit);
154
614
    }
155

            
156
2755
    pub fn write_debug_number(&mut self, emit: &mut FunctionEmitter, numer: i64, denom: i64) {
157
2755
        let data_start = self.begin_debug_value_entity(emit);
158
2755
        debug!(data_start, numer, denom, "writing number value");
159
2755
        let local = self.output_base_local;
160
2755
        emit.store_u8_dynamic(
161
2755
            local,
162
2755
            data_start + offset_of!(DebugValueData, value_type) as u32,
163
2755
            ValueType::Number as u8,
164
        );
165
2755
        emit.store_i64_dynamic(
166
2755
            local,
167
2755
            data_start + offset_of!(DebugValueData, data1) as u32,
168
2755
            numer,
169
        );
170
2755
        emit.store_i64_dynamic(
171
2755
            local,
172
2755
            data_start + offset_of!(DebugValueData, data2) as u32,
173
2755
            denom,
174
        );
175
2755
        self.cursor = data_start + DEBUG_VALUE_DATA_SIZE as u32;
176
2755
        self.finalize_cursor(emit);
177
2755
    }
178

            
179
6837
    pub fn write_debug_string_gc(
180
6837
        &mut self,
181
6837
        emit: &mut FunctionEmitter,
182
6837
        value_type: ValueType,
183
6837
        data_idx: u32,
184
6837
        len: u32,
185
6837
        gc: &GcLocals,
186
6837
    ) {
187
6837
        debug!(
188
            cursor = self.cursor,
189
            ?value_type,
190
            data_idx,
191
            len,
192
            "writing string value with GC"
193
        );
194
6837
        let local = self.output_base_local;
195
6837
        let data_start = self.begin_debug_value_entity(emit);
196
6837
        let strings_pool = data_start + DEBUG_VALUE_DATA_SIZE as u32;
197

            
198
6837
        emit.store_u8_dynamic(
199
6837
            local,
200
6837
            data_start + offset_of!(DebugValueData, value_type) as u32,
201
6837
            value_type as u8,
202
        );
203
6837
        emit.store_i64_dynamic(
204
6837
            local,
205
6837
            data_start + offset_of!(DebugValueData, data1) as u32,
206
            0,
207
        );
208
6837
        emit.store_i64_dynamic(
209
6837
            local,
210
6837
            data_start + offset_of!(DebugValueData, data2) as u32,
211
6837
            i64::from(len),
212
        );
213

            
214
6837
        self.cursor = data_start + DEBUG_VALUE_DATA_SIZE as u32;
215

            
216
6837
        self.copy_data_gc(emit, data_idx, len, strings_pool, gc);
217

            
218
6837
        emit.store_i32_dynamic(
219
6837
            local,
220
6837
            offset_of!(OutputHeader, strings_offset) as u32,
221
6837
            strings_pool as i32,
222
        );
223

            
224
6837
        self.cursor += len;
225
6837
        emit.store_i32_dynamic(
226
6837
            local,
227
6837
            offset_of!(OutputHeader, next_write_pos) as u32,
228
6837
            self.cursor as i32,
229
        );
230
6837
    }
231

            
232
34
    pub fn write_tag_gc(
233
34
        &mut self,
234
34
        emit: &mut FunctionEmitter,
235
34
        name_data_idx: u32,
236
34
        name_len: u32,
237
34
        value_data_idx: u32,
238
34
        value_len: u32,
239
34
        gc: &GcLocals,
240
34
    ) {
241
34
        let local = self.output_base_local;
242
34
        let data_start = self.append_entity_header(
243
34
            emit,
244
34
            EntityType::Tag,
245
34
            Operation::Create,
246
            0,
247
            -1,
248
34
            TAG_DATA_SIZE as u32,
249
        );
250

            
251
34
        let strings_start = data_start + TAG_DATA_SIZE as u32;
252
34
        let name_start = strings_start;
253
34
        let value_start = name_start + name_len;
254
34
        let strings_end = value_start + value_len;
255
34
        let name_offset = strings_end - name_start;
256
34
        let value_offset = strings_end - value_start;
257

            
258
34
        emit.store_i32_dynamic(
259
34
            local,
260
34
            data_start + offset_of!(TagData, name_offset) as u32,
261
34
            name_offset as i32,
262
        );
263
34
        emit.store_i32_dynamic(
264
34
            local,
265
34
            data_start + offset_of!(TagData, value_offset) as u32,
266
34
            value_offset as i32,
267
        );
268
34
        emit.store_i32_dynamic(
269
34
            local,
270
34
            data_start + offset_of!(TagData, name_len) as u32,
271
34
            ((value_len as i32) << 16) | (name_len as i32),
272
        );
273
34
        emit.store_i32_dynamic(local, data_start + offset_of!(TagData, reserved) as u32, 0);
274

            
275
34
        self.copy_data_gc(emit, name_data_idx, name_len, name_start, gc);
276
34
        self.copy_data_gc(emit, value_data_idx, value_len, value_start, gc);
277

            
278
34
        self.cursor = strings_end;
279
34
        emit.store_i32_dynamic(
280
34
            local,
281
34
            offset_of!(OutputHeader, strings_offset) as u32,
282
34
            strings_end as i32,
283
        );
284
34
        emit.store_i32_dynamic(
285
34
            local,
286
34
            offset_of!(OutputHeader, next_write_pos) as u32,
287
34
            self.cursor as i32,
288
        );
289
34
    }
290

            
291
6905
    fn copy_data_gc(
292
6905
        &self,
293
6905
        emit: &mut FunctionEmitter,
294
6905
        data_idx: u32,
295
6905
        len: u32,
296
6905
        dst_offset: u32,
297
6905
        gc: &GcLocals,
298
6905
    ) {
299
6905
        let local = self.output_base_local;
300
6905
        emit.i32_const(0);
301
6905
        emit.i32_const(len as i32);
302
6905
        emit.array_new_data(gc.type_idx, data_idx);
303
6905
        emit.local_set(gc.arr);
304

            
305
6905
        emit.i32_const(0);
306
6905
        emit.local_set(gc.idx);
307

            
308
6905
        emit.block_start();
309
6905
        emit.loop_start();
310

            
311
6905
        emit.local_get(gc.idx);
312
6905
        emit.i32_const(len as i32);
313
6905
        emit.i32_ge_u();
314
6905
        emit.br_if(1);
315

            
316
6905
        emit.local_get(local);
317
6905
        emit.i32_const(dst_offset as i32);
318
6905
        emit.i32_add();
319
6905
        emit.local_get(gc.idx);
320
6905
        emit.i32_add();
321

            
322
6905
        emit.local_get(gc.arr);
323
6905
        emit.local_get(gc.idx);
324
6905
        emit.array_get_u(gc.type_idx);
325

            
326
6905
        emit.i32_store8_raw();
327

            
328
6905
        emit.local_get(gc.idx);
329
6905
        emit.i32_const(1);
330
6905
        emit.i32_add();
331
6905
        emit.local_set(gc.idx);
332

            
333
6905
        emit.br(0);
334
6905
        emit.block_end();
335
6905
        emit.block_end();
336
6905
    }
337

            
338
4187
    fn finalize_cursor(&self, emit: &mut FunctionEmitter) {
339
4187
        let local = self.output_base_local;
340
4187
        emit.store_i32_dynamic(
341
4187
            local,
342
4187
            offset_of!(OutputHeader, strings_offset) as u32,
343
4187
            self.cursor as i32,
344
        );
345
4187
        emit.store_i32_dynamic(
346
4187
            local,
347
4187
            offset_of!(OutputHeader, next_write_pos) as u32,
348
4187
            self.cursor as i32,
349
        );
350
4187
    }
351
}