1
use std::collections::HashMap;
2

            
3
use anodized::spec;
4
use tracing::debug;
5

            
6
use super::layout::OutputSerializer;
7
use crate::ast::WasmType;
8
use wasm_encoder::{
9
    ArrayType, CodeSection, CompositeInnerType, CompositeType, DataCountSection, DataSection,
10
    EntityType as WasmEntityType, ExportKind, ExportSection, FieldType, Function, FunctionSection,
11
    HeapType, ImportSection, Instruction, MemorySection, MemoryType, Module, RefType, StorageType,
12
    StructType, SubType, TypeSection, ValType,
13
};
14

            
15
/// Base index for user-allocatable locals (after pre-allocated slots)
16
pub const LOCAL_POOL_BASE: u32 = 5;
17

            
18
pub struct CompileContext {
19
    types: TypeSection,
20
    imports: ImportSection,
21
    functions: FunctionSection,
22
    memories: MemorySection,
23
    exports: ExportSection,
24
    data: DataSection,
25
    codes: CodeSection,
26
    data_count: u32,
27
    type_count: u32,
28
    import_func_count: u32,
29
    local_func_count: u32,
30
    type_cache: HashMap<Vec<ValType>, HashMap<Vec<ValType>, u32>>,
31
    type_names: HashMap<String, u32>,
32
    func_names: HashMap<String, u32>,
33
    pending_helpers: Vec<Function>,
34
    next_local: u32,
35
    local_types: Vec<(WasmType, u32)>,
36
    serializer: OutputSerializer,
37
}
38

            
39
impl CompileContext {
40
20256
    pub fn new() -> Self {
41
20256
        debug!("initializing compile context");
42
20256
        let mut ctx = Self {
43
20256
            types: TypeSection::new(),
44
20256
            imports: ImportSection::new(),
45
20256
            functions: FunctionSection::new(),
46
20256
            memories: MemorySection::new(),
47
20256
            exports: ExportSection::new(),
48
20256
            data: DataSection::new(),
49
20256
            codes: CodeSection::new(),
50
20256
            data_count: 0,
51
20256
            type_count: 0,
52
20256
            import_func_count: 0,
53
20256
            local_func_count: 0,
54
20256
            type_cache: HashMap::new(),
55
20256
            type_names: HashMap::new(),
56
20256
            func_names: HashMap::new(),
57
20256
            pending_helpers: Vec::new(),
58
20256
            next_local: LOCAL_POOL_BASE,
59
20256
            local_types: Vec::new(),
60
20256
            serializer: OutputSerializer::new(super::expr::LOCAL_OUTPUT_BASE),
61
20256
        };
62

            
63
20256
        ctx.register_type("i8_array");
64
20256
        ctx.register_struct_type("ratio", &[ValType::I64, ValType::I64]);
65
20256
        ctx.register_cons_type();
66

            
67
20256
        ctx.register_import("env", "get_output_offset", &[], &[ValType::I32]);
68
20256
        ctx.register_import("env", "symbol_resolve", &[ValType::I32, ValType::I32], &[]);
69
20256
        ctx.register_import(
70
20256
            "env",
71
20256
            "log",
72
20256
            &[ValType::I32, ValType::I32, ValType::I32],
73
20256
            &[],
74
        );
75
20256
        ctx.register_import("env", "get_input_offset", &[], &[ValType::I32]);
76
20256
        ctx.register_import("env", "get_strings_offset", &[], &[ValType::I32]);
77
20256
        ctx.register_import("env", "get_input_entities_count", &[], &[ValType::I32]);
78
20256
        ctx.register_import("env", "get_timestamp", &[], &[ValType::I64]);
79
20256
        ctx.register_import("env", "generate_uuid", &[ValType::I32], &[]);
80
20256
        ctx.register_import(
81
20256
            "env",
82
20256
            "write_string",
83
20256
            &[ValType::I32, ValType::I32],
84
20256
            &[ValType::I32],
85
        );
86
20256
        ctx.register_import(
87
20256
            "env",
88
20256
            "write_bytes",
89
20256
            &[ValType::I32, ValType::I32, ValType::I32],
90
20256
            &[ValType::I32],
91
        );
92
20256
        ctx.register_function("should_apply", &[], &[ValType::I32]);
93
20256
        ctx.register_ratio_helpers();
94
20256
        ctx.register_string_eq_helper();
95
20256
        ctx.register_function("process", &[], &[]);
96
20256
        ctx.export_func("should_apply");
97
20256
        ctx.export_func("process");
98

            
99
20256
        ctx.memories.memory(MemoryType {
100
20256
            minimum: 1,
101
20256
            maximum: None,
102
20256
            memory64: false,
103
20256
            shared: false,
104
20256
            page_size_log2: None,
105
20256
        });
106
20256
        ctx.exports.export("memory", ExportKind::Memory, 0);
107

            
108
20256
        debug!("compile context initialized");
109
20256
        ctx
110
20256
    }
111

            
112
41491
    pub fn ratio_ref(&self) -> ValType {
113
41491
        ValType::Ref(RefType {
114
41491
            nullable: true,
115
41491
            heap_type: HeapType::Concrete(self.type_idx("ratio")),
116
41491
        })
117
41491
    }
118

            
119
2496
    pub fn cons_ref(&self) -> ValType {
120
2496
        ValType::Ref(RefType {
121
2496
            nullable: true,
122
2496
            heap_type: HeapType::Concrete(self.type_idx("cons")),
123
2496
        })
124
2496
    }
125

            
126
22614
    pub fn alloc_local(&mut self, ty: WasmType) -> u32 {
127
22614
        let idx = self.next_local;
128
22614
        self.local_types.push((ty, idx));
129
22614
        self.next_local += 1;
130
22614
        idx
131
22614
    }
132

            
133
19064
    pub fn reset_locals(&mut self) {
134
19064
        self.next_local = LOCAL_POOL_BASE;
135
19064
        self.local_types.clear();
136
19064
        self.serializer = OutputSerializer::new(super::expr::LOCAL_OUTPUT_BASE);
137
19064
    }
138

            
139
19064
    pub fn build_locals_declaration(&self) -> Vec<(u32, ValType)> {
140
19064
        let preallocated: [(u32, ValType); 5] = [
141
19064
            (1, self.string_ref()),
142
19064
            (1, ValType::I32),
143
19064
            (1, ValType::I32),
144
19064
            (1, self.ratio_ref()),
145
19064
            (1, ValType::I32),
146
19064
        ];
147
19064
        let mut locals: Vec<(u32, ValType)> = preallocated.to_vec();
148
22622
        for &(ty, _) in &self.local_types {
149
22614
            locals.push((1, self.wasm_val_type(ty)));
150
22614
        }
151
19064
        locals
152
19064
    }
153

            
154
41866
    pub fn string_ref(&self) -> ValType {
155
41866
        ValType::Ref(RefType {
156
41866
            nullable: true,
157
41866
            heap_type: HeapType::Concrete(self.type_idx("i8_array")),
158
41866
        })
159
41866
    }
160

            
161
22702
    pub fn wasm_val_type(&self, ty: WasmType) -> ValType {
162
22702
        match ty {
163
15490
            WasmType::I32 => ValType::I32,
164
2170
            WasmType::Ratio => self.ratio_ref(),
165
2496
            WasmType::ConsRef => self.cons_ref(),
166
2546
            WasmType::StringRef => self.string_ref(),
167
        }
168
22702
    }
169

            
170
    #[spec(
171
        captures: [self.type_count as prev_count],
172
20256
        ensures: [self.type_count == prev_count + 1, *output == prev_count],
173
    )]
174
20256
    pub fn register_type(&mut self, name: &str) -> u32 {
175
20256
        let idx = self.type_count;
176
20256
        self.types.ty().subtype(&SubType {
177
20256
            is_final: true,
178
20256
            supertype_idx: None,
179
20256
            composite_type: CompositeType {
180
20256
                inner: CompositeInnerType::Array(ArrayType(FieldType {
181
20256
                    element_type: StorageType::I8,
182
20256
                    mutable: true,
183
20256
                })),
184
20256
                shared: false,
185
20256
                describes: None,
186
20256
                descriptor: None,
187
20256
            },
188
20256
        });
189
20256
        self.type_count += 1;
190
20256
        self.type_names.insert(name.to_string(), idx);
191
20256
        idx
192
    }
193

            
194
20256
    pub fn register_struct_type(&mut self, name: &str, fields: &[ValType]) -> u32 {
195
20256
        let idx = self.type_count;
196
20256
        let struct_fields: Vec<FieldType> = fields
197
20256
            .iter()
198
20256
            .map(|vt| FieldType {
199
40512
                element_type: StorageType::Val(*vt),
200
                mutable: false,
201
40512
            })
202
20256
            .collect();
203
20256
        self.types.ty().subtype(&SubType {
204
20256
            is_final: true,
205
20256
            supertype_idx: None,
206
20256
            composite_type: CompositeType {
207
20256
                inner: CompositeInnerType::Struct(StructType {
208
20256
                    fields: struct_fields.into_boxed_slice(),
209
20256
                }),
210
20256
                shared: false,
211
20256
                describes: None,
212
20256
                descriptor: None,
213
20256
            },
214
20256
        });
215
20256
        self.type_count += 1;
216
20256
        self.type_names.insert(name.to_string(), idx);
217
20256
        idx
218
20256
    }
219

            
220
    #[spec(
221
        captures: [self.import_func_count as prev_count],
222
202560
        ensures: [self.import_func_count == prev_count + 1, *output == prev_count],
223
    )]
224
    pub fn register_import(
225
        &mut self,
226
        module: &str,
227
        name: &str,
228
        params: &[ValType],
229
        results: &[ValType],
230
202560
    ) -> u32 {
231
202560
        let type_idx = self.get_or_create_func_type(params, results);
232
202560
        self.imports
233
202560
            .import(module, name, WasmEntityType::Function(type_idx));
234
202560
        let func_idx = self.import_func_count;
235
202560
        self.import_func_count += 1;
236
202560
        self.func_names.insert(name.to_string(), func_idx);
237
202560
        func_idx
238
    }
239

            
240
    #[spec(
241
        captures: [self.local_func_count as prev_count],
242
243072
        ensures: [self.local_func_count == prev_count + 1],
243
    )]
244
243072
    pub fn register_function(&mut self, name: &str, params: &[ValType], results: &[ValType]) {
245
243072
        let type_idx = self.get_or_create_func_type(params, results);
246
243072
        self.functions.function(type_idx);
247
243072
        let func_idx = self.import_func_count + self.local_func_count;
248
243072
        self.local_func_count += 1;
249
243072
        self.func_names.insert(name.to_string(), func_idx);
250
    }
251

            
252
40512
    pub fn export_func(&mut self, name: &str) {
253
40512
        let idx = self.func_names[name];
254
40512
        self.exports.export(name, ExportKind::Func, idx);
255
40512
    }
256

            
257
82771
    pub fn func(&self, name: &str) -> u32 {
258
82771
        self.func_names[name]
259
82771
    }
260

            
261
209571
    pub fn type_idx(&self, name: &str) -> u32 {
262
209571
        self.type_names[name]
263
209571
    }
264

            
265
39267
    pub fn serializer(&mut self) -> &mut OutputSerializer {
266
39267
        &mut self.serializer
267
39267
    }
268

            
269
445632
    #[spec(ensures: [*output < self.type_count])]
270
445632
    fn get_or_create_func_type(&mut self, params: &[ValType], results: &[ValType]) -> u32 {
271
445632
        if let Some(inner) = self.type_cache.get(params)
272
283584
            && let Some(&idx) = inner.get(results)
273
        {
274
162048
            return idx;
275
283584
        }
276
283584
        let idx = self.type_count;
277
283584
        self.types
278
283584
            .ty()
279
283584
            .function(params.iter().copied(), results.iter().copied());
280
283584
        self.type_count += 1;
281
283584
        self.type_cache
282
283584
            .entry(params.to_vec())
283
283584
            .or_default()
284
283584
            .insert(results.to_vec(), idx);
285
283584
        idx
286
    }
287

            
288
    #[spec(
289
        captures: [self.data_count as prev_count],
290
11361
        ensures: [self.data_count == prev_count + 1, *output == prev_count],
291
    )]
292
11361
    pub fn add_data(&mut self, bytes: &[u8]) -> u32 {
293
11361
        let idx = self.data_count;
294
11361
        debug!(idx, len = bytes.len(), "adding data segment");
295
11361
        self.data.passive(bytes.iter().copied());
296
11361
        self.data_count += 1;
297
11361
        idx
298
    }
299

            
300
20256
    fn register_cons_type(&mut self) {
301
20256
        let cons_idx = self.type_count;
302
20256
        let cons_nullable_ref = ValType::Ref(RefType {
303
20256
            nullable: true,
304
20256
            heap_type: HeapType::Concrete(cons_idx),
305
20256
        });
306
20256
        let struct_fields = vec![
307
20256
            FieldType {
308
20256
                element_type: StorageType::Val(ValType::I32),
309
20256
                mutable: false,
310
20256
            },
311
20256
            FieldType {
312
20256
                element_type: StorageType::Val(cons_nullable_ref),
313
20256
                mutable: false,
314
20256
            },
315
        ];
316
20256
        self.types.ty().subtype(&SubType {
317
20256
            is_final: true,
318
20256
            supertype_idx: None,
319
20256
            composite_type: CompositeType {
320
20256
                inner: CompositeInnerType::Struct(StructType {
321
20256
                    fields: struct_fields.into_boxed_slice(),
322
20256
                }),
323
20256
                shared: false,
324
20256
                describes: None,
325
20256
                descriptor: None,
326
20256
            },
327
20256
        });
328
20256
        self.type_count += 1;
329
20256
        self.type_names.insert("cons".to_string(), cons_idx);
330
20256
    }
331

            
332
20256
    fn register_string_eq_helper(&mut self) {
333
20256
        let string_ref = self.string_ref();
334
20256
        self.register_function("string_eq", &[string_ref, string_ref], &[ValType::I32]);
335
20256
        self.build_string_eq_body();
336
20256
    }
337

            
338
20256
    fn build_string_eq_body(&mut self) {
339
20256
        let i8_array_idx = self.type_idx("i8_array");
340
        // params: $a=0 (ref null $i8_array), $b=1 (ref null $i8_array)
341
        // locals: $len=2 (i32), $i=3 (i32)
342
20256
        let mut f = Function::new([(1, ValType::I32), (1, ValType::I32)]);
343

            
344
        // len = array.len(a)
345
20256
        f.instruction(&Instruction::LocalGet(0));
346
20256
        f.instruction(&Instruction::ArrayLen);
347
20256
        f.instruction(&Instruction::LocalSet(2));
348

            
349
        // if array.len(b) != len → return 0
350
20256
        f.instruction(&Instruction::LocalGet(1));
351
20256
        f.instruction(&Instruction::ArrayLen);
352
20256
        f.instruction(&Instruction::LocalGet(2));
353
20256
        f.instruction(&Instruction::I32Ne);
354
20256
        f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
355
20256
        f.instruction(&Instruction::I32Const(0));
356
20256
        f.instruction(&Instruction::Return);
357
20256
        f.instruction(&Instruction::End);
358

            
359
        // i = 0
360
20256
        f.instruction(&Instruction::I32Const(0));
361
20256
        f.instruction(&Instruction::LocalSet(3));
362

            
363
        // block $exit
364
20256
        f.instruction(&Instruction::Block(wasm_encoder::BlockType::Empty));
365
        // loop $cmp
366
20256
        f.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
367

            
368
        // if i >= len → break (equal)
369
20256
        f.instruction(&Instruction::LocalGet(3));
370
20256
        f.instruction(&Instruction::LocalGet(2));
371
20256
        f.instruction(&Instruction::I32GeU);
372
20256
        f.instruction(&Instruction::BrIf(1));
373

            
374
        // if a[i] != b[i] → return 0
375
20256
        f.instruction(&Instruction::LocalGet(0));
376
20256
        f.instruction(&Instruction::LocalGet(3));
377
20256
        f.instruction(&Instruction::ArrayGetU(i8_array_idx));
378
20256
        f.instruction(&Instruction::LocalGet(1));
379
20256
        f.instruction(&Instruction::LocalGet(3));
380
20256
        f.instruction(&Instruction::ArrayGetU(i8_array_idx));
381
20256
        f.instruction(&Instruction::I32Ne);
382
20256
        f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
383
20256
        f.instruction(&Instruction::I32Const(0));
384
20256
        f.instruction(&Instruction::Return);
385
20256
        f.instruction(&Instruction::End);
386

            
387
        // i++
388
20256
        f.instruction(&Instruction::LocalGet(3));
389
20256
        f.instruction(&Instruction::I32Const(1));
390
20256
        f.instruction(&Instruction::I32Add);
391
20256
        f.instruction(&Instruction::LocalSet(3));
392
20256
        f.instruction(&Instruction::Br(0));
393

            
394
20256
        f.instruction(&Instruction::End); // end loop
395
20256
        f.instruction(&Instruction::End); // end block
396

            
397
        // return 1 (equal)
398
20256
        f.instruction(&Instruction::I32Const(1));
399
20256
        f.instruction(&Instruction::End);
400
20256
        self.pending_helpers.push(f);
401
20256
    }
402

            
403
20256
    fn register_ratio_helpers(&mut self) {
404
20256
        let ratio_ref = self.ratio_ref();
405

            
406
20256
        self.register_function("gcd", &[ValType::I64, ValType::I64], &[ValType::I64]);
407
20256
        self.register_function("ratio_new", &[ValType::I64, ValType::I64], &[ratio_ref]);
408
20256
        self.register_function("ratio_add", &[ratio_ref, ratio_ref], &[ratio_ref]);
409
20256
        self.register_function("ratio_sub", &[ratio_ref, ratio_ref], &[ratio_ref]);
410
20256
        self.register_function("ratio_mul", &[ratio_ref, ratio_ref], &[ratio_ref]);
411
20256
        self.register_function("ratio_div", &[ratio_ref, ratio_ref], &[ratio_ref]);
412
20256
        self.register_function("ratio_eq", &[ratio_ref, ratio_ref], &[ValType::I32]);
413
20256
        self.register_function("ratio_lt", &[ratio_ref, ratio_ref], &[ValType::I32]);
414
20256
        self.register_function("ratio_from_i64", &[ValType::I64], &[ratio_ref]);
415

            
416
20256
        self.build_gcd_body();
417
20256
        self.build_ratio_new_body();
418
20256
        self.build_ratio_binop_bodies();
419
20256
        self.build_ratio_cmp_bodies();
420
20256
        self.build_ratio_from_i64_body();
421
20256
    }
422

            
423
20256
    fn build_gcd_body(&mut self) {
424
        // Euclidean GCD: gcd(a, b) while b != 0 { t = b; b = a % b; a = t }; abs(a)
425
20256
        let mut f = Function::new([(1, ValType::I64)]); // local $t
426
        // params: $a=0, $b=1, local: $t=2
427
20256
        f.instruction(&Instruction::Block(wasm_encoder::BlockType::Empty));
428
20256
        f.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));
429
        // if b == 0, break
430
20256
        f.instruction(&Instruction::LocalGet(1));
431
20256
        f.instruction(&Instruction::I64Eqz);
432
20256
        f.instruction(&Instruction::BrIf(1));
433
        // t = b
434
20256
        f.instruction(&Instruction::LocalGet(1));
435
20256
        f.instruction(&Instruction::LocalSet(2));
436
        // b = a % b
437
20256
        f.instruction(&Instruction::LocalGet(0));
438
20256
        f.instruction(&Instruction::LocalGet(1));
439
20256
        f.instruction(&Instruction::I64RemS);
440
20256
        f.instruction(&Instruction::LocalSet(1));
441
        // a = t
442
20256
        f.instruction(&Instruction::LocalGet(2));
443
20256
        f.instruction(&Instruction::LocalSet(0));
444
20256
        f.instruction(&Instruction::Br(0));
445
20256
        f.instruction(&Instruction::End); // loop
446
20256
        f.instruction(&Instruction::End); // block
447
        // return abs(a)
448
20256
        f.instruction(&Instruction::LocalGet(0));
449
20256
        f.instruction(&Instruction::I64Const(0));
450
20256
        f.instruction(&Instruction::LocalGet(0));
451
20256
        f.instruction(&Instruction::I64Sub);
452
20256
        f.instruction(&Instruction::LocalGet(0));
453
20256
        f.instruction(&Instruction::I64Const(0));
454
20256
        f.instruction(&Instruction::I64LtS);
455
20256
        f.instruction(&Instruction::Select);
456
20256
        f.instruction(&Instruction::End);
457
20256
        self.pending_helpers.push(f);
458
20256
    }
459

            
460
20256
    fn build_ratio_new_body(&mut self) {
461
20256
        let ratio_idx = self.type_idx("ratio");
462
20256
        let gcd_func = self.func("gcd");
463
        // ratio_new(num, denom) -> reduce by GCD, normalize sign
464
        // params: $num=0, $denom=1, locals: $g=2
465
20256
        let mut f = Function::new([(1, ValType::I64)]);
466
        // g = gcd(num, denom)
467
20256
        f.instruction(&Instruction::LocalGet(0));
468
20256
        f.instruction(&Instruction::LocalGet(1));
469
20256
        f.instruction(&Instruction::Call(gcd_func));
470
20256
        f.instruction(&Instruction::LocalSet(2));
471
        // num = num / g
472
20256
        f.instruction(&Instruction::LocalGet(0));
473
20256
        f.instruction(&Instruction::LocalGet(2));
474
20256
        f.instruction(&Instruction::I64DivS);
475
20256
        f.instruction(&Instruction::LocalSet(0));
476
        // denom = denom / g
477
20256
        f.instruction(&Instruction::LocalGet(1));
478
20256
        f.instruction(&Instruction::LocalGet(2));
479
20256
        f.instruction(&Instruction::I64DivS);
480
20256
        f.instruction(&Instruction::LocalSet(1));
481
        // if denom < 0 { num = -num; denom = -denom }
482
20256
        f.instruction(&Instruction::LocalGet(1));
483
20256
        f.instruction(&Instruction::I64Const(0));
484
20256
        f.instruction(&Instruction::I64LtS);
485
20256
        f.instruction(&Instruction::If(wasm_encoder::BlockType::Empty));
486
20256
        f.instruction(&Instruction::I64Const(0));
487
20256
        f.instruction(&Instruction::LocalGet(0));
488
20256
        f.instruction(&Instruction::I64Sub);
489
20256
        f.instruction(&Instruction::LocalSet(0));
490
20256
        f.instruction(&Instruction::I64Const(0));
491
20256
        f.instruction(&Instruction::LocalGet(1));
492
20256
        f.instruction(&Instruction::I64Sub);
493
20256
        f.instruction(&Instruction::LocalSet(1));
494
20256
        f.instruction(&Instruction::End);
495
        // struct.new $ratio (num, denom)
496
20256
        f.instruction(&Instruction::LocalGet(0));
497
20256
        f.instruction(&Instruction::LocalGet(1));
498
20256
        f.instruction(&Instruction::StructNew(ratio_idx));
499
20256
        f.instruction(&Instruction::End);
500
20256
        self.pending_helpers.push(f);
501
20256
    }
502

            
503
20256
    fn build_ratio_binop_bodies(&mut self) {
504
20256
        let ratio_idx = self.type_idx("ratio");
505
20256
        let ratio_new = self.func("ratio_new");
506

            
507
        // ratio_add(a, b): (a.num * b.denom + b.num * a.denom, a.denom * b.denom)
508
20256
        let mut f = Function::new([]);
509
        // a.num * b.denom
510
20256
        f.instruction(&Instruction::LocalGet(0));
511
20256
        f.instruction(&Instruction::StructGet {
512
20256
            struct_type_index: ratio_idx,
513
20256
            field_index: 0,
514
20256
        });
515
20256
        f.instruction(&Instruction::LocalGet(1));
516
20256
        f.instruction(&Instruction::StructGet {
517
20256
            struct_type_index: ratio_idx,
518
20256
            field_index: 1,
519
20256
        });
520
20256
        f.instruction(&Instruction::I64Mul);
521
        // b.num * a.denom
522
20256
        f.instruction(&Instruction::LocalGet(1));
523
20256
        f.instruction(&Instruction::StructGet {
524
20256
            struct_type_index: ratio_idx,
525
20256
            field_index: 0,
526
20256
        });
527
20256
        f.instruction(&Instruction::LocalGet(0));
528
20256
        f.instruction(&Instruction::StructGet {
529
20256
            struct_type_index: ratio_idx,
530
20256
            field_index: 1,
531
20256
        });
532
20256
        f.instruction(&Instruction::I64Mul);
533
20256
        f.instruction(&Instruction::I64Add);
534
        // a.denom * b.denom
535
20256
        f.instruction(&Instruction::LocalGet(0));
536
20256
        f.instruction(&Instruction::StructGet {
537
20256
            struct_type_index: ratio_idx,
538
20256
            field_index: 1,
539
20256
        });
540
20256
        f.instruction(&Instruction::LocalGet(1));
541
20256
        f.instruction(&Instruction::StructGet {
542
20256
            struct_type_index: ratio_idx,
543
20256
            field_index: 1,
544
20256
        });
545
20256
        f.instruction(&Instruction::I64Mul);
546
20256
        f.instruction(&Instruction::Call(ratio_new));
547
20256
        f.instruction(&Instruction::End);
548
20256
        self.pending_helpers.push(f);
549

            
550
        // ratio_sub(a, b): (a.num * b.denom - b.num * a.denom, a.denom * b.denom)
551
20256
        let mut f = Function::new([]);
552
20256
        f.instruction(&Instruction::LocalGet(0));
553
20256
        f.instruction(&Instruction::StructGet {
554
20256
            struct_type_index: ratio_idx,
555
20256
            field_index: 0,
556
20256
        });
557
20256
        f.instruction(&Instruction::LocalGet(1));
558
20256
        f.instruction(&Instruction::StructGet {
559
20256
            struct_type_index: ratio_idx,
560
20256
            field_index: 1,
561
20256
        });
562
20256
        f.instruction(&Instruction::I64Mul);
563
20256
        f.instruction(&Instruction::LocalGet(1));
564
20256
        f.instruction(&Instruction::StructGet {
565
20256
            struct_type_index: ratio_idx,
566
20256
            field_index: 0,
567
20256
        });
568
20256
        f.instruction(&Instruction::LocalGet(0));
569
20256
        f.instruction(&Instruction::StructGet {
570
20256
            struct_type_index: ratio_idx,
571
20256
            field_index: 1,
572
20256
        });
573
20256
        f.instruction(&Instruction::I64Mul);
574
20256
        f.instruction(&Instruction::I64Sub);
575
20256
        f.instruction(&Instruction::LocalGet(0));
576
20256
        f.instruction(&Instruction::StructGet {
577
20256
            struct_type_index: ratio_idx,
578
20256
            field_index: 1,
579
20256
        });
580
20256
        f.instruction(&Instruction::LocalGet(1));
581
20256
        f.instruction(&Instruction::StructGet {
582
20256
            struct_type_index: ratio_idx,
583
20256
            field_index: 1,
584
20256
        });
585
20256
        f.instruction(&Instruction::I64Mul);
586
20256
        f.instruction(&Instruction::Call(ratio_new));
587
20256
        f.instruction(&Instruction::End);
588
20256
        self.pending_helpers.push(f);
589

            
590
        // ratio_mul(a, b): (a.num * b.num, a.denom * b.denom)
591
20256
        let mut f = Function::new([]);
592
20256
        f.instruction(&Instruction::LocalGet(0));
593
20256
        f.instruction(&Instruction::StructGet {
594
20256
            struct_type_index: ratio_idx,
595
20256
            field_index: 0,
596
20256
        });
597
20256
        f.instruction(&Instruction::LocalGet(1));
598
20256
        f.instruction(&Instruction::StructGet {
599
20256
            struct_type_index: ratio_idx,
600
20256
            field_index: 0,
601
20256
        });
602
20256
        f.instruction(&Instruction::I64Mul);
603
20256
        f.instruction(&Instruction::LocalGet(0));
604
20256
        f.instruction(&Instruction::StructGet {
605
20256
            struct_type_index: ratio_idx,
606
20256
            field_index: 1,
607
20256
        });
608
20256
        f.instruction(&Instruction::LocalGet(1));
609
20256
        f.instruction(&Instruction::StructGet {
610
20256
            struct_type_index: ratio_idx,
611
20256
            field_index: 1,
612
20256
        });
613
20256
        f.instruction(&Instruction::I64Mul);
614
20256
        f.instruction(&Instruction::Call(ratio_new));
615
20256
        f.instruction(&Instruction::End);
616
20256
        self.pending_helpers.push(f);
617

            
618
        // ratio_div(a, b): (a.num * b.denom, a.denom * b.num)
619
20256
        let mut f = Function::new([]);
620
20256
        f.instruction(&Instruction::LocalGet(0));
621
20256
        f.instruction(&Instruction::StructGet {
622
20256
            struct_type_index: ratio_idx,
623
20256
            field_index: 0,
624
20256
        });
625
20256
        f.instruction(&Instruction::LocalGet(1));
626
20256
        f.instruction(&Instruction::StructGet {
627
20256
            struct_type_index: ratio_idx,
628
20256
            field_index: 1,
629
20256
        });
630
20256
        f.instruction(&Instruction::I64Mul);
631
20256
        f.instruction(&Instruction::LocalGet(0));
632
20256
        f.instruction(&Instruction::StructGet {
633
20256
            struct_type_index: ratio_idx,
634
20256
            field_index: 1,
635
20256
        });
636
20256
        f.instruction(&Instruction::LocalGet(1));
637
20256
        f.instruction(&Instruction::StructGet {
638
20256
            struct_type_index: ratio_idx,
639
20256
            field_index: 0,
640
20256
        });
641
20256
        f.instruction(&Instruction::I64Mul);
642
20256
        f.instruction(&Instruction::Call(ratio_new));
643
20256
        f.instruction(&Instruction::End);
644
20256
        self.pending_helpers.push(f);
645
20256
    }
646

            
647
20256
    fn build_ratio_cmp_bodies(&mut self) {
648
20256
        let ratio_idx = self.type_idx("ratio");
649

            
650
        // ratio_eq(a, b): a.num * b.denom == b.num * a.denom
651
20256
        let mut f = Function::new([]);
652
20256
        f.instruction(&Instruction::LocalGet(0));
653
20256
        f.instruction(&Instruction::StructGet {
654
20256
            struct_type_index: ratio_idx,
655
20256
            field_index: 0,
656
20256
        });
657
20256
        f.instruction(&Instruction::LocalGet(1));
658
20256
        f.instruction(&Instruction::StructGet {
659
20256
            struct_type_index: ratio_idx,
660
20256
            field_index: 1,
661
20256
        });
662
20256
        f.instruction(&Instruction::I64Mul);
663
20256
        f.instruction(&Instruction::LocalGet(1));
664
20256
        f.instruction(&Instruction::StructGet {
665
20256
            struct_type_index: ratio_idx,
666
20256
            field_index: 0,
667
20256
        });
668
20256
        f.instruction(&Instruction::LocalGet(0));
669
20256
        f.instruction(&Instruction::StructGet {
670
20256
            struct_type_index: ratio_idx,
671
20256
            field_index: 1,
672
20256
        });
673
20256
        f.instruction(&Instruction::I64Mul);
674
20256
        f.instruction(&Instruction::I64Eq);
675
20256
        f.instruction(&Instruction::End);
676
20256
        self.pending_helpers.push(f);
677

            
678
        // ratio_lt(a, b): a.num * b.denom < b.num * a.denom
679
20256
        let mut f = Function::new([]);
680
20256
        f.instruction(&Instruction::LocalGet(0));
681
20256
        f.instruction(&Instruction::StructGet {
682
20256
            struct_type_index: ratio_idx,
683
20256
            field_index: 0,
684
20256
        });
685
20256
        f.instruction(&Instruction::LocalGet(1));
686
20256
        f.instruction(&Instruction::StructGet {
687
20256
            struct_type_index: ratio_idx,
688
20256
            field_index: 1,
689
20256
        });
690
20256
        f.instruction(&Instruction::I64Mul);
691
20256
        f.instruction(&Instruction::LocalGet(1));
692
20256
        f.instruction(&Instruction::StructGet {
693
20256
            struct_type_index: ratio_idx,
694
20256
            field_index: 0,
695
20256
        });
696
20256
        f.instruction(&Instruction::LocalGet(0));
697
20256
        f.instruction(&Instruction::StructGet {
698
20256
            struct_type_index: ratio_idx,
699
20256
            field_index: 1,
700
20256
        });
701
20256
        f.instruction(&Instruction::I64Mul);
702
20256
        f.instruction(&Instruction::I64LtS);
703
20256
        f.instruction(&Instruction::End);
704
20256
        self.pending_helpers.push(f);
705
20256
    }
706

            
707
20256
    fn build_ratio_from_i64_body(&mut self) {
708
20256
        let ratio_idx = self.type_idx("ratio");
709
        // ratio_from_i64(n) -> struct.new $ratio (n, 1)
710
20256
        let mut f = Function::new([]);
711
20256
        f.instruction(&Instruction::LocalGet(0));
712
20256
        f.instruction(&Instruction::I64Const(1));
713
20256
        f.instruction(&Instruction::StructNew(ratio_idx));
714
20256
        f.instruction(&Instruction::End);
715
20256
        self.pending_helpers.push(f);
716
20256
    }
717

            
718
18843
    pub fn add_should_apply(&mut self, f: Function) {
719
18843
        debug!("emitting should_apply function");
720
18843
        self.codes.function(&f);
721

            
722
188430
        for helper in self.pending_helpers.drain(..) {
723
188430
            self.codes.function(&helper);
724
188430
        }
725
18843
    }
726

            
727
18621
    pub fn default_should_apply() -> Function {
728
18621
        let mut f = Function::new([]);
729
18621
        f.instruction(&Instruction::I32Const(1));
730
18621
        f.instruction(&Instruction::End);
731
18621
        f
732
18621
    }
733

            
734
18843
    pub fn add_process(&mut self, f: Function) {
735
18843
        debug!("emitting process function");
736
18843
        self.codes.function(&f);
737
18843
    }
738

            
739
18843
    pub fn finish(self) -> Vec<u8> {
740
18843
        debug!(data_segments = self.data_count, "assembling WASM module");
741
18843
        let mut module = Module::new();
742
18843
        module.section(&self.types);
743
18843
        module.section(&self.imports);
744
18843
        module.section(&self.functions);
745
18843
        module.section(&self.memories);
746
18843
        module.section(&self.exports);
747
18843
        module.section(&DataCountSection {
748
18843
            count: self.data_count,
749
18843
        });
750
18843
        module.section(&self.codes);
751
18843
        module.section(&self.data);
752
18843
        module.finish()
753
18843
    }
754
}
755

            
756
impl Default for CompileContext {
757
    fn default() -> Self {
758
        Self::new()
759
    }
760
}
761

            
762
#[cfg(test)]
763
mod tests {
764
    use super::*;
765

            
766
    #[test]
767
1
    fn test_ratio_type_registered() {
768
1
        let ctx = CompileContext::new();
769
1
        assert!(ctx.type_names.contains_key("ratio"));
770
1
        assert!(ctx.type_names.contains_key("i8_array"));
771
1
    }
772

            
773
    #[test]
774
1
    fn test_ratio_helpers_registered() {
775
1
        let ctx = CompileContext::new();
776
9
        for name in [
777
1
            "gcd",
778
1
            "ratio_new",
779
1
            "ratio_add",
780
1
            "ratio_sub",
781
1
            "ratio_mul",
782
1
            "ratio_div",
783
1
            "ratio_eq",
784
1
            "ratio_lt",
785
1
            "ratio_from_i64",
786
1
        ] {
787
9
            assert!(
788
9
                ctx.func_names.contains_key(name),
789
                "missing helper function: {name}"
790
            );
791
        }
792
1
    }
793

            
794
    #[test]
795
1
    fn test_host_imports_registered() {
796
1
        let ctx = CompileContext::new();
797
10
        for name in [
798
1
            "get_output_offset",
799
1
            "get_input_offset",
800
1
            "get_strings_offset",
801
1
            "get_input_entities_count",
802
1
            "get_timestamp",
803
1
            "generate_uuid",
804
1
            "write_string",
805
1
            "write_bytes",
806
1
            "symbol_resolve",
807
1
            "log",
808
1
        ] {
809
10
            assert!(
810
10
                ctx.func_names.contains_key(name),
811
                "missing host import: {name}"
812
            );
813
        }
814
1
    }
815

            
816
    #[test]
817
1
    fn test_ratio_ref_type() {
818
1
        let ctx = CompileContext::new();
819
1
        let ratio_ref = ctx.ratio_ref();
820
1
        match ratio_ref {
821
1
            ValType::Ref(r) => {
822
1
                assert!(r.nullable);
823
1
                assert_eq!(r.heap_type, HeapType::Concrete(ctx.type_idx("ratio")));
824
            }
825
            _ => panic!("expected Ref type"),
826
        }
827
1
    }
828

            
829
    #[test]
830
1
    fn test_valid_wasm_with_ratio_helpers() {
831
1
        let mut ctx = CompileContext::new();
832
1
        ctx.add_should_apply(CompileContext::default_should_apply());
833
1
        let mut f = Function::new([]);
834
1
        f.instruction(&Instruction::End);
835
1
        ctx.add_process(f);
836
1
        let wasm = ctx.finish();
837
1
        assert_eq!(&wasm[0..4], b"\0asm");
838
1
    }
839
}