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