1use {
2 super::{ExecutionResult, Vm},
3 crate::{errors::ExecutionError, inst_param::Number, instruction::Instruction},
4};
5
6pub fn execute_call_immediate(vm: &mut dyn Vm, inst: &Instruction) -> ExecutionResult<()> {
7 match &inst.imm {
8 Some(either::Either::Left(syscall_name)) => {
9 let result = vm.handle_syscall(syscall_name)?;
10 vm.set_register(0, result);
11 vm.advance_pc();
12 Ok(())
13 }
14 Some(either::Either::Right(Number::Int(target))) => {
15 if vm.get_call_depth() >= vm.max_call_depth() {
16 return Err(ExecutionError::CallDepthExceeded(vm.max_call_depth()));
17 }
18 let saved_registers = [
19 vm.get_register(6),
20 vm.get_register(7),
21 vm.get_register(8),
22 vm.get_register(9),
23 ];
24 let saved_frame_pointer = vm.get_register(10);
25 let return_pc = vm.get_pc() + 1;
26 vm.push_frame(return_pc, saved_registers, saved_frame_pointer)?;
27 vm.set_register(
28 10,
29 saved_frame_pointer.wrapping_add(vm.get_stack_frame_size()),
30 );
31 let target_pc = ((vm.get_pc() as i64) + 1 + *target) as usize;
32 vm.set_pc(target_pc);
33 Ok(())
34 }
35 _ => Err(ExecutionError::InvalidOperand),
36 }
37}
38
39pub fn execute_call_register(vm: &mut dyn Vm, inst: &Instruction) -> ExecutionResult<()> {
40 let reg_num = match &inst.dst {
41 Some(reg) => reg.n as usize,
42 _ => return Err(ExecutionError::InvalidOperand),
43 };
44 if reg_num >= 10 {
45 return Err(ExecutionError::InvalidOperand);
46 }
47 let target = vm.get_register(reg_num) as usize;
48
49 if vm.get_call_depth() >= vm.max_call_depth() {
50 return Err(ExecutionError::CallDepthExceeded(vm.max_call_depth()));
51 }
52
53 let saved_registers = [
54 vm.get_register(6),
55 vm.get_register(7),
56 vm.get_register(8),
57 vm.get_register(9),
58 ];
59 let saved_frame_pointer = vm.get_register(10);
60 let return_pc = vm.get_pc() + 1;
61 vm.push_frame(return_pc, saved_registers, saved_frame_pointer)?;
62 vm.set_register(
63 10,
64 saved_frame_pointer.wrapping_add(vm.get_stack_frame_size()),
65 );
66 vm.set_pc(target);
67 Ok(())
68}
69
70pub fn execute_exit(vm: &mut dyn Vm, _inst: &Instruction) -> ExecutionResult<()> {
71 if let Some((return_pc, saved_registers, saved_frame_pointer)) = vm.pop_frame() {
72 vm.set_register(6, saved_registers[0]);
73 vm.set_register(7, saved_registers[1]);
74 vm.set_register(8, saved_registers[2]);
75 vm.set_register(9, saved_registers[3]);
76 vm.set_register(10, saved_frame_pointer);
77 vm.set_pc(return_pc);
78 Ok(())
79 } else {
80 vm.halt(vm.get_register(0));
81 Ok(())
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use {
88 super::*,
89 crate::{
90 errors::ExecutionError,
91 execute::{MockVm, make_test_instruction},
92 inst_param::{Number, Register},
93 opcode::Opcode,
94 },
95 either::Either,
96 };
97
98 #[test]
99 fn test_syscall() {
100 let inst = make_test_instruction(
102 Opcode::Call,
103 None,
104 None,
105 None,
106 Some(Either::Left("sol_log_".to_string())),
107 );
108 let mut vm = MockVm::new();
109
110 execute_call_immediate(&mut vm, &inst).unwrap();
111
112 assert_eq!(vm.pc, 1);
113 assert_eq!(vm.syscall_logs.len(), 1);
114 assert_eq!(vm.syscall_logs[0], "sol_log_");
115 }
116
117 #[test]
118 fn test_internal_call() {
119 let inst = make_test_instruction(
121 Opcode::Call,
122 None,
123 None,
124 None,
125 Some(Either::Right(Number::Int(10))),
126 );
127 let mut vm = MockVm::new();
128 vm.registers[6] = 100;
129 vm.registers[7] = 200;
130
131 execute_call_immediate(&mut vm, &inst).unwrap();
132
133 assert_eq!(vm.pc, 11); assert_eq!(vm.call_stack.len(), 1);
135 assert_eq!(vm.call_stack[0].0, 1);
136 assert_eq!(vm.call_stack[0].1[0], 100); assert_eq!(vm.call_stack[0].1[1], 200); }
139
140 #[test]
141 fn test_internal_call_depth_exceeded() {
142 let inst = make_test_instruction(
144 Opcode::Call,
145 None,
146 None,
147 None,
148 Some(Either::Right(Number::Int(10))),
149 );
150
151 let mut vm = MockVm::new();
152 vm.call_depth_limit = 1;
153
154 execute_call_immediate(&mut vm, &inst).unwrap();
156 assert_eq!(vm.call_stack.len(), 1);
157
158 let result = execute_call_immediate(&mut vm, &inst);
160 assert!(matches!(result, Err(ExecutionError::CallDepthExceeded(1))));
161 }
162
163 #[test]
164 fn test_callx() {
165 let inst = make_test_instruction(Opcode::Callx, Some(Register { n: 5 }), None, None, None);
167 let mut vm = MockVm::new();
168 vm.registers[5] = 20; vm.registers[6] = 100;
170 vm.registers[7] = 200;
171
172 execute_call_register(&mut vm, &inst).unwrap();
173
174 assert_eq!(vm.pc, 20); assert_eq!(vm.call_stack.len(), 1);
176 assert_eq!(vm.call_stack[0].0, 1);
177 assert_eq!(vm.call_stack[0].1[0], 100); assert_eq!(vm.call_stack[0].1[1], 200); }
180
181 #[test]
182 fn test_callx_r10_invalid() {
183 let inst = make_test_instruction(Opcode::Callx, Some(Register { n: 10 }), None, None, None);
185 let mut vm = MockVm::new();
186
187 let result = execute_call_register(&mut vm, &inst);
188 assert!(matches!(result, Err(ExecutionError::InvalidOperand)));
189 }
190
191 #[test]
192 fn test_callx_depth_exceeded() {
193 let inst = make_test_instruction(Opcode::Callx, Some(Register { n: 5 }), None, None, None);
195 let mut vm = MockVm::new();
196 vm.registers[5] = 0;
197 vm.call_depth_limit = 1;
198
199 execute_call_register(&mut vm, &inst).unwrap();
201 assert_eq!(vm.call_stack.len(), 1);
202
203 let result = execute_call_register(&mut vm, &inst);
205 assert!(matches!(result, Err(ExecutionError::CallDepthExceeded(1))));
206 }
207
208 #[test]
209 fn test_exit_halt() {
210 let inst = make_test_instruction(Opcode::Exit, None, None, None, None);
212 let mut vm = MockVm::new();
213 vm.registers[0] = 42; execute_exit(&mut vm, &inst).unwrap();
216
217 assert!(vm.halted);
218 assert_eq!(vm.exit_code, Some(42));
219 }
220
221 #[test]
222 fn test_exit_return() {
223 let inst = make_test_instruction(Opcode::Exit, None, None, None, None);
225 let mut vm = MockVm::new();
226
227 vm.call_stack.push((5, [100, 200, 300, 400], 0x1000));
229 vm.registers[0] = 99;
230
231 execute_exit(&mut vm, &inst).unwrap();
232
233 assert!(!vm.halted);
234 assert_eq!(vm.pc, 5); assert_eq!(vm.registers[6], 100);
236 assert_eq!(vm.registers[7], 200);
237 assert_eq!(vm.registers[8], 300);
238 assert_eq!(vm.registers[9], 400);
239 assert_eq!(vm.registers[10], 0x1000);
240 }
241}