zingen/jump/
target.rs

1//! Target related operations of the jump table.
2//!
3//! This module provides functions to retrieve and shift the target program counters
4//! associated with various jump types.
5
6use crate::{
7    jump::{Jump, JumpTable},
8    Error, Result,
9};
10
11impl JumpTable {
12    /// Retrieves the target of a given jump.
13    ///
14    /// This function returns the target program counter based on the type of jump
15    /// (offset, label, function, or external function).
16    pub fn target(&self, jump: &Jump) -> Result<u16> {
17        match jump {
18            Jump::Label(label) => Ok(*label),
19            Jump::Func(func) => Ok(*self.func.get(func).ok_or(Error::FuncNotFound(*func))?),
20            Jump::ExtFunc(ext) => Ok(self.code.offset_of(ext).ok_or(Error::ExtFuncNotFound)?),
21        }
22    }
23
24    /// Shifts the target program counters as a pre-calculation step.
25    ///
26    /// This function calculates the target program counter from the original program
27    /// counter and the target, adjusting for any offsets.
28    pub fn shift_targets(&mut self) -> Result<()> {
29        let mut total_offset = 0;
30        let mut target_sizes = Vec::new();
31        let jumps = self.jump.clone();
32
33        // First pass: calculate all target sizes and accumulate offsets
34        for (original_pc, jump) in jumps.iter() {
35            let pc = original_pc + total_offset;
36            let raw_target = self.target(jump)?;
37
38            // Calculate the absolute target including future offsets
39            let target = if raw_target > *original_pc {
40                raw_target + total_offset
41            } else {
42                raw_target
43            };
44
45            // Calculate instruction size based on absolute target value
46            let instr_size = if target > 0xff {
47                3 // PUSH2 + 2 bytes
48            } else {
49                2 // PUSH1 + 1 byte
50            };
51
52            target_sizes.push((pc, instr_size));
53            total_offset += instr_size;
54        }
55
56        // Second pass: apply shifts with accumulated offsets
57        total_offset = 0;
58        for (pc, size) in target_sizes {
59            tracing::debug!("shift target at pc=0x{pc:x} with size={size}");
60            self.shift_target(pc, size)?;
61            total_offset += size;
62        }
63
64        Ok(())
65    }
66
67    /// Shifts the program counter of targets with the given pointer and offset.
68    ///
69    /// This function handles the shifting of the code section, label targets, and
70    /// function targets.
71    pub fn shift_target(&mut self, ptr: u16, offset: u16) -> Result<()> {
72        // First shift the code section
73        self.code.shift(offset);
74
75        // Only shift targets that are after ptr
76        self.shift_label_target(ptr, offset)?;
77        self.shift_func_target(ptr, offset)
78    }
79
80    /// Shifts the program counter for functions.
81    pub fn shift_func_target(&mut self, ptr: u16, offset: u16) -> Result<()> {
82        self.func.iter_mut().try_for_each(|(index, target)| {
83            if *target > ptr {
84                let next_target = *target + offset;
85                tracing::trace!(
86                    "shift Func({index}) target with offset={offset}: 0x{target:x}(0x{ptr:x}) -> 0x{:x}",
87                    next_target
88                );
89
90                *target = next_target;
91            }
92
93            Ok(())
94        })
95    }
96
97    /// Shifts the program counter for labels.
98    pub fn shift_label_target(&mut self, ptr: u16, offset: u16) -> Result<()> {
99        for (_, jump) in self.jump.iter_mut() {
100            let Jump::Label(target) = jump else {
101                continue;
102            };
103
104            // Only shift targets that come after ptr AND
105            // only if the jump instruction itself comes after ptr
106            if *target > ptr {
107                let next_target = *target + offset;
108                tracing::trace!(
109                    "shift Label target with offset={offset}: 0x{target:x}(0x{ptr:x}) -> 0x{:x}",
110                    next_target,
111                );
112                *target = next_target;
113            }
114        }
115        Ok(())
116    }
117}