androscalpel/androscalpel_serializer/src/annotation/mod.rs
2023-11-28 19:06:28 +01:00

291 lines
9.4 KiB
Rust

//! Annotation items.
pub mod encoded_annotation;
pub use encoded_annotation::*;
use crate as androscalpel_serializer;
use crate::{ReadSeek, Result, Serializable};
use std::io::Write;
/// <https://source.android.com/docs/core/runtime/dex-format#referenced-from-class_def_item_1>
/// alignment: 4 bytes
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AnnotationDirectoryItem {
/// 0 if they are no annotation, else offset of an [`crate::AnnotationSetItem`].
pub class_annotations_off: u32,
// pub fields_size: u32,
// pub annotated_methods_size: u32,
// pub annotated_parameters_size: u32,
/// List of field annotation. The field annotations must be sorted by
/// increasing order of [`crate::FieldAnnotation.field_idx`].
pub field_annotations: Vec<FieldAnnotation>,
/// List of method annotation. The method annotations must be sorted by
/// increasing order of [`crate::MethodAnnotation.method_idx`].
pub method_annotation: Vec<MethodAnnotation>,
/// List of associated method parameter annotation. The parameter annotations
/// must be sorted by increasing order of [`crate::ParameterAnnotation.parameter_size`].
pub parameter_annotations: Vec<ParameterAnnotation>,
}
impl AnnotationDirectoryItem {
pub fn fields_size_field(&self) -> u32 {
self.field_annotations.len() as u32
}
pub fn annotated_methods_size_field(&self) -> u32 {
self.method_annotation.len() as u32
}
pub fn parameter_annotations_field(&self) -> u32 {
self.parameter_annotations.len() as u32
}
}
impl Serializable for AnnotationDirectoryItem {
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
self.class_annotations_off.serialize(output)?;
self.fields_size_field().serialize(output)?;
self.annotated_methods_size_field().serialize(output)?;
self.parameter_annotations_field().serialize(output)?;
for item in &self.field_annotations {
item.serialize(output)?;
}
for item in &self.method_annotation {
item.serialize(output)?;
}
for item in &self.parameter_annotations {
item.serialize(output)?;
}
Ok(())
}
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
let class_annotations_off = u32::deserialize(input)?;
let fields_size = u32::deserialize(input)?;
let annotated_methods_size = u32::deserialize(input)?;
let annotated_parameters_size = u32::deserialize(input)?;
let mut field_annotations = vec![];
for _ in 0..fields_size {
field_annotations.push(FieldAnnotation::deserialize(input)?);
}
let mut method_annotation = vec![];
for _ in 0..annotated_methods_size {
method_annotation.push(MethodAnnotation::deserialize(input)?);
}
let mut parameter_annotations = vec![];
for _ in 0..annotated_parameters_size {
parameter_annotations.push(ParameterAnnotation::deserialize(input)?);
}
Ok(Self {
class_annotations_off,
field_annotations,
method_annotation,
parameter_annotations,
})
}
fn size(&self) -> usize {
self.class_annotations_off.size()
+ self.fields_size_field().size()
+ self.annotated_methods_size_field().size()
+ self.parameter_annotations_field().size()
+ self
.field_annotations
.iter()
.map(|val| val.size())
.sum::<usize>()
+ self
.method_annotation
.iter()
.map(|val| val.size())
.sum::<usize>()
+ self
.parameter_annotations
.iter()
.map(|val| val.size())
.sum::<usize>()
}
}
/// <https://source.android.com/docs/core/runtime/dex-format#field-annotation>
#[derive(Serializable, Debug, Clone, PartialEq, Eq)]
pub struct FieldAnnotation {
pub field_idx: u32,
/// Offset to a [`AnnotationSetItem`]
pub annotation_off: u32,
}
/// <https://source.android.com/docs/core/runtime/dex-format#method-annotation>
#[derive(Serializable, Debug, Clone, PartialEq, Eq)]
pub struct MethodAnnotation {
pub method_idx: u32,
/// Offset to a [`AnnotationSetItem`]
pub annotation_off: u32,
}
/// <https://source.android.com/docs/core/runtime/dex-format#parameter-annotation>
#[derive(Serializable, Debug, Clone, PartialEq, Eq)]
pub struct ParameterAnnotation {
pub field_idx: u32,
/// Offset of a [`AnnotationSetRefList`]
pub annotation_off: u32,
}
/// <https://source.android.com/docs/core/runtime/dex-format#set-ref-list>
/// alignment: 4 bytes
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AnnotationSetRefList {
// pub size: u32,
pub list: Vec<AnnotationSetRefItem>,
}
impl AnnotationSetRefList {
pub fn size_field(&self) -> u32 {
self.list.len() as u32
}
}
impl Serializable for AnnotationSetRefList {
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
self.size_field().serialize(output)?;
for item in &self.list {
item.serialize(output)?;
}
Ok(())
}
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
let size = u32::deserialize(input)?;
let mut list = vec![];
for _ in 0..size {
list.push(AnnotationSetRefItem::deserialize(input)?);
}
Ok(Self { list })
}
fn size(&self) -> usize {
self.size_field().size() + self.list.iter().map(|val| val.size()).sum::<usize>()
}
}
/// <https://source.android.com/docs/core/runtime/dex-format#set-ref-item>
#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)]
pub struct AnnotationSetRefItem {
/// 0 if there are no annotation, offset to a [`AnnotationSetItem`] else.
pub annotations_off: u32,
}
/// <https://source.android.com/docs/core/runtime/dex-format#annotation-set-item>
/// alignment: 4
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AnnotationSetItem {
// pub size: u32,
/// Must be sorted by increasing [`AnnotationOffItem.type_idx`].
pub entries: Vec<AnnotationOffItem>,
}
impl AnnotationSetItem {
pub fn size_field(&self) -> u32 {
self.entries.len() as u32
}
}
impl Serializable for AnnotationSetItem {
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
self.size_field().serialize(output)?;
for item in &self.entries {
item.serialize(output)?;
}
Ok(())
}
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
let size = u32::deserialize(input)?;
let mut entries = vec![];
for _ in 0..size {
entries.push(AnnotationOffItem::deserialize(input)?);
}
Ok(Self { entries })
}
fn size(&self) -> usize {
self.size_field().size() + self.entries.iter().map(|val| val.size()).sum::<usize>()
}
}
/// <https://source.android.com/docs/core/runtime/dex-format#off-item>
#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)]
pub struct AnnotationOffItem {
/// Offset to a [`crate::AnnotationItem`]
pub annotation_off: u32,
}
/// <https://source.android.com/docs/core/runtime/dex-format#annotation-item>
#[derive(Serializable, Debug, Clone, PartialEq, Eq)]
pub struct AnnotationItem {
pub visibility: AnnotationVisibility,
pub annotation: EncodedAnnotation,
}
// TODO: Enum or flags ?
/// <https://source.android.com/docs/core/runtime/dex-format#annotation-item>
#[derive(Serializable, Debug, Clone, PartialEq, Eq)]
#[prefix_type(u8)]
pub enum AnnotationVisibility {
#[prefix(0x00)]
Build,
#[prefix(0x01)]
Runtime,
#[prefix(0x02)]
System,
}
#[cfg(test)]
mod test {
use super::*;
const ANNOTATION_ITEM: &[u8] = &[
0x02, 0xbe, 0x37, 0x01, 0xa8, 0x9f, 0x03, 0x1d, 0xf6, 0x33, 0x01, 0xd3, 0xe8, 0x02, 0x3f,
];
const ANNOTATION_ITEM_2: &[u8] = &[0x02, 0xbf, 0x37, 0x01, 0xa8, 0x9f, 0x03, 0x38, 0x38, 0x02];
#[test]
fn deserialize_annotation_item() {
assert_eq!(
AnnotationItem::deserialize_from_slice(ANNOTATION_ITEM).unwrap(),
AnnotationItem {
visibility: AnnotationVisibility::System,
annotation: EncodedAnnotation::deserialize_from_slice(&ANNOTATION_ITEM[1..])
.unwrap(),
}
);
assert_eq!(
AnnotationItem::deserialize_from_slice(ANNOTATION_ITEM_2).unwrap(),
AnnotationItem {
visibility: AnnotationVisibility::System,
annotation: EncodedAnnotation::deserialize_from_slice(&ANNOTATION_ITEM_2[1..])
.unwrap(),
}
);
}
#[test]
fn serialize_annotation_item() {
assert_eq!(
ANNOTATION_ITEM,
AnnotationItem {
visibility: AnnotationVisibility::System,
annotation: EncodedAnnotation::deserialize_from_slice(&ANNOTATION_ITEM[1..])
.unwrap(),
}
.serialize_to_vec()
.unwrap()
.as_slice()
);
assert_eq!(
ANNOTATION_ITEM_2,
AnnotationItem {
visibility: AnnotationVisibility::System,
annotation: EncodedAnnotation::deserialize_from_slice(&ANNOTATION_ITEM_2[1..])
.unwrap(),
}
.serialize_to_vec()
.unwrap()
.as_slice()
);
}
}