use std::clone::Clone;
use getset::{CopyGetters, Getters};
use scroll::{ctx, Pread, Uleb128};
use crate::{
annotation::{AnnotationSetItem, AnnotationsDirectoryItem},
encoded_item::EncodedItemArrayCtx,
error::Error,
field::{EncodedFieldArray, Field},
jtype::Type,
method::{EncodedMethodArray, Method},
source::Source,
string::DexString,
uint, utils,
};
pub type ClassId = uint;
bitflags! {
pub struct AccessFlags: uint {
const PUBLIC = 0x1;
const PRIVATE = 0x2;
const PROTECTED = 0x4;
const STATIC = 0x8;
const FINAL = 0x10;
const INTERFACE = 0x200;
const ABSTRACT = 0x400;
const SYNTHETIC = 0x1000;
const ANNOTATION = 0x2000;
const ENUM = 0x4000;
}
}
#[derive(Debug, Getters, CopyGetters)]
pub struct Class {
#[get_copy = "pub"]
pub(crate) id: ClassId,
#[get = "pub"]
pub(crate) jtype: Type,
#[get_copy = "pub"]
pub(crate) access_flags: AccessFlags,
#[get_copy = "pub"]
pub(crate) super_class: Option<ClassId>,
#[get = "pub"]
pub(crate) interfaces: Vec<Type>,
pub(crate) source_file: Option<DexString>,
#[get = "pub"]
pub(crate) static_fields: Vec<Field>,
#[get = "pub"]
pub(crate) instance_fields: Vec<Field>,
#[get = "pub"]
pub(crate) direct_methods: Vec<Method>,
#[get = "pub"]
pub(crate) virtual_methods: Vec<Method>,
#[get = "pub"]
pub(crate) annotations: AnnotationSetItem,
}
impl Class {
gen_is_flag_set!(is_public, PUBLIC);
gen_is_flag_set!(is_private, PRIVATE);
gen_is_flag_set!(is_protected, PROTECTED);
gen_is_flag_set!(is_static, STATIC);
gen_is_flag_set!(is_final, FINAL);
gen_is_flag_set!(is_interface, INTERFACE);
gen_is_flag_set!(is_abstract, ABSTRACT);
gen_is_flag_set!(is_synthetic, SYNTHETIC);
gen_is_flag_set!(is_annotation, ANNOTATION);
gen_is_flag_set!(is_enum, ENUM);
pub fn signature(&self) -> super::Result<Option<String>> {
utils::get_signature(self.annotations())
}
pub fn source_file(&self) -> Option<&DexString> {
self.source_file.as_ref()
}
pub fn fields(&self) -> impl Iterator<Item = &Field> + '_ {
self.static_fields()
.iter()
.chain(self.instance_fields().iter())
}
pub fn methods(&self) -> impl Iterator<Item = &Method> + '_ {
self.direct_methods()
.iter()
.chain(self.virtual_methods().iter())
}
pub(crate) fn try_from_dex<T: AsRef<[u8]>>(
dex: &super::Dex<T>,
class_def: &ClassDefItem,
) -> super::Result<Self> {
debug!(target: "class", "trying to load class: {}", class_def.class_idx);
let jtype = dex.get_type(class_def.class_idx)?;
debug!(target: "class", "class: {}, jtype: {}", class_def.class_idx, jtype);
let data_off = class_def.class_data_off;
let AnnotationsDirectoryItem {
class_annotations,
mut field_annotations,
mut method_annotations,
mut parameter_annotations,
} = dex.get_annotations_directory_item(class_def.annotations_off)?;
let static_values = dex.get_static_values(class_def.static_values_off)?;
let (static_fields, instance_fields, direct_methods, virtual_methods) = dex
.get_class_data(data_off)?
.map(move |c| {
let mut static_values = static_values.into_inner();
static_values.reverse();
Ok((
try_from_item!(c.static_fields, |encoded_field| {
dex.get_field(
&encoded_field,
static_values.pop(),
field_annotations
.binary_search_by_key(&encoded_field.field_id(), |f| f.field_idx())
.map(|index| field_annotations.remove(index).annotations)
.unwrap_or_else(|_| Default::default()),
)
}),
try_from_item!(c.instance_fields, |encoded_field| {
dex.get_field(
&encoded_field,
None,
field_annotations
.binary_search_by_key(&encoded_field.field_id(), |f| f.field_idx())
.map(|index| field_annotations.remove(index).annotations)
.unwrap_or_else(|_| Default::default()),
)
}),
try_from_item!(c.direct_methods, |encoded_method| {
let method_annotations = method_annotations
.binary_search_by_key(&encoded_method.method_id(), |m| m.method_idx())
.map(|index| method_annotations.remove(index).annotations)
.unwrap_or_else(|_| Default::default());
let parameter_annotations = parameter_annotations
.binary_search_by_key(&encoded_method.method_id(), |m| m.method_idx())
.map(|index| parameter_annotations.remove(index).annotations)
.unwrap_or_else(|_| Default::default());
dex.get_method(&encoded_method, method_annotations, parameter_annotations)
}),
try_from_item!(c.virtual_methods, |encoded_method| {
let method_annotations = method_annotations
.binary_search_by_key(&encoded_method.method_id(), |m| m.method_idx())
.map(|index| method_annotations.remove(index).annotations)
.unwrap_or_else(|_| Default::default());
let parameter_annotations = parameter_annotations
.binary_search_by_key(&encoded_method.method_id(), |m| m.method_idx())
.map(|index| parameter_annotations.remove(index).annotations)
.unwrap_or_else(|_| Default::default());
dex.get_method(&encoded_method, method_annotations, parameter_annotations)
}),
))
})
.unwrap_or_else(|| Ok::<_, Error>(Default::default()))?;
debug!(target: "class", "super class id: {}", class_def.superclass_idx);
let super_class = if class_def.superclass_idx != super::NO_INDEX {
Some(class_def.superclass_idx)
} else {
None
};
debug!(target: "class", "access flags: {}", class_def.access_flags);
Ok(Class {
id: class_def.class_idx,
jtype,
super_class,
interfaces: dex.get_interfaces(class_def.interfaces_off)?,
access_flags: AccessFlags::from_bits(class_def.access_flags).ok_or_else(|| {
Error::InvalidId(format!(
"Invalid Access flags in class {}",
class_def.class_idx
))
})?,
source_file: dex.get_source_file(class_def.source_file_idx)?,
static_fields,
instance_fields,
direct_methods,
virtual_methods,
annotations: class_annotations,
})
}
}
#[derive(Getters)]
pub struct ClassDataItem {
static_fields: Option<EncodedFieldArray>,
instance_fields: Option<EncodedFieldArray>,
direct_methods: Option<EncodedMethodArray>,
virtual_methods: Option<EncodedMethodArray>,
}
impl ClassDataItem {
pub fn static_fields(&self) -> Option<&EncodedFieldArray> {
self.static_fields.as_ref()
}
pub fn instance_fields(&self) -> Option<&EncodedFieldArray> {
self.instance_fields.as_ref()
}
pub fn direct_methods(&self) -> Option<&EncodedMethodArray> {
self.direct_methods.as_ref()
}
pub fn virtual_methods(&self) -> Option<&EncodedMethodArray> {
self.virtual_methods.as_ref()
}
}
impl<'a, S> ctx::TryFromCtx<'a, &super::Dex<S>> for ClassDataItem
where
S: AsRef<[u8]>,
{
type Error = Error;
type Size = usize;
fn try_from_ctx(source: &'a [u8], dex: &super::Dex<S>) -> super::Result<(Self, Self::Size)> {
let offset = &mut 0;
let static_field_size = Uleb128::read(source, offset)?;
let instance_field_size = Uleb128::read(source, offset)?;
let direct_methods_size = Uleb128::read(source, offset)?;
let virtual_methods_size = Uleb128::read(source, offset)?;
debug!(target: "class data", "static-fields: {}, instance-fields: {}, direct-methods: {}, virtual-methods: {}",
static_field_size, instance_field_size, direct_methods_size, virtual_methods_size);
Ok((
ClassDataItem {
static_fields: encoded_array!(source, dex, offset, static_field_size),
instance_fields: encoded_array!(source, dex, offset, instance_field_size),
direct_methods: encoded_array!(source, dex, offset, direct_methods_size),
virtual_methods: encoded_array!(source, dex, offset, virtual_methods_size),
},
*offset,
))
}
}
#[derive(Copy, Clone, Debug, Pread, CopyGetters)]
#[get_copy = "pub"]
pub struct ClassDefItem {
pub(crate) class_idx: uint,
pub(crate) access_flags: uint,
pub(crate) superclass_idx: uint,
pub(crate) interfaces_off: uint,
pub(crate) source_file_idx: uint,
pub(crate) annotations_off: uint,
pub(crate) class_data_off: uint,
pub(crate) static_values_off: uint,
}
pub(crate) struct ClassDefItemIter<T> {
source: Source<T>,
offset: usize,
len: uint,
endian: super::Endian,
}
impl<T> ClassDefItemIter<T> {
pub(crate) fn new(source: Source<T>, offset: uint, len: uint, endian: super::Endian) -> Self {
Self {
source,
offset: offset as usize,
len,
endian,
}
}
}
impl<T: AsRef<[u8]>> Iterator for ClassDefItemIter<T> {
type Item = super::Result<ClassDefItem>;
fn next(&mut self) -> Option<Self::Item> {
if self.len == 0 {
return None;
}
let class_item: super::Result<ClassDefItem> = self
.source
.as_ref()
.gread_with(&mut self.offset, self.endian)
.map_err(Error::from);
self.len -= 1;
Some(class_item)
}
}