Skip to main content

avm_rs/opcodes/
function.rs

1//! Function control opcodes
2
3use crate::error::AvmResult;
4use crate::vm::EvalContext;
5
6/// Function prototype declaration
7pub fn op_proto(ctx: &mut EvalContext) -> AvmResult<()> {
8    ctx.advance_pc(1)?; // advance past opcode
9    let args = ctx.read_bytes(1)?[0] as usize;
10    ctx.advance_pc(1)?; // advance past immediate
11    let returns = ctx.read_bytes(1)?[0] as usize;
12    ctx.advance_pc(1)?; // advance past immediate
13
14    // Proto just declares function signature, doesn't execute anything
15    // Store the function prototype information in context
16    ctx.set_function_prototype(args, returns)?;
17
18    Ok(())
19}
20
21/// Frame dig - access value from function frame
22pub fn op_frame_dig(ctx: &mut EvalContext) -> AvmResult<()> {
23    ctx.advance_pc(1)?; // advance past opcode
24    let depth = ctx.read_bytes(1)?[0] as i8;
25    ctx.advance_pc(1)?; // advance past immediate
26
27    let value = ctx.frame_dig(depth)?;
28    ctx.push(value)?;
29
30    Ok(())
31}
32
33/// Frame bury - store value in function frame
34pub fn op_frame_bury(ctx: &mut EvalContext) -> AvmResult<()> {
35    ctx.advance_pc(1)?; // advance past opcode
36    let depth = ctx.read_bytes(1)?[0] as i8;
37    ctx.advance_pc(1)?; // advance past immediate
38
39    let value = ctx.pop()?;
40    ctx.frame_bury(depth, value)?;
41
42    Ok(())
43}
44
45/// Switch statement - jump to one of many targets
46pub fn op_switch(ctx: &mut EvalContext) -> AvmResult<()> {
47    ctx.advance_pc(1)?; // advance past opcode
48
49    // Read number of targets
50    let num_targets = ctx.read_bytes(1)?[0] as usize;
51    ctx.advance_pc(1)?; // advance past count
52
53    // Read all target offsets
54    let mut targets = Vec::new();
55    for _ in 0..num_targets {
56        let target_bytes = ctx.read_bytes(2)?;
57        let target = i16::from_be_bytes([target_bytes[0], target_bytes[1]]);
58        targets.push(target);
59        ctx.advance_pc(2)?;
60    }
61
62    // Pop the switch value
63    let switch_value = ctx.pop()?;
64    let index = switch_value.as_uint()? as usize;
65
66    // Jump to target or fall through
67    if index < targets.len() {
68        let target = targets[index];
69        ctx.branch(target)?;
70    }
71    // If index is out of bounds, just continue (fall through)
72
73    Ok(())
74}
75
76/// Match statement - like switch but matches specific values
77pub fn op_match(ctx: &mut EvalContext) -> AvmResult<()> {
78    ctx.advance_pc(1)?; // advance past opcode
79
80    // Read number of match cases
81    let num_cases = ctx.read_bytes(1)?[0] as usize;
82    ctx.advance_pc(1)?; // advance past count
83
84    // Read all match values and targets
85    let mut cases = Vec::new();
86    for _ in 0..num_cases {
87        // Read match value (8 bytes for u64)
88        let value_bytes = ctx.read_bytes(8)?;
89        let match_value = u64::from_be_bytes([
90            value_bytes[0],
91            value_bytes[1],
92            value_bytes[2],
93            value_bytes[3],
94            value_bytes[4],
95            value_bytes[5],
96            value_bytes[6],
97            value_bytes[7],
98        ]);
99        ctx.advance_pc(8)?;
100
101        // Read target offset (2 bytes)
102        let target_bytes = ctx.read_bytes(2)?;
103        let target = i16::from_be_bytes([target_bytes[0], target_bytes[1]]);
104        ctx.advance_pc(2)?;
105
106        cases.push((match_value, target));
107    }
108
109    // Pop the value to match
110    let match_value = ctx.pop()?;
111    let value = match_value.as_uint()?;
112
113    // Find matching case and jump
114    for (case_value, target) in cases {
115        if value == case_value {
116            ctx.branch(target)?;
117            return Ok(());
118        }
119    }
120
121    // No match found, fall through
122    Ok(())
123}