jetcrab\vm\executor\instruction_handlers/control_flow.rs
1//! # Control Flow Handler
2//!
3//! Handles all control flow operations in the VM including jumps, function calls,
4//! returns, and exception handling.
5//!
6//! ## Operations Supported
7//!
8//! - **Jumps**: jump, jump_if_true, jump_if_false
9//! - **Function Calls**: call, return_from_function
10//! - **Scope Management**: create_scope, exit_scope
11//! - **Exception Handling**: throw, try_catch, finally
12//! - **Loop Control**: break_statement, continue_statement
13//! - **Switch Statements**: switch_statement, case_statement, default_case
14//!
15//! ## Control Flow Semantics
16//!
17//! - **Jumps**: Modify the program counter to change execution flow
18//! - **Function Calls**: Set up new execution context with arguments
19//! - **Returns**: Restore previous execution context
20//! - **Exception Handling**: Manage try-catch-finally blocks
21//!
22//! ## Usage
23//!
24//! ```rust
25//! use jetcrab::vm::executor::instruction_handlers::ControlFlowHandler;
26//! use jetcrab::vm::executor::traits::{StackOperations, VariableManager};
27//!
28//! let mut stack = MyStack::new();
29//! let mut registers = MyRegisters::new();
30//! let mut frame = MyFrame::new();
31//!
32//! ControlFlowHandler::jump(&mut stack, &mut registers, 100)?;
33//! ```
34
35use crate::vm::executor::error_handler::ExecutionError;
36use crate::vm::executor::traits::{StackOperations, VariableManager};
37use crate::vm::frame::Frame;
38use crate::vm::registers::Registers;
39use crate::vm::types::{CodeAddress, ArgIndex};
40use crate::vm::value::Value;
41
42/// Handles control flow operations for the VM
43pub struct ControlFlowHandler;
44
45impl ControlFlowHandler {
46 /// Performs an unconditional jump to a target address
47 ///
48 /// Sets the program counter to the target address, effectively
49 /// changing the execution flow.
50 ///
51 /// # Arguments
52 /// * `stack` - The stack to operate on
53 /// * `registers` - The VM registers containing the program counter
54 /// * `target_ip` - The target instruction pointer to jump to
55 ///
56 /// # Returns
57 /// * `Ok(usize)` with the new instruction pointer on success
58 /// * `Err(ExecutionError)` on failure
59 ///
60 /// # Examples
61 ///
62 /// ```rust
63 /// let mut stack = MyStack::new();
64 /// let mut registers = MyRegisters::new();
65 /// let new_ip = ControlFlowHandler::jump(&mut stack, &mut registers, 100)?;
66 /// assert_eq!(new_ip, 100);
67 /// ```
68 pub fn jump<S, V>(
69 _stack: &mut S,
70 _registers: &mut Registers,
71 target_ip: CodeAddress,
72 ) -> Result<usize, ExecutionError>
73 where
74 S: StackOperations,
75 V: VariableManager,
76 {
77 let new_ip = usize::from(target_ip);
78 Ok(new_ip)
79 }
80
81 /// Performs a conditional jump if the top stack value is true
82 ///
83 /// Pops a value from the stack and jumps to the target address
84 /// if the value is truthy.
85 ///
86 /// # Arguments
87 /// * `stack` - The stack to operate on
88 /// * `registers` - The VM registers
89 /// * `target_ip` - The target instruction pointer to jump to
90 ///
91 /// # Returns
92 /// * `Ok(usize)` with the new instruction pointer if jump occurs
93 /// * `Ok(usize)` with current IP + 1 if no jump occurs
94 /// * `Err(ExecutionError)` on failure
95 pub fn jump_if_true<S, V>(
96 stack: &mut S,
97 _registers: &mut Registers,
98 target_ip: CodeAddress,
99 ) -> Result<usize, ExecutionError>
100 where
101 S: StackOperations,
102 V: VariableManager,
103 {
104 let condition = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
105
106 let should_jump = match condition {
107 Value::Boolean(b) => b,
108 Value::Number(n) => n != 0.0 && !n.is_nan(),
109 Value::String(s) => !s.is_empty(),
110 Value::Null | Value::Undefined => false,
111 _ => true,
112 };
113
114 if should_jump {
115 Ok(usize::from(target_ip))
116 } else {
117 Ok(0)
118 }
119 }
120
121 /// Performs a conditional jump if the top stack value is false
122 ///
123 /// Pops a value from the stack and jumps to the target address
124 /// if the value is falsy.
125 ///
126 /// # Arguments
127 /// * `stack` - The stack to operate on
128 /// * `registers` - The VM registers
129 /// * `target_ip` - The target instruction pointer to jump to
130 ///
131 /// # Returns
132 /// * `Ok(usize)` with the new instruction pointer if jump occurs
133 /// * `Ok(usize)` with current IP + 1 if no jump occurs
134 /// * `Err(ExecutionError)` on failure
135 pub fn jump_if_false<S, V>(
136 stack: &mut S,
137 _registers: &mut Registers,
138 target_ip: CodeAddress,
139 ) -> Result<usize, ExecutionError>
140 where
141 S: StackOperations,
142 V: VariableManager,
143 {
144 let condition = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
145
146 let should_jump = match condition {
147 Value::Boolean(b) => !b,
148 Value::Number(n) => n == 0.0 || n.is_nan(),
149 Value::String(s) => s.is_empty(),
150 Value::Null | Value::Undefined => true,
151 _ => false,
152 };
153
154 if should_jump {
155 Ok(usize::from(target_ip))
156 } else {
157 Ok(0)
158 }
159 }
160
161 /// Sets up a function call with arguments
162 ///
163 /// Pops the function and arguments from the stack and sets up
164 /// the frame for function execution.
165 ///
166 /// # Arguments
167 /// * `stack` - The stack to operate on
168 /// * `registers` - The VM registers
169 /// * `frame` - The current execution frame
170 /// * `arg_count` - The number of arguments to pop from stack
171 ///
172 /// # Returns
173 /// * `Ok(())` on success
174 /// * `Err(ExecutionError)` on failure
175 pub fn call<S, V>(
176 stack: &mut S,
177 _registers: &mut Registers,
178 frame: &mut Frame,
179 arg_count: ArgIndex,
180 ) -> Result<(), ExecutionError>
181 where
182 S: StackOperations,
183 V: VariableManager,
184 {
185 let function_value = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
186
187 match function_value {
188 Value::Function(function_handle) => {
189 let mut args = Vec::new();
190 for _ in 0..usize::from(arg_count) {
191 args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
192 }
193 args.reverse();
194
195 frame.arguments = args;
196 frame.function_handle = Some(function_handle);
197 Ok(())
198 }
199 _ => Err(ExecutionError::RuntimeError("Cannot call non-function value".to_string())),
200 }
201 }
202
203 /// Returns from the current function
204 ///
205 /// Restores the previous execution context and returns control
206 /// to the calling function.
207 ///
208 /// # Arguments
209 /// * `stack` - The stack to operate on
210 /// * `registers` - The VM registers
211 /// * `frame` - The current execution frame
212 ///
213 /// # Returns
214 /// * `Ok(usize)` with the return address on success
215 /// * `Err(ExecutionError)` on failure
216 pub fn return_from_function<S, V>(
217 _stack: &mut S,
218 _registers: &mut Registers,
219 _frame: &mut Frame,
220 ) -> Result<usize, ExecutionError>
221 where
222 S: StackOperations,
223 V: VariableManager,
224 {
225 let return_address = usize::from(_registers.program_counter);
226 Ok(return_address)
227 }
228
229 /// Creates a new scope for variable declarations
230 ///
231 /// Sets up a new variable scope for block execution.
232 ///
233 /// # Arguments
234 /// * `stack` - The stack to operate on
235 /// * `frame` - The current execution frame
236 ///
237 /// # Returns
238 /// * `Ok(())` on success
239 /// * `Err(ExecutionError)` on failure
240 pub fn create_scope<S, V>(_stack: &mut S, _frame: &mut Frame) -> Result<(), ExecutionError>
241 where
242 S: StackOperations,
243 V: VariableManager,
244 {
245 Ok(())
246 }
247
248 /// Exits the current scope
249 ///
250 /// Cleans up the current variable scope.
251 ///
252 /// # Arguments
253 /// * `stack` - The stack to operate on
254 /// * `frame` - The current execution frame
255 ///
256 /// # Returns
257 /// * `Ok(())` on success
258 /// * `Err(ExecutionError)` on failure
259 pub fn exit_scope<S, V>(_stack: &mut S, _frame: &mut Frame) -> Result<(), ExecutionError>
260 where
261 S: StackOperations,
262 V: VariableManager,
263 {
264 Ok(())
265 }
266
267 /// Throws an exception
268 ///
269 /// Pops a value from the stack and throws it as an exception,
270 /// interrupting normal execution flow.
271 ///
272 /// # Arguments
273 /// * `stack` - The stack to operate on
274 /// * `registers` - The VM registers
275 ///
276 /// # Returns
277 /// * `Ok(())` on success
278 /// * `Err(ExecutionError)` on failure
279 pub fn throw<S, V>(stack: &mut S, _registers: &mut Registers) -> Result<(), ExecutionError>
280 where
281 S: StackOperations,
282 V: VariableManager,
283 {
284 let exception = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
285 Err(ExecutionError::RuntimeError(format!("Exception thrown: {:?}", exception)))
286 }
287
288 /// Sets up a try-catch block
289 ///
290 /// Prepares the VM for exception handling with try and catch blocks.
291 ///
292 /// # Arguments
293 /// * `stack` - The stack to operate on
294 /// * `registers` - The VM registers
295 /// * `frame` - The current execution frame
296 /// * `try_block_size` - Size of the try block
297 /// * `catch_block_size` - Size of the catch block
298 ///
299 /// # Returns
300 /// * `Ok(usize)` with the new instruction pointer on success
301 /// * `Err(ExecutionError)` on failure
302 pub fn try_catch<S, V>(
303 _stack: &mut S,
304 _registers: &mut Registers,
305 _frame: &mut Frame,
306 try_block_size: CodeAddress,
307 catch_block_size: CodeAddress,
308 ) -> Result<usize, ExecutionError>
309 where
310 S: StackOperations,
311 V: VariableManager,
312 {
313 let new_ip = usize::from(try_block_size) + usize::from(catch_block_size);
314 Ok(new_ip)
315 }
316
317 /// Executes the finally block
318 ///
319 /// Handles cleanup code that should run regardless of exception handling.
320 ///
321 /// # Arguments
322 /// * `stack` - The stack to operate on
323 /// * `registers` - The VM registers
324 /// * `frame` - The current execution frame
325 ///
326 /// # Returns
327 /// * `Ok(usize)` with the new instruction pointer on success
328 /// * `Err(ExecutionError)` on failure
329 pub fn finally<S, V>(
330 _stack: &mut S,
331 _registers: &mut Registers,
332 _frame: &mut Frame,
333 ) -> Result<usize, ExecutionError>
334 where
335 S: StackOperations,
336 V: VariableManager,
337 {
338 Ok(0)
339 }
340
341 /// Breaks out of a loop or switch statement
342 ///
343 /// Exits the current loop or switch statement and continues
344 /// execution at the specified target.
345 ///
346 /// # Arguments
347 /// * `stack` - The stack to operate on
348 /// * `registers` - The VM registers
349 /// * `frame` - The current execution frame
350 /// * `target_label` - The target label to jump to
351 ///
352 /// # Returns
353 /// * `Ok(usize)` with the target instruction pointer on success
354 /// * `Err(ExecutionError)` on failure
355 pub fn break_statement<S, V>(
356 _stack: &mut S,
357 _registers: &mut Registers,
358 _frame: &mut Frame,
359 target_label: String,
360 ) -> Result<usize, ExecutionError>
361 where
362 S: StackOperations,
363 V: VariableManager,
364 {
365 let target_ip = target_label.parse::<usize>().unwrap_or(0);
366 Ok(target_ip)
367 }
368
369 /// Continues to the next iteration of a loop
370 ///
371 /// Skips the rest of the current loop iteration and continues
372 /// with the next iteration.
373 ///
374 /// # Arguments
375 /// * `stack` - The stack to operate on
376 /// * `registers` - The VM registers
377 /// * `frame` - The current execution frame
378 /// * `target_label` - The target label to jump to
379 ///
380 /// # Returns
381 /// * `Ok(usize)` with the target instruction pointer on success
382 /// * `Err(ExecutionError)` on failure
383 pub fn continue_statement<S, V>(
384 _stack: &mut S,
385 _registers: &mut Registers,
386 _frame: &mut Frame,
387 target_label: String,
388 ) -> Result<usize, ExecutionError>
389 where
390 S: StackOperations,
391 V: VariableManager,
392 {
393 let target_ip = target_label.parse::<usize>().unwrap_or(0);
394 Ok(target_ip + 1)
395 }
396
397 /// Sets up a switch statement
398 ///
399 /// Prepares the VM for switch statement execution.
400 ///
401 /// # Arguments
402 /// * `stack` - The stack to operate on
403 /// * `registers` - The VM registers
404 /// * `frame` - The current execution frame
405 /// * `case_count` - The number of cases in the switch
406 ///
407 /// # Returns
408 /// * `Ok(())` on success
409 /// * `Err(ExecutionError)` on failure
410 pub fn switch_statement<S, V>(
411 _stack: &mut S,
412 _registers: &mut Registers,
413 _frame: &mut Frame,
414 _case_count: CodeAddress,
415 ) -> Result<(), ExecutionError>
416 where
417 S: StackOperations,
418 V: VariableManager,
419 {
420 Ok(())
421 }
422
423 /// Handles a case in a switch statement
424 ///
425 /// Compares the switch value with a case value and jumps
426 /// to the target if they match.
427 ///
428 /// # Arguments
429 /// * `stack` - The stack to operate on
430 /// * `registers` - The VM registers
431 /// * `frame` - The current execution frame
432 /// * `case_value` - The value to compare against
433 /// * `target_ip` - The target instruction pointer to jump to
434 ///
435 /// # Returns
436 /// * `Ok(usize)` with the target instruction pointer on success
437 /// * `Err(ExecutionError)` on failure
438 pub fn case_statement<S, V>(
439 _stack: &mut S,
440 _registers: &mut Registers,
441 _frame: &mut Frame,
442 _case_value: Value,
443 target_ip: CodeAddress,
444 ) -> Result<usize, ExecutionError>
445 where
446 S: StackOperations,
447 V: VariableManager,
448 {
449 Ok(usize::from(target_ip))
450 }
451
452 /// Handles the default case in a switch statement
453 ///
454 /// Jumps to the default case target when no other cases match.
455 ///
456 /// # Arguments
457 /// * `stack` - The stack to operate on
458 /// * `registers` - The VM registers
459 /// * `frame` - The current execution frame
460 /// * `target_ip` - The target instruction pointer to jump to
461 ///
462 /// # Returns
463 /// * `Ok(usize)` with the target instruction pointer on success
464 /// * `Err(ExecutionError)` on failure
465 pub fn default_case<S, V>(
466 _stack: &mut S,
467 _registers: &mut Registers,
468 _frame: &mut Frame,
469 target_ip: CodeAddress,
470 ) -> Result<usize, ExecutionError>
471 where
472 S: StackOperations,
473 V: VariableManager,
474 {
475 Ok(usize::from(target_ip))
476 }
477}