dex/
code.rs

1//! Structures defining the contents of a `Method`'s code.
2use scroll::{ctx, Pread, Uleb128};
3use std::{fmt, ops::Deref};
4
5use getset::{CopyGetters, Getters};
6
7use crate::{
8    encoded_item::EncodedCatchHandlers, error::Error, jtype::Type, string::DexString, uint, ulong,
9    ushort,
10};
11
12/// Debug Info of a method.
13/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#debug-info-item)
14#[derive(Debug, Getters, CopyGetters)]
15pub struct DebugInfoItem {
16    /// Initial value for the state machines's line register.
17    #[get_copy = "pub"]
18    line_start: usize,
19    /// Names of the incoming parameters.
20    #[get = "pub"]
21    parameter_names: Vec<Option<DexString>>,
22}
23
24/// Code and Debug Info of a method.
25#[derive(Getters, CopyGetters)]
26pub struct CodeItem {
27    /// The number of registers the method must use.
28    #[get_copy = "pub"]
29    registers_size: ushort,
30    /// Line number and source file information.
31    debug_info_item: Option<DebugInfoItem>,
32    /// Number of words for incoming arguments to this method.
33    #[get_copy = "pub"]
34    ins_size: ushort,
35    /// Number of words for outgoing arguments required for invocation.
36    #[get_copy = "pub"]
37    outs_size: ushort,
38    /// Code instructions for this method.
39    #[get = "pub"]
40    insns: Vec<ushort>,
41    /// Try, Exception handling information of this method.
42    #[get = "pub"]
43    tries: Tries,
44}
45
46impl CodeItem {
47    /// Line number and source file information.
48    pub fn debug_info_item(&self) -> Option<&DebugInfoItem> {
49        self.debug_info_item.as_ref()
50    }
51}
52
53impl fmt::Debug for CodeItem {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(f, "CodeItem {{ registers_size: {}, debug_info: {}, ins_size: {}, outs_size: {}, tries: {} }}",
56            self.registers_size, self.debug_info_item.is_some(), self.ins_size, self.outs_size, self.tries.len())
57    }
58}
59
60/// Represents a Try-Catch block
61#[derive(Pread, Clone, Copy, Debug, Getters, CopyGetters)]
62pub(crate) struct TryItem {
63    /// The instruction at which the try block starts.
64    #[get_copy = "pub"]
65    start_addr: uint,
66    /// Number of instructions the try block covers.
67    #[get_copy = "pub"]
68    insn_count: ushort,
69    /// Exception handler offset.
70    #[get_copy = "pub"]
71    handler_off: ushort,
72}
73
74#[derive(Debug, Clone)]
75pub enum ExceptionType {
76    /// The `Exception` class.
77    BaseException,
78    /// Sub-types of the `Exception` class.
79    Ty(Type),
80}
81
82#[derive(Debug, Clone, Getters, CopyGetters)]
83pub struct CatchHandler {
84    /// Type of the exception handled by this handler.
85    #[get = "pub"]
86    pub(crate) exception: ExceptionType,
87    /// Start address of the catch handler.
88    #[get_copy = "pub"]
89    pub(crate) addr: ulong,
90}
91
92/// Represents Try and catch blocks.
93#[derive(Debug, Getters, CopyGetters)]
94pub struct TryCatchHandlers {
95    /// Start of the try block.
96    #[get_copy = "pub"]
97    start_addr: uint,
98    /// Number of instructions covered by this try block.
99    #[get_copy = "pub"]
100    insn_count: ushort,
101    /// List of catch handlers for this try block.
102    #[get = "pub"]
103    catch_handlers: Vec<CatchHandler>,
104}
105
106/// List of try-catch blocks found in this method.
107#[derive(Debug, Default, Getters, CopyGetters)]
108pub struct Tries {
109    #[get = "pub"]
110    try_catch_blocks: Vec<TryCatchHandlers>,
111}
112
113impl Deref for Tries {
114    type Target = Vec<TryCatchHandlers>;
115
116    fn deref(&self) -> &Self::Target {
117        &self.try_catch_blocks
118    }
119}
120
121impl<'a, S> ctx::TryFromCtx<'a, (usize, &super::Dex<S>)> for Tries
122where
123    S: AsRef<[u8]>,
124{
125    type Error = Error;
126    type Size = usize;
127
128    fn try_from_ctx(
129        source: &'a [u8],
130        (tries_size, dex): (usize, &super::Dex<S>),
131    ) -> Result<(Self, Self::Size), Self::Error> {
132        let offset = &mut 0;
133        let endian = dex.get_endian();
134        let tries: Vec<TryItem> = try_gread_vec_with!(source, offset, tries_size, endian);
135        let encoded_catch_handlers: EncodedCatchHandlers = source.gread_with(offset, dex)?;
136        let tries: super::Result<Vec<_>> = tries
137            .into_iter()
138            .map(|c| {
139                let encoded_handler =
140                    encoded_catch_handlers.find(c.handler_off).ok_or_else(|| {
141                        Error::InvalidId(format!("Invalid catch handler: {}", c.handler_off))
142                    })?;
143                Ok(TryCatchHandlers {
144                    start_addr: c.start_addr,
145                    insn_count: c.insn_count,
146                    catch_handlers: encoded_handler.handlers(),
147                })
148            })
149            .collect();
150        Ok((
151            Self {
152                try_catch_blocks: tries?,
153            },
154            *offset,
155        ))
156    }
157}
158
159impl<'a, S> ctx::TryFromCtx<'a, &super::Dex<S>> for DebugInfoItem
160where
161    S: AsRef<[u8]>,
162{
163    type Error = Error;
164    type Size = usize;
165
166    fn try_from_ctx(
167        source: &'a [u8],
168        dex: &super::Dex<S>,
169    ) -> Result<(Self, Self::Size), Self::Error> {
170        let offset = &mut 0;
171        let line_start = Uleb128::read(source, offset)? as usize;
172        let parameters_size = Uleb128::read(source, offset)?;
173        let mut parameter_names = Vec::with_capacity(parameters_size as usize);
174        for _ in 0..parameters_size {
175            let string_id = Uleb128::read(source, offset)? + 1;
176            parameter_names.push(if string_id != u64::from(crate::NO_INDEX) {
177                Some(dex.get_string(string_id as uint)?)
178            } else {
179                None
180            });
181        }
182        Ok((
183            Self {
184                line_start,
185                parameter_names,
186            },
187            *offset,
188        ))
189    }
190}
191
192impl<'a, S> ctx::TryFromCtx<'a, &super::Dex<S>> for CodeItem
193where
194    S: AsRef<[u8]>,
195{
196    type Error = Error;
197    type Size = usize;
198
199    fn try_from_ctx(
200        source: &'a [u8],
201        dex: &super::Dex<S>,
202    ) -> Result<(Self, Self::Size), Self::Error> {
203        let offset = &mut 0;
204        let endian = dex.get_endian();
205        let registers_size: ushort = source.gread_with(offset, endian)?;
206        let ins_size = source.gread_with(offset, endian)?;
207        let outs_size = source.gread_with(offset, endian)?;
208        let tries_size: ushort = source.gread_with(offset, endian)?;
209        let debug_info_off = source.gread_with(offset, endian)?;
210        let debug_info_item = if debug_info_off != 0 {
211            Some(dex.get_debug_info_item(debug_info_off)?)
212        } else {
213            None
214        };
215        let insns_size: uint = source.gread_with(offset, endian)?;
216        let insns: Vec<ushort> = try_gread_vec_with!(source, offset, insns_size, endian);
217        if insns_size % 2 != 0 && tries_size != 0 {
218            source.gread_with::<ushort>(offset, endian)?;
219        }
220        let tries: Tries = if tries_size != 0 {
221            source.gread_with(offset, (tries_size as usize, dex))?
222        } else {
223            Default::default()
224        };
225        Ok((
226            Self {
227                registers_size,
228                debug_info_item,
229                ins_size,
230                outs_size,
231                insns,
232                tries,
233            },
234            *offset,
235        ))
236    }
237}