295 lines
9 KiB
Rust
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
|
|
})
|
|
}
|
|
}
|