Skip to main content

ddc_dex/
code.rs

1//! `code_item`: register frame, insns, try table and catch handlers.
2
3use std::collections::HashMap;
4
5use crate::insn::{decode_all, Insn, Payload};
6use crate::reader::Cursor;
7
8/// One decoded try region.
9#[derive(Debug, Clone)]
10pub struct TryItem {
11    /// First code unit covered (inclusive).
12    pub start_addr: u32,
13    /// Number of 16-bit code units covered.
14    pub insn_count: u32,
15    /// Index into [`CodeItem::handlers`].
16    pub handler_idx: usize,
17}
18
19/// One `encoded_catch_handler`.
20#[derive(Debug, Clone)]
21pub struct CatchHandler {
22    /// `(type id, handler address)` pairs, in priority order.
23    pub catches: Vec<(u32, u32)>,
24    /// Catch-all (`finally`) handler address.
25    pub catch_all: Option<u32>,
26}
27
28/// A decoded `code_item`.
29#[derive(Debug, Clone)]
30pub struct CodeItem {
31    pub registers_size: u16,
32    /// Incoming argument registers (the LAST `ins_size` registers).
33    pub ins_size: u16,
34    pub outs_size: u16,
35    pub debug_info_off: u32,
36    /// Linearly decoded instructions (payload bodies are NOT included —
37    /// they live in `payloads`).
38    pub insns: Vec<Insn>,
39    /// Payload pseudo-instructions keyed by their code-unit offset.
40    pub payloads: HashMap<u32, Payload>,
41    pub tries: Vec<TryItem>,
42    pub handlers: Vec<CatchHandler>,
43}
44
45impl CodeItem {
46    pub fn parse(data: &[u8], off: usize) -> Option<CodeItem> {
47        let mut c = Cursor::at(data, off);
48        let registers_size = c.u2()?;
49        let ins_size = c.u2()?;
50        let outs_size = c.u2()?;
51        let tries_size = c.u2()?;
52        let debug_info_off = c.u4()?;
53        let insns_size = c.u4()? as usize; // in 16-bit code units
54        let insns_off = c.pos;
55        if insns_off + 2 * insns_size > data.len() {
56            return None;
57        }
58        let insns_end = insns_off + 2 * insns_size;
59        let (insns, payloads) = decode_all(&data[insns_off..insns_end]);
60
61        // Optional 4-byte alignment padding before the try table.
62        let mut p = insns_end;
63        if tries_size != 0 && insns_size % 2 == 1 {
64            p += 2;
65        }
66
67        let mut tries = Vec::with_capacity(tries_size as usize);
68        let mut c = Cursor::at(data, p);
69        for _ in 0..tries_size {
70            let start_addr = c.u4()?;
71            let insn_count = c.u2()? as u32;
72            // Raw byte offset from the start of the handler list; resolved
73            // below once the list has been walked.
74            let handler_off = c.u2()? as usize;
75            tries.push(TryItem {
76                start_addr,
77                insn_count,
78                handler_idx: handler_off,
79            });
80        }
81
82        // encoded_catch_handler_list: uleb size, then handlers back to back.
83        let list_off = c.pos;
84        let n_handlers = c.read_uleb128()? as usize;
85        let uleb_len = c.pos - list_off;
86        let mut handlers: Vec<CatchHandler> = Vec::with_capacity(n_handlers);
87        // handler byte offset (from list start) → handler index.
88        let mut idx_by_off: HashMap<usize, usize> = HashMap::new();
89        for _ in 0..n_handlers {
90            let hoff = c.pos - list_off;
91            let h = Self::parse_handler(c)?;
92            idx_by_off.insert(hoff, handlers.len());
93            handlers.push(h);
94        }
95
96        // Resolve each try's handler_off. The spec's offsets are from the
97        // list start (the uleb size included in the offset space); some
98        // writers count from after it — accept both, then the first handler.
99        for t in tries.iter_mut() {
100            let raw = t.handler_idx;
101            t.handler_idx = idx_by_off
102                .get(&raw)
103                .or_else(|| idx_by_off.get(&(raw + uleb_len)))
104                .or_else(|| idx_by_off.get(&raw.saturating_sub(uleb_len)))
105                .copied()
106                .unwrap_or(0);
107        }
108
109        Some(CodeItem {
110            registers_size,
111            ins_size,
112            outs_size,
113            debug_info_off,
114            insns,
115            payloads,
116            tries,
117            handlers,
118        })
119    }
120
121    fn parse_handler(mut c: Cursor<'_>) -> Option<CatchHandler> {
122        let sz = c.read_sleb128()?;
123        let mut h = CatchHandler {
124            catches: Vec::new(),
125            catch_all: None,
126        };
127        if sz != 0 {
128            for _ in 0..sz.unsigned_abs() {
129                let ty = c.read_uleb128()? as u32;
130                let addr = c.read_uleb128()? as u32;
131                h.catches.push((ty, addr));
132            }
133        }
134        if sz <= 0 {
135            let addr = c.read_uleb128()? as u32;
136            h.catch_all = Some(addr);
137        }
138        Some(h)
139    }
140
141    /// Handler for a try item (index-resolved, tolerating out-of-range).
142    pub fn handler_of(&self, t: &TryItem) -> Option<&CatchHandler> {
143        self.handlers.get(t.handler_idx)
144    }
145}