use getset::{CopyGetters, Getters};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use scroll::{ctx, Pread, Uleb128};
use crate::{
annotation::{AnnotationSetItem, AnnotationSetRefList},
code::CodeItem,
encoded_item::{EncodedItem, EncodedItemArray},
error::Error,
field::FieldId,
jtype::{Type, TypeId},
string::{DexString, StringId},
uint, ulong, ushort, utils,
};
bitflags! {
pub struct AccessFlags: ulong {
const PUBLIC = 0x1;
const PRIVATE = 0x2;
const PROTECTED = 0x4;
const STATIC = 0x8;
const FINAL = 0x10;
const SYNCHRONIZED = 0x20;
const BRIDGE = 0x40;
const VARARGS = 0x80;
const NATIVE = 0x100;
const ABSTRACT = 0x400;
const STRICT = 0x800;
const SYNTHETIC = 0x1000;
const CONSTRUCTOR = 0x10000;
const DECLARED_SYNCHRONIZED = 0x20000;
}
}
#[derive(Debug, Getters, CopyGetters)]
pub struct Method {
#[get = "pub"]
class: Type,
#[get = "pub"]
name: DexString,
#[get_copy = "pub"]
access_flags: AccessFlags,
#[get = "pub"]
params: Vec<Type>,
#[get = "pub"]
shorty: DexString,
#[get = "pub"]
return_type: Type,
code: Option<CodeItem>,
#[get = "pub"]
annotations: AnnotationSetItem,
#[get = "pub"]
param_annotations: AnnotationSetRefList,
}
impl Method {
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_synchronized, SYNCHRONIZED);
gen_is_flag_set!(is_bridge, BRIDGE);
gen_is_flag_set!(is_varargs, VARARGS);
gen_is_flag_set!(is_native, NATIVE);
gen_is_flag_set!(is_abstract, ABSTRACT);
gen_is_flag_set!(is_strict, STRICT);
gen_is_flag_set!(is_synthetic, SYNTHETIC);
gen_is_flag_set!(is_constructor, CONSTRUCTOR);
gen_is_flag_set!(is_declared_synchronized, DECLARED_SYNCHRONIZED);
pub fn signature(&self) -> super::Result<Option<String>> {
utils::get_signature(self.annotations())
}
pub fn code(&self) -> Option<&CodeItem> {
self.code.as_ref()
}
}
pub type ProtoId = ulong;
#[derive(Pread, Debug, CopyGetters, PartialEq)]
#[get_copy = "pub"]
pub struct ProtoIdItem {
shorty: StringId,
return_type: TypeId,
params_off: uint,
}
impl ProtoIdItem {
pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
dex: &super::Dex<S>,
offset: ulong,
) -> super::Result<Self> {
let source = dex.source.as_ref();
Ok(source.pread_with(offset as usize, dex.get_endian())?)
}
}
impl Method {
pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
dex: &super::Dex<S>,
encoded_method: &EncodedMethod,
annotations: AnnotationSetItem,
param_annotations: AnnotationSetRefList,
) -> super::Result<Method> {
debug!(target: "method", "encoded method: {:?}", encoded_method);
let source = &dex.source;
let method_item = dex.get_method_item(encoded_method.method_id)?;
let name = dex.get_string(method_item.name_idx)?;
debug!(target: "method", "name: {}, method id item: {:?}", name, method_item);
let proto_item = dex.get_proto_item(ProtoId::from(method_item.proto_idx))?;
debug!(target: "method", "method proto_item: {:?}", proto_item);
let shorty = dex.get_string(proto_item.shorty)?;
let return_type = dex.get_type(proto_item.return_type)?;
let params = if proto_item.params_off != 0 {
if !dex.is_offset_in_data_section(proto_item.params_off) {
return Err(Error::BadOffset(
proto_item.params_off as usize,
format!(
"Params offset not in data section for proto_item: {:?}",
proto_item
),
));
}
let offset = &mut (proto_item.params_off as usize);
let endian = dex.get_endian();
let len = source.gread_with::<uint>(offset, endian)?;
let type_ids: Vec<ushort> = try_gread_vec_with!(source, offset, len, endian);
utils::get_types(dex, &type_ids)?
} else {
Default::default()
};
debug!(target: "method", "code item offset: {}", encoded_method.code_offset);
let code = dex.get_code_item(encoded_method.code_offset)?;
Ok(Self {
name,
class: dex.get_type(TypeId::from(method_item.class_idx))?,
access_flags: AccessFlags::from_bits(encoded_method.access_flags).ok_or_else(|| {
Error::InvalidId(format!(
"Invalid access flags for method {}",
method_item.name_idx
))
})?,
shorty,
return_type,
params,
code,
annotations,
param_annotations,
})
}
}
#[derive(Pread, Debug, CopyGetters, PartialEq)]
#[get_copy = "pub"]
pub struct MethodIdItem {
class_idx: ushort,
proto_idx: ushort,
name_idx: StringId,
}
impl MethodIdItem {
pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
dex: &super::Dex<S>,
offset: ulong,
) -> super::Result<Self> {
let source = &dex.source;
Ok(source.pread_with(offset as usize, dex.get_endian())?)
}
}
pub type MethodId = ulong;
pub type MethodHandleId = uint;
#[derive(Debug, Getters, CopyGetters)]
pub struct EncodedMethod {
#[get_copy = "pub(crate)"]
pub(crate) method_id: MethodId,
#[get = "pub"]
access_flags: ulong,
#[get = "pub"]
code_offset: ulong,
}
impl EncodedItem for EncodedMethod {
fn id(&self) -> ulong {
self.method_id
}
}
pub type EncodedMethodArray = EncodedItemArray<EncodedMethod>;
impl<'a> ctx::TryFromCtx<'a, ulong> for EncodedMethod {
type Error = Error;
type Size = usize;
fn try_from_ctx(source: &'a [u8], prev_id: ulong) -> super::Result<(Self, Self::Size)> {
let offset = &mut 0;
let id = Uleb128::read(source, offset)?;
let access_flags = Uleb128::read(source, offset)?;
let code_offset = Uleb128::read(source, offset)?;
Ok((
Self {
method_id: prev_id + id,
code_offset,
access_flags,
},
*offset,
))
}
}
#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq)]
pub enum MethodHandleType {
StaticPut = 0x00,
StaticGet = 0x01,
InstancePut = 0x02,
InstanceGet = 0x03,
InvokeStatic = 0x04,
InvokeInstance = 0x05,
InvokeConstructor = 0x06,
InvokeDirect = 0x07,
InvokeInterface = 0x08,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FieldOrMethodId {
Field(FieldId),
Method(MethodId),
}
#[derive(Debug, CopyGetters, PartialEq)]
#[get_copy = "pub"]
pub struct MethodHandleItem {
handle_type: MethodHandleType,
id: FieldOrMethodId,
}
impl<'a, S: AsRef<[u8]>> ctx::TryFromCtx<'a, &super::Dex<S>> for MethodHandleItem {
type Error = Error;
type Size = usize;
fn try_from_ctx(source: &'a [u8], dex: &super::Dex<S>) -> super::Result<(Self, Self::Size)> {
let endian = dex.get_endian();
let offset = &mut 0;
let handle_type: ushort = source.gread_with(offset, endian)?;
let handle_type = MethodHandleType::from_u16(handle_type)
.ok_or_else(|| Error::InvalidId(format!("Invalid handle type {}", handle_type)))?;
let _: ushort = source.gread_with(offset, endian)?;
let id: ushort = source.gread_with(offset, endian)?;
let _: ushort = source.gread_with(offset, endian)?;
let id = match handle_type {
MethodHandleType::StaticPut
| MethodHandleType::StaticGet
| MethodHandleType::InstancePut
| MethodHandleType::InstanceGet => FieldOrMethodId::Field(FieldId::from(id)),
_ => FieldOrMethodId::Method(MethodId::from(id)),
};
Ok((Self { handle_type, id }, *offset))
}
}