use scroll::{ctx, Pread, Uleb128};
use crate::{
annotation::AnnotationSetItem,
class::ClassId,
encoded_item::{EncodedItem, EncodedItemArray},
encoded_value::EncodedValue,
error::Error,
jtype::{Type, TypeId},
string::{DexString, StringId},
ulong, ushort, utils,
};
use getset::{CopyGetters, Getters};
bitflags! {
pub struct AccessFlags: ulong {
const PUBLIC = 0x1;
const PRIVATE = 0x2;
const PROTECTED = 0x4;
const STATIC = 0x8;
const FINAL = 0x10;
const VOLATILE = 0x40;
const TRANSIENT = 0x80;
const SYNTHETIC = 0x1000;
const ENUM = 0x4000;
}
}
#[derive(Debug, Getters, CopyGetters)]
pub struct Field {
#[get = "pub"]
name: DexString,
#[get = "pub"]
jtype: Type,
#[get_copy = "pub"]
class: ClassId,
#[get_copy = "pub"]
access_flags: AccessFlags,
initial_value: Option<EncodedValue>,
#[get = "pub"]
annotations: AnnotationSetItem,
}
impl Field {
pub fn initial_value(&self) -> Option<&EncodedValue> {
self.initial_value.as_ref()
}
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_volatile, VOLATILE);
gen_is_flag_set!(is_transient, TRANSIENT);
gen_is_flag_set!(is_synthetic, SYNTHETIC);
gen_is_flag_set!(is_enum, ENUM);
pub fn signature(&self) -> super::Result<Option<String>> {
utils::get_signature(self.annotations())
}
pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
dex: &super::Dex<S>,
encoded_field: &EncodedField,
initial_value: Option<EncodedValue>,
annotations: AnnotationSetItem,
) -> super::Result<Self> {
debug!(target: "field", "encoded field: {:?}", encoded_field);
let field_item = dex.get_field_item(encoded_field.field_id)?;
debug!(target: "field", "field id item: {:?}", field_item);
Ok(Self {
name: dex.get_string(field_item.name_idx)?,
jtype: dex.get_type(TypeId::from(field_item.type_idx))?,
class: ClassId::from(field_item.class_idx),
access_flags: AccessFlags::from_bits(encoded_field.access_flags).ok_or_else(|| {
Error::InvalidId(format!(
"Invalid access flags when loading field {}",
field_item.name_idx
))
})?,
initial_value,
annotations,
})
}
}
pub type EncodedFieldArray = EncodedItemArray<EncodedField>;
#[derive(Pread, Debug, Getters, PartialEq)]
#[get = "pub"]
pub struct FieldIdItem {
class_idx: ushort,
type_idx: ushort,
name_idx: StringId,
}
impl FieldIdItem {
pub(crate) fn try_from_dex<T: AsRef<[u8]>>(
dex: &super::Dex<T>,
offset: ulong,
) -> super::Result<Self> {
let source = &dex.source;
Ok(source.pread_with(offset as usize, dex.get_endian())?)
}
}
pub type FieldId = ulong;
#[derive(Debug, CopyGetters)]
#[get_copy = "pub"]
pub struct EncodedField {
pub(crate) field_id: FieldId,
access_flags: ulong,
}
impl EncodedItem for EncodedField {
fn id(&self) -> ulong {
self.field_id
}
}
impl<'a> ctx::TryFromCtx<'a, ulong> for EncodedField {
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)?;
Ok((
Self {
field_id: prev_id + id,
access_flags,
},
*offset,
))
}
}