androscalpel/androscalpel/src/field.rs
2025-01-24 16:43:30 +01:00

295 lines
9 KiB
Rust

//! Representation of the fields of a class.
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[cfg(feature = "python")]
use pyo3::prelude::*;
use crate::{
DexAnnotationItem, DexString, DexValue, HiddenApiData, IdField, IdMethod, IdMethodType, IdType,
MethodHandle, Result, Visitable, VisitableMut, Visitor, VisitorMut,
};
use androscalpel_serializer::consts::*;
/// Represent a field.
#[cfg_attr(feature = "python", pyclass(eq))]
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Field {
/// The structure used to reference this field.
#[cfg_attr(feature = "python", pyo3(get))]
pub descriptor: IdField,
/// The field visibility
#[cfg_attr(feature = "python", pyo3(get))]
pub visibility: FieldVisibility,
/// If the field is defined for the class globally
#[cfg_attr(feature = "python", pyo3(get))]
pub is_static: bool,
/// If the field is immutable after construction
#[cfg_attr(feature = "python", pyo3(get))]
pub is_final: bool,
/// For thread safety
#[cfg_attr(feature = "python", pyo3(get))]
pub is_volatile: bool,
/// If the field should **not** be saved by default serialization
#[cfg_attr(feature = "python", pyo3(get))]
pub is_transient: bool,
/// If the field is not defined in the source code
#[cfg_attr(feature = "python", pyo3(get))]
pub is_synthetic: bool,
/// If the field is an enumerated value
#[cfg_attr(feature = "python", pyo3(get))]
pub is_enum: bool,
/// The default value of this field
pub value: Option<DexValue>,
/// The annotations for this field
#[cfg_attr(feature = "python", pyo3(get))]
pub annotations: Vec<DexAnnotationItem>,
/// Hidden Api data.
#[cfg_attr(feature = "python", pyo3(get))]
pub hiddenapi: Option<HiddenApiData>,
}
/// Represent the visibility of a field
#[cfg_attr(feature = "python", pyclass(eq, eq_int))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub enum FieldVisibility {
Public,
Private,
Protected,
None_, // Actually quite common
}
#[cfg_attr(feature = "python", pymethods)]
impl Field {
pub fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(self)?)
}
#[cfg_attr(feature = "python", staticmethod)]
pub fn from_json(json: &str) -> Result<Self> {
Ok(serde_json::from_str(json)?)
}
#[cfg_attr(feature = "python", new)]
pub fn new(descriptor: IdField) -> Self {
Self {
descriptor,
visibility: FieldVisibility::Public,
is_static: false,
is_final: false,
is_volatile: false,
is_transient: false,
is_synthetic: false,
is_enum: false,
value: None,
annotations: vec![],
hiddenapi: None,
}
}
pub fn __str__(&self) -> String {
/*
let name: String = (&self.name).into();
let file = if let Some(file) = &self.source_file {
let file: String = file.into();
format!(" defined in {file}\n")
} else {
"".into()
};
let superclass = if let Some(spcl) = &self.superclass {
let spcl: String = spcl.into();
format!(" extends: {spcl}\n")
} else {
"".into()
};
let interfaces = if self.interfaces.is_empty() {
"".into()
} else {
let mut interfaces: String = " implements:\n".into();
for it in &self.interfaces {
let it: String = it.into();
interfaces += &format!(" {it}\n");
}
interfaces
};
format!("{name}\n{file}{superclass}{interfaces}")
*/
self.__repr__()
}
pub fn __repr__(&self) -> String {
let descr = self.descriptor.__str__();
format!("Field({descr})")
}
/// Return the value as a python object
#[cfg(feature = "python")]
#[getter]
pub fn get_value(&self) -> Option<DexValue> {
self.value.clone()
}
/// Set the value from a python object
#[cfg(feature = "python")]
#[setter]
pub fn set_value(&mut self, ob: &Bound<'_, PyAny>) -> PyResult<()> {
self.value = Some(ob.extract()?);
// TODO: check type match
Ok(())
}
/// Return all strings referenced in the field.
pub fn get_all_strings(&self) -> HashSet<DexString> {
let mut strings = HashSet::new();
strings.extend(self.descriptor.get_all_strings());
if let Some(val) = &self.value {
strings.extend(val.get_all_strings());
}
for annot in &self.annotations {
strings.extend(annot.get_all_strings());
}
strings
}
/// Return all types referenced in the field.
pub fn get_all_types(&self) -> HashSet<IdType> {
let mut types = HashSet::new();
types.extend(self.descriptor.get_all_types());
if let Some(value) = &self.value {
types.extend(value.get_all_types());
}
for annot in &self.annotations {
types.extend(annot.get_all_types());
}
types
}
/// Return all prototypes referenced in the field.
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
let mut protos = HashSet::new();
if let Some(value) = &self.value {
protos.extend(value.get_all_protos());
}
for annot in &self.annotations {
protos.extend(annot.get_all_protos());
}
protos
}
/// Return all field ids referenced in the field.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
fields.insert(self.descriptor.clone());
if let Some(value) = &self.value {
fields.extend(value.get_all_field_ids());
}
for annot in &self.annotations {
fields.extend(annot.get_all_field_ids());
}
fields
}
/// Return all method ids referenced in the field.
pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
let mut methods = HashSet::new();
if let Some(value) = &self.value {
methods.extend(value.get_all_method_ids());
}
for annot in &self.annotations {
methods.extend(annot.get_all_method_ids());
}
methods
}
/// Return all method handles referenced in the field.
pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
let mut methods = HashSet::new();
if let Some(value) = &self.value {
methods.extend(value.get_all_method_handles());
}
for annot in &self.annotations {
methods.extend(annot.get_all_method_handles());
}
methods
}
/// Return the binary representation of access flags.
///
/// # Note
///
/// The return value is a u32, not the Uleb128
/// encoded value found in the dex file.
pub fn get_raw_access_flags(&self) -> u32 {
let mut flags = 0;
match self.visibility {
FieldVisibility::Public => flags |= ACC_PUBLIC,
FieldVisibility::Private => flags |= ACC_PRIVATE,
FieldVisibility::Protected => flags |= ACC_PROTECTED,
FieldVisibility::None_ => (),
}
if self.is_static {
flags |= ACC_STATIC
}
if self.is_final {
flags |= ACC_FINAL
}
if self.is_volatile {
flags |= ACC_VOLATILE
}
if self.is_transient {
flags |= ACC_TRANSIENT
}
if self.is_synthetic {
flags |= ACC_SYNTHETIC
}
if self.is_enum {
flags |= ACC_ENUM
}
flags
}
/// If the fields has annotations
pub fn has_annotations(&self) -> bool {
!self.annotations.is_empty()
}
}
impl<V: Visitor> Visitable<V> for Field {
fn default_visit(&self, v: &mut V) -> Result<()> {
v.visit_field_id(&self.descriptor)?;
v.visit_field_visibility(&self.visibility)?;
if let Some(val) = &self.value {
v.visit_value(val)?;
}
for annot in &self.annotations {
v.visit_annotation_item(annot)?;
}
if let Some(hiddenapi) = &self.hiddenapi {
v.visit_hidden_api_data(hiddenapi)?;
}
Ok(())
}
}
impl<V: VisitorMut> VisitableMut<V> for Field {
fn default_visit_mut(self, v: &mut V) -> Result<Self> {
Ok(Self {
descriptor: v.visit_field_id(self.descriptor)?,
visibility: v.visit_field_visibility(self.visibility)?,
value: self.value.map(|val| v.visit_value(val)).transpose()?,
annotations: self
.annotations
.into_iter()
.map(|annot| v.visit_annotation_item(annot))
.collect::<Result<_>>()?,
hiddenapi: self
.hiddenapi
.map(|hiddenapi| v.visit_hidden_api_data(hiddenapi))
.transpose()?,
..self
})
}
}