swamp_script_vm_test/
util.rs

1use seq_map::SeqMap;
2use swamp_script_code_gen::alloc::ConstantMemoryRegion;
3use swamp_script_code_gen::alloc_util::type_size_and_alignment;
4use swamp_script_code_gen::{CodeGenState, Error, GenOptions};
5use swamp_script_compile::Program;
6use swamp_script_compile::compile_string;
7use swamp_script_semantic::{ConstantId, Function};
8use swamp_script_types::Type;
9use swamp_vm::host::HostArgs;
10use swamp_vm::{Vm, VmSetup};
11use swamp_vm_disasm::{disasm_instructions_color, disasm_instructions_no_color};
12use swamp_vm_types::ConstantMemoryAddress;
13use tracing::info;
14
15pub fn execute_constants(
16    code_gen_state: &mut CodeGenState,
17    vm: &mut Vm,
18) -> Result<SeqMap<ConstantId, ConstantMemoryRegion>, Error> {
19    let mut constant_offsets = SeqMap::default();
20
21    for (constant_id, constant_info) in code_gen_state.constant_functions() {
22        let (size, _alignment) = type_size_and_alignment(&constant_info.constant_ref.resolved_type);
23
24        vm.execute_from_ip(&constant_info.ip);
25
26        let constant_offset = vm.allocate_constant(size.0 as usize);
27        let constant_region = ConstantMemoryRegion {
28            addr: ConstantMemoryAddress(constant_offset),
29            size,
30        };
31        constant_offsets
32            .insert(*constant_id, constant_region)
33            .unwrap();
34    }
35
36    Ok(constant_offsets)
37}
38
39fn gen_internal(code: &str) -> Result<(CodeGenState, Program), Error> {
40    let (program, main_module, _source_map) = compile_string(code).unwrap();
41
42    let mut code_gen = CodeGenState::new();
43    code_gen.gen_constants_in_order(&program.state.constants_in_dependency_order)?;
44    //let constants = execute_constants(&program.state)?;
45
46    let debug_expr = main_module.main_expression.as_ref().unwrap();
47    info!(?debug_expr, "MAIN EXPRESSION");
48    let main_expression = main_module.main_expression.as_ref().unwrap();
49    let halt_function = GenOptions {
50        is_halt_function: true,
51    };
52
53    code_gen.gen_main_function(main_expression, &halt_function)?;
54
55    let normal_function = GenOptions {
56        is_halt_function: false,
57    };
58
59    for internal_function_def in &main_module.symbol_table.internal_functions() {
60        code_gen.gen_function_def(internal_function_def, &normal_function)?;
61    }
62
63    for (associated_on_type, impl_functions) in
64        &program.state.instantiator.associated_impls.functions
65    {
66        if !associated_on_type.is_concrete() {
67            continue;
68        }
69        if associated_on_type == &Type::Int
70            || associated_on_type == &Type::Float
71            || associated_on_type == &Type::Bool
72            || associated_on_type == &Type::String
73        {
74            continue;
75        }
76
77        for (_name, func) in &impl_functions.functions {
78            if func.name().clone().starts_with("instantiated ") {
79                continue;
80            }
81            match &**func {
82                Function::Internal(int_fn) => {
83                    code_gen.gen_function_def(int_fn, &normal_function)?;
84                }
85
86                Function::External(_ext_fn) => {}
87            }
88        }
89    }
90
91    code_gen.finalize();
92
93    Ok((code_gen, program))
94}
95
96fn gen_internal_debug(code: &str) -> Result<(CodeGenState, Program), Error> {
97    let (code_gen, program) = gen_internal(code)?;
98    let disassembler_output = disasm_instructions_color(
99        code_gen.instructions(),
100        code_gen.comments(),
101        &code_gen.create_function_sections(),
102    );
103
104    eprintln!("{disassembler_output}");
105
106    Ok((code_gen, program))
107}
108
109fn exec_code_gen_state(code_gen_state: CodeGenState) -> Vm {
110    let (instructions, constants) = code_gen_state.take_instructions_and_constants();
111
112    let setup = VmSetup {
113        frame_memory_size: 1024,
114        heap_memory_size: 1024,
115        constant_memory: constants,
116    };
117    let mut vm = Vm::new(instructions, setup);
118
119    vm.execute();
120
121    vm
122}
123
124pub fn exec_internal(code: &str) -> Result<Vm, Error> {
125    let (code_gen, _program) = gen_internal_debug(code)?;
126
127    Ok(exec_code_gen_state(code_gen))
128}
129
130fn exec_internal_debug(code: &str) -> Result<Vm, Error> {
131    exec_internal(code)
132}
133
134fn trim_lines(text: &str) -> String {
135    text.lines()
136        .map(|line| {
137            // Ignore comments that starts with ;
138            line.split(';').next().unwrap_or("").trim()
139        })
140        .filter(|line| !line.is_empty())
141        .collect::<Vec<_>>()
142        .join("\n")
143}
144
145fn compare_line_outputs(encountered: &str, expected: &str) {
146    let encountered_trimmed = trim_lines(encountered);
147    let expected_trimmed = trim_lines(expected);
148
149    eprintln!("{encountered}");
150    assert_eq!(encountered_trimmed, expected_trimmed);
151}
152
153fn compare_hex_outputs(memory: &[u8], expected_hex: &str) {
154    let encountered_hexed = hexify::format_hex(memory);
155    let expected_hex_trimmed = expected_hex.trim();
156
157    compare_line_outputs(&encountered_hexed, expected_hex_trimmed);
158}
159
160fn exec(code: &str, expected_hex: &str) {
161    let vm = exec_internal_debug(code).expect("should work");
162
163    compare_hex_outputs(&vm.stack_memory()[..16], expected_hex);
164}
165
166pub fn exec_with_assembly(code: &str, expected_assembly: &str, expected_hex: &str) {
167    let (generator, _program) = gen_internal_debug(code).expect("should work");
168
169    let disassembler_output = disasm_instructions_no_color(
170        generator.instructions(),
171        generator.comments(),
172        &generator.create_function_sections(),
173        false,
174    );
175    compare_line_outputs(&disassembler_output, expected_assembly);
176
177    let vm = exec_code_gen_state(generator);
178
179    compare_hex_outputs(&vm.frame_memory()[..16], expected_hex);
180}
181
182/// # Panics
183///
184pub fn exec_with_host_function<F>(
185    code: &str,
186    expected_assembly: &str,
187    expected_hex: &str,
188    id: &str,
189    callback: F,
190) where
191    F: 'static + FnMut(HostArgs),
192{
193    let (generator, program) = gen_internal_debug(code).expect("should work");
194
195    let disassembler_output = disasm_instructions_no_color(
196        generator.instructions(),
197        generator.comments(),
198        &generator.create_function_sections(),
199        false,
200    );
201    compare_line_outputs(&disassembler_output, expected_assembly);
202
203    let module = program
204        .modules
205        .get(&["crate".to_string(), "test".to_string()])
206        .unwrap();
207
208    let external_id = module
209        .symbol_table
210        .get_external_function_declaration(id)
211        .unwrap();
212
213    let (instructions, constants) = generator.take_instructions_and_constants();
214    let setup = VmSetup {
215        frame_memory_size: 1024,
216        heap_memory_size: 1024,
217        constant_memory: constants,
218    };
219    let mut vm = Vm::new(instructions, setup);
220
221    vm.add_host_function(external_id.id as u16, callback);
222
223    vm.execute();
224
225    compare_hex_outputs(&vm.stack_memory()[..16], expected_hex);
226}
227
228fn exec_vars(code: &str, expected_hex: &str) {
229    let vm = exec_internal_debug(code);
230
231    compare_hex_outputs(&vm.unwrap().stack_memory()[..16], expected_hex);
232}
233
234fn gen_code(code: &str, expected_output: &str) {
235    let (generator, _program) = gen_internal_debug(code).expect("should work");
236
237    let disassembler_output = disasm_instructions_no_color(
238        generator.instructions(),
239        generator.comments(),
240        &generator.create_function_sections(),
241        false,
242    );
243
244    compare_line_outputs(&disassembler_output, expected_output);
245}
246
247pub fn exec_show_constants(code: &str, expected_hex: &str, expected_constants: &str) {
248    let vm = exec_internal_debug(code).unwrap();
249
250    compare_hex_outputs(&vm.stack_memory()[..16], expected_hex);
251    compare_hex_outputs(&vm.memory()[0xFFF0..], expected_constants);
252}