use scroll::{ctx, Pread, Uleb128};
use std::{fmt, ops::Deref};
use getset::{CopyGetters, Getters};
use crate::{
encoded_item::EncodedCatchHandlers, error::Error, jtype::Type, string::DexString, uint, ulong,
ushort,
};
#[derive(Debug, Getters, CopyGetters)]
pub struct DebugInfoItem {
#[get_copy = "pub"]
line_start: usize,
#[get = "pub"]
parameter_names: Vec<Option<DexString>>,
}
#[derive(Getters, CopyGetters)]
pub struct CodeItem {
#[get_copy = "pub"]
registers_size: ushort,
debug_info_item: Option<DebugInfoItem>,
#[get_copy = "pub"]
ins_size: ushort,
#[get_copy = "pub"]
outs_size: ushort,
#[get = "pub"]
insns: Vec<ushort>,
#[get = "pub"]
tries: Tries,
}
impl CodeItem {
pub fn debug_info_item(&self) -> Option<&DebugInfoItem> {
self.debug_info_item.as_ref()
}
}
impl fmt::Debug for CodeItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CodeItem {{ registers_size: {}, debug_info: {}, ins_size: {}, outs_size: {}, tries: {} }}",
self.registers_size, self.debug_info_item.is_some(), self.ins_size, self.outs_size, self.tries.len())
}
}
#[derive(Pread, Clone, Copy, Debug, Getters, CopyGetters)]
pub(crate) struct TryItem {
#[get_copy = "pub"]
start_addr: uint,
#[get_copy = "pub"]
insn_count: ushort,
#[get_copy = "pub"]
handler_off: ushort,
}
#[derive(Debug, Clone)]
pub enum ExceptionType {
BaseException,
Ty(Type),
}
#[derive(Debug, Clone, Getters, CopyGetters)]
pub struct CatchHandler {
#[get = "pub"]
pub(crate) exception: ExceptionType,
#[get_copy = "pub"]
pub(crate) addr: ulong,
}
#[derive(Debug, Getters, CopyGetters)]
pub struct TryCatchHandlers {
#[get_copy = "pub"]
start_addr: uint,
#[get_copy = "pub"]
insn_count: ushort,
#[get = "pub"]
catch_handlers: Vec<CatchHandler>,
}
#[derive(Debug, Default, Getters, CopyGetters)]
pub struct Tries {
#[get = "pub"]
try_catch_blocks: Vec<TryCatchHandlers>,
}
impl Deref for Tries {
type Target = Vec<TryCatchHandlers>;
fn deref(&self) -> &Self::Target {
&self.try_catch_blocks
}
}
impl<'a, S> ctx::TryFromCtx<'a, (usize, &super::Dex<S>)> for Tries
where
S: AsRef<[u8]>,
{
type Error = Error;
type Size = usize;
fn try_from_ctx(
source: &'a [u8],
(tries_size, dex): (usize, &super::Dex<S>),
) -> Result<(Self, Self::Size), Self::Error> {
let offset = &mut 0;
let endian = dex.get_endian();
let tries: Vec<TryItem> = try_gread_vec_with!(source, offset, tries_size, endian);
let encoded_catch_handlers: EncodedCatchHandlers = source.gread_with(offset, dex)?;
let tries: super::Result<Vec<_>> = tries
.into_iter()
.map(|c| {
let encoded_handler =
encoded_catch_handlers.find(c.handler_off).ok_or_else(|| {
Error::InvalidId(format!("Invalid catch handler: {}", c.handler_off))
})?;
Ok(TryCatchHandlers {
start_addr: c.start_addr,
insn_count: c.insn_count,
catch_handlers: encoded_handler.handlers(),
})
})
.collect();
Ok((
Self {
try_catch_blocks: tries?,
},
*offset,
))
}
}
impl<'a, S> ctx::TryFromCtx<'a, &super::Dex<S>> for DebugInfoItem
where
S: AsRef<[u8]>,
{
type Error = Error;
type Size = usize;
fn try_from_ctx(
source: &'a [u8],
dex: &super::Dex<S>,
) -> Result<(Self, Self::Size), Self::Error> {
let offset = &mut 0;
let line_start = Uleb128::read(source, offset)? as usize;
let parameters_size = Uleb128::read(source, offset)?;
let mut parameter_names = Vec::with_capacity(parameters_size as usize);
for _ in 0..parameters_size {
let string_id = Uleb128::read(source, offset)? + 1;
parameter_names.push(if string_id != u64::from(crate::NO_INDEX) {
Some(dex.get_string(string_id as uint)?)
} else {
None
});
}
Ok((
Self {
line_start,
parameter_names,
},
*offset,
))
}
}
impl<'a, S> ctx::TryFromCtx<'a, &super::Dex<S>> for CodeItem
where
S: AsRef<[u8]>,
{
type Error = Error;
type Size = usize;
fn try_from_ctx(
source: &'a [u8],
dex: &super::Dex<S>,
) -> Result<(Self, Self::Size), Self::Error> {
let offset = &mut 0;
let endian = dex.get_endian();
let registers_size: ushort = source.gread_with(offset, endian)?;
let ins_size = source.gread_with(offset, endian)?;
let outs_size = source.gread_with(offset, endian)?;
let tries_size: ushort = source.gread_with(offset, endian)?;
let debug_info_off = source.gread_with(offset, endian)?;
let debug_info_item = if debug_info_off != 0 {
Some(dex.get_debug_info_item(debug_info_off)?)
} else {
None
};
let insns_size: uint = source.gread_with(offset, endian)?;
let insns: Vec<ushort> = try_gread_vec_with!(source, offset, insns_size, endian);
if insns_size % 2 != 0 && tries_size != 0 {
source.gread_with::<ushort>(offset, endian)?;
}
let tries: Tries = if tries_size != 0 {
source.gread_with(offset, (tries_size as usize, dex))?
} else {
Default::default()
};
Ok((
Self {
registers_size,
debug_info_item,
ins_size,
outs_size,
insns,
tries,
},
*offset,
))
}
}