add annotations to generated dex

This commit is contained in:
Jean-Marie Mineau 2023-12-12 11:25:33 +01:00
parent 53d321c7fe
commit bd22b7990b
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
8 changed files with 599 additions and 38 deletions

89
TODO.md
View file

@ -2,7 +2,94 @@
- generate .dex
- code
- edditable code format
- json serialize with serde
- sanity checks
- tests
- DexValues will become a problem ? (eg need to clone the vector for the array)
- PyRef
- https://source.android.com/docs/core/runtime/dex-format#system-annotation
```
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::hash::Hash;
pub mod vectorize {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::iter::FromIterator;
pub fn serialize<'a, T, K, V, S>(target: T, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: IntoIterator<Item = (&'a K, &'a V)>,
K: Serialize + 'a,
V: Serialize + 'a,
{
let container: Vec<_> = target.into_iter().collect();
serde::Serialize::serialize(&container, ser)
}
pub fn deserialize<'de, T, K, V, D>(des: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: FromIterator<(K, V)>,
K: Deserialize<'de>,
V: Deserialize<'de>,
{
let container: Vec<_> = serde::Deserialize::deserialize(des)?;
Ok(T::from_iter(container.into_iter()))
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
struct KeyItem {
name: String,
len: u32,
}
impl KeyItem {
fn new(s: &str) -> Self {
Self {
name: s.into(),
len: s.len() as u32,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
enum ValueItem {
String1(String),
String2(String),
Int(u32),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
struct ToSerialize {
#[serde(with = "vectorize")]
map1: HashMap<KeyItem, ValueItem>,
#[serde(with = "vectorize")]
map2: HashMap<KeyItem, ValueItem>,
}
fn main() {
let value = ToSerialize {
map1: HashMap::from([
(KeyItem::new("plop"), ValueItem::String1("Plop".into())),
(KeyItem::new("plip"), ValueItem::String2("Plip".into())),
(KeyItem::new("lop"), ValueItem::Int(109)),
]),
map2: HashMap::from([(
KeyItem::new("run"),
ValueItem::String1("Demons run when a good man goes to war".into()),
)]),
};
let serialized = serde_json::to_string(&value).unwrap();
println!("{serialized}");
assert_eq!(
serde_json::from_str::<ToSerialize>(&serialized).unwrap(),
value
);
}
```
`cargo add serde -F derive && cargo add serde_json`

View file

@ -85,7 +85,7 @@ impl Apk {
let annotations_directory = if class_item.annotations_off == 0 {
None
} else {
Some(dex.get_struct_at_offset::<AnnotationDirectoryItem>(class_item.annotations_off)?)
Some(dex.get_struct_at_offset::<AnnotationsDirectoryItem>(class_item.annotations_off)?)
};
let mut annotations = vec![];
if let Some(annotations_directory) = &annotations_directory {

View file

@ -306,6 +306,27 @@ impl Class {
.any(|field| field.value.is_some())
}
/// If the class or its fields/methods have annotations
pub fn has_annotations(&self) -> bool {
!self.annotations.is_empty()
|| self
.static_fields
.values()
.any(|field| field.has_annotations())
|| self
.instance_fields
.values()
.any(|field| field.has_annotations())
|| self
.direct_methods
.values()
.any(|field| field.has_annotations())
|| self
.virtual_methods
.values()
.any(|field| field.has_annotations())
}
/// Return the binary representation of access flags.
pub fn get_raw_access_flags(&self) -> u32 {
let mut flags = 0u32;

View file

@ -39,12 +39,6 @@ pub struct DexWriter {
/// index in the `class_defs` section.
class_defs: HashMap<IdType, (Class, usize)>,
// call_site_ids: // TODO: parsing code insns
// method_handles are not deduplicated nor ordered, so they must be serialized on the fly.
// TODO: other structs in data:
// **map_list**, prbl generate on write
// values
// annotations
//
/// A struct that keep track of sections size, nb elements and offsets.
section_manager: SectionManager,
/// The string_data section. Once populated, the elements must be sorted according to the spec.
@ -72,6 +66,14 @@ pub struct DexWriter {
type_lists_with_offset: Vec<(TypeList, u32)>,
/// The encoded_array_items section.
encoded_array_items: Vec<EncodedArrayItem>,
/// The annotations_directory_item section.
annotations_directory_items: Vec<AnnotationsDirectoryItem>,
/// The annotation_set_item section.
annotation_set_items: Vec<AnnotationSetItem>,
/// The annotation item section.
annotation_items: Vec<AnnotationItem>,
/// The annotation_set_ref_list section.
annotation_set_lists: Vec<AnnotationSetRefList>,
/// The method_handles section.
method_handles: Vec<MethodHandleItem>,
/// The code_items sections.
@ -130,6 +132,10 @@ impl Default for DexWriter {
encoded_array_items: vec![],
method_handles: vec![],
code_items: vec![],
annotations_directory_items: vec![],
annotation_set_items: vec![],
annotation_items: vec![],
annotation_set_lists: vec![],
}
}
}
@ -418,16 +424,19 @@ impl DexWriter {
Ok(())
}
/// Insert annotation associated to a class.
///
/// Insert a class_data_item in the class_data section (in data).
///
/// # Note
///
/// code_item object are 4 bytes aligns, so code_item_off cannot be odd.
/// code_item objects are 4 bytes aligns, so their offset cannot be odd.
///
/// To distinguish prelinked value (offset inside the code_item section) to actual values (offset
/// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow
/// to distinguish the offset of the first code item (equal to zero before linking) and the value
/// 0 used to indicate an abscence of code.
/// to distinguish the offset of the first item (equal to zero before linking) and the value
/// 0 used to indicate an abscence of item.
fn insert_class_data_item(&mut self, class_id: &IdType) -> Result<()> {
let mut data = ClassDataItem::default();
let (class, _) = self.class_defs.get(class_id).unwrap();
@ -685,7 +694,9 @@ impl DexWriter {
}
Ok(EncodedValue::Array(EncodedArray { values }))
}
DexValue::Annotation(_) => todo!(), // Ok(EncodedValue::Annotation(todo!())),
DexValue::Annotation(val) => Ok(EncodedValue::Annotation(
self.dex_annotation_to_encoded_annotation(val.clone())?,
)),
DexValue::Null(DexNull) => Ok(EncodedValue::Null),
DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)),
}
@ -740,6 +751,349 @@ impl DexWriter {
})
}
fn dex_annotation_to_encoded_annotation(
&mut self,
DexAnnotation { type_, elements }: DexAnnotation,
) -> Result<EncodedAnnotation> {
let mut encoded_elements = vec![];
let mut elements_names: Vec<_> = elements.keys().collect();
elements_names.sort();
for name in elements_names {
let elt = elements.get(name).unwrap();
encoded_elements.push(AnnotationElement {
name_idx: Uleb128(*self.strings.get(name).ok_or(anyhow!(
"{} (annotation element name) not found in dex builder",
name.__str__()
))? as u32),
value: self.dex_value_to_encoded_value(elt)?,
});
}
Ok(EncodedAnnotation {
type_idx: Uleb128(*self.type_ids.get(&type_).ok_or(anyhow!(
"Annotation type {} not found in dex builder",
type_.__repr__(),
))? as u32),
elements: encoded_elements,
})
}
/// Insert the annnotations set for a class.
fn insert_class_annotation_set(&mut self, class_id: &IdType) -> Result<()> {
let (class, _) = self.class_defs.get(class_id).unwrap();
let mut annotations = class.annotations.clone();
let mut annotation_set = AnnotationSetItem { entries: vec![] };
annotations.sort_by_key(|annot| annot.annotation.type_.clone());
for annot in annotations {
annotation_set.entries.push(AnnotationOffItem {
annotation_off: self.section_manager.get_size(Section::AnnotationItem),
}); // linked in link_annotations()
let item = AnnotationItem {
visibility: match (annot.visibility_build, annot.visibility_runtime, annot.visibility_system) {
(true, false, false) => AnnotationVisibility::Build,
(false, true, false) => AnnotationVisibility::Runtime,
(false, false, true) => AnnotationVisibility::System,
_ => bail!("Annotation need visibility set to one and only one of build, runtime or system"), // TODO: check if this is true
},
annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?,
};
self.section_manager
.add_elt(Section::AnnotationItem, Some(item.size()));
self.annotation_items.push(item);
}
self.section_manager
.add_elt(Section::AnnotationSetItem, Some(annotation_set.size()));
self.annotation_set_items.push(annotation_set);
Ok(())
}
/// Insert the annnotations set for a class.
fn insert_field_annotation_set(
&mut self,
field_id: &IdField,
is_static_field: bool,
) -> Result<()> {
let (class, _) = self.class_defs.get(&field_id.class_).unwrap();
let field = if is_static_field {
class.static_fields.get(field_id).unwrap()
} else {
class.instance_fields.get(field_id).unwrap()
};
let mut annotations = field.annotations.clone();
let mut annotation_set = AnnotationSetItem { entries: vec![] };
annotations.sort_by_key(|annot| annot.annotation.type_.clone());
for annot in annotations {
annotation_set.entries.push(AnnotationOffItem {
annotation_off: self.section_manager.get_size(Section::AnnotationItem),
}); // linked in link_annotations()
let item = AnnotationItem {
visibility: match (annot.visibility_build, annot.visibility_runtime, annot.visibility_system) {
(true, false, false) => AnnotationVisibility::Build,
(false, true, false) => AnnotationVisibility::Runtime,
(false, false, true) => AnnotationVisibility::System,
_ => bail!("Annotation need visibility set to one and only one of build, runtime or system"), // TODO: check if this is true
},
annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?,
};
self.section_manager
.add_elt(Section::AnnotationItem, Some(item.size()));
self.annotation_items.push(item);
}
self.section_manager
.add_elt(Section::AnnotationSetItem, Some(annotation_set.size()));
self.annotation_set_items.push(annotation_set);
Ok(())
}
/// Insert the annnotations set for a method (but not the parameters annotations).
fn insert_method_annotation_set(
&mut self,
method_id: &IdMethod,
is_direct_method: bool,
) -> Result<()> {
let (class, _) = self.class_defs.get(&method_id.class_).unwrap();
let method = if is_direct_method {
class.direct_methods.get(method_id).unwrap()
} else {
class.virtual_methods.get(method_id).unwrap()
};
let mut annotations = method.annotations.clone();
let mut annotation_set = AnnotationSetItem { entries: vec![] };
annotations.sort_by_key(|annot| annot.annotation.type_.clone());
for annot in annotations {
annotation_set.entries.push(AnnotationOffItem {
annotation_off: self.section_manager.get_size(Section::AnnotationItem),
}); // linked in link_annotations()
let item = AnnotationItem {
visibility: match (annot.visibility_build, annot.visibility_runtime, annot.visibility_system) {
(true, false, false) => AnnotationVisibility::Build,
(false, true, false) => AnnotationVisibility::Runtime,
(false, false, true) => AnnotationVisibility::System,
_ => bail!("Annotation need visibility set to one and only one of build, runtime or system"), // TODO: check if this is true
},
annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?,
};
self.section_manager
.add_elt(Section::AnnotationItem, Some(item.size()));
self.annotation_items.push(item);
}
self.section_manager
.add_elt(Section::AnnotationSetItem, Some(annotation_set.size()));
self.annotation_set_items.push(annotation_set);
Ok(())
}
/// Insert the annotations set for a method parameter.
fn insert_parameters_annotation_set(
&mut self,
method_id: &IdMethod,
is_direct_method: bool,
parameter_idx: usize,
) -> Result<()> {
let (class, _) = self.class_defs.get(&method_id.class_).unwrap();
let method = if is_direct_method {
class.direct_methods.get(method_id).unwrap()
} else {
class.virtual_methods.get(method_id).unwrap()
};
let mut annotations = method.parameters_annotations[parameter_idx].clone();
let mut annotation_set = AnnotationSetItem { entries: vec![] };
annotations.sort_by_key(|annot| annot.annotation.type_.clone());
for annot in annotations {
annotation_set.entries.push(AnnotationOffItem {
annotation_off: self.section_manager.get_size(Section::AnnotationItem),
}); // linked in link_annotations()
let item = AnnotationItem {
visibility: match (annot.visibility_build, annot.visibility_runtime, annot.visibility_system) {
(true, false, false) => AnnotationVisibility::Build,
(false, true, false) => AnnotationVisibility::Runtime,
(false, false, true) => AnnotationVisibility::System,
_ => bail!("Annotation need visibility set to one and only one of build, runtime or system"), // TODO: check if this is true
},
annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?,
};
self.section_manager
.add_elt(Section::AnnotationItem, Some(item.size()));
self.annotation_items.push(item);
}
self.section_manager
.add_elt(Section::AnnotationSetItem, Some(annotation_set.size()));
self.annotation_set_items.push(annotation_set);
Ok(())
}
/// Insert the annotations set list for a method parameters.
fn insert_parameters_annotation_set_list(
&mut self,
method_id: &IdMethod,
is_direct_method: bool,
) -> Result<()> {
let mut list = AnnotationSetRefList { list: vec![] };
let (class, _) = self.class_defs.get(&method_id.class_).unwrap();
let method = if is_direct_method {
class.direct_methods.get(method_id).unwrap()
} else {
class.virtual_methods.get(method_id).unwrap()
};
let param_has_annotation: Vec<_> = method
.parameters_annotations
.iter()
.map(|annots| !annots.is_empty())
.collect();
for (param_idx, has_annotation) in param_has_annotation.into_iter().enumerate() {
list.list.push(AnnotationSetRefItem {
annotations_off: if has_annotation {
let annotation_off = self.section_manager.get_size(Section::AnnotationSetItem);
self.insert_parameters_annotation_set(method_id, is_direct_method, param_idx)?;
annotation_off + 1
} else {
0
}, // TODO: link
});
}
self.section_manager
.add_elt(Section::AnnotationSetRefList, Some(list.size()));
self.annotation_set_lists.push(list);
Ok(())
}
/// Insert a class annotations (including field, methods and parameters annotations).
///
/// # Note
///
/// annotation_set_item objects are 4 bytes aligns, so their offset cannot be odd.
///
/// To distinguish prelinked value (offset inside the code_item section) to actual values (offset
/// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow
/// to distinguish the offset of the first item (equal to zero before linking) and the value
/// 0 used to indicate an abscence of item.
fn insert_annotations(&mut self, class_id: &IdType) -> Result<()> {
let (class, _) = self.class_defs.get(class_id).unwrap();
let class_annotations_off = if !class.annotations.is_empty() {
let class_annotations_off = self.section_manager.get_size(Section::AnnotationSetItem);
self.insert_class_annotation_set(class_id)
.with_context(|| {
format!(
"Failed to insert class annotation for class {}",
class_id.__repr__()
)
})?;
class_annotations_off + 1
} else {
0
};
let mut field_ids = vec![];
let (class, _) = self.class_defs.get(class_id).unwrap();
field_ids.extend(class.static_fields.keys().cloned());
field_ids.extend(class.instance_fields.keys().cloned());
field_ids.sort();
let mut field_annotations = vec![];
for field_id in field_ids {
let (class, _) = self.class_defs.get(class_id).unwrap();
let static_field = class.static_fields.get(&field_id);
let instance_field = class.instance_fields.get(&field_id);
let (is_static, field) = match (static_field, instance_field) {
(Some(field), None) => (true, field),
(None, Some(field)) => (false, field),
_ => bail!(
"Unexpected configuration: field {} is both a static and a instance field in {}",
field_id.__repr__(), class_id.__repr__()),
};
if !field.annotations.is_empty() {
let annotations_off = self.section_manager.get_size(Section::AnnotationSetItem) + 1;
self.insert_field_annotation_set(&field_id, is_static)?;
field_annotations.push(FieldAnnotation {
field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!(
"Field {} in {} not found in dex builder",
field_id.__repr__(),
class_id.__repr__(),
))? as u32,
annotations_off, // linked in link_annotations()
});
}
}
let mut method_ids = vec![];
let (class, _) = self.class_defs.get(class_id).unwrap();
method_ids.extend(class.direct_methods.keys().cloned());
method_ids.extend(class.virtual_methods.keys().cloned());
method_ids.sort();
let mut method_annotations = vec![];
for method_id in &method_ids {
let (class, _) = self.class_defs.get(class_id).unwrap();
let direct_method = class.direct_methods.get(method_id);
let virtual_method = class.virtual_methods.get(method_id);
let (is_direct, method) = match (direct_method, virtual_method) {
(Some(method), None) => (true, method),
(None, Some(method)) => (false, method),
_ => bail!(
"Unexpected configuration: method {} is both a direct and a virtual method in {}",
method_id.__repr__(), class_id.__repr__()),
};
if !method.annotations.is_empty() {
let annotations_off = self.section_manager.get_size(Section::AnnotationSetItem) + 1;
self.insert_method_annotation_set(method_id, is_direct)?;
method_annotations.push(MethodAnnotation {
method_idx: *self.method_ids.get(method_id).ok_or(anyhow!(
"Method {} in {} not found in dex builder",
method_id.__repr__(),
class_id.__repr__(),
))? as u32,
annotations_off, // linked in link_annotations()
});
}
}
let mut parameter_annotations = vec![];
for method_id in method_ids {
let (class, _) = self.class_defs.get(class_id).unwrap();
let direct_method = class.direct_methods.get(&method_id);
let virtual_method = class.virtual_methods.get(&method_id);
let (is_direct, method) = match (direct_method, virtual_method) {
(Some(method), None) => (true, method),
(None, Some(method)) => (false, method),
_ => bail!(
"Unexpected configuration: method {} is both a direct and a virtual method in {}",
method_id.__repr__(), class_id.__repr__()),
};
if !method.parameters_annotations.is_empty() {
let annotations_off =
self.section_manager.get_size(Section::AnnotationSetRefList) + 1;
self.insert_parameters_annotation_set_list(&method_id, is_direct)?;
parameter_annotations.push(ParameterAnnotation {
method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!(
"Method {} in {} not found in dex builder",
method_id.__repr__(),
class_id.__repr__(),
))? as u32,
annotations_off, // linked in link_annotations()
});
}
}
let item = AnnotationsDirectoryItem {
class_annotations_off, // linked in link_annotations()
field_annotations,
method_annotations,
parameter_annotations,
};
self.section_manager
.add_elt(Section::AnnotationsDirectoryItem, Some(item.size()));
self.annotations_directory_items.push(item);
Ok(())
}
/// Insert a class_def_item in the class_defs section **and** the other struct that needs to be
/// generated on the fly.
///
@ -748,6 +1102,16 @@ impl DexWriter {
/// The class_defs section **MUST** be sorted by inheritance dependencies (parents classes and
/// interfaces must appear **before** child classes). Accordingly, this method must be invoked
/// in the right order.
///
/// # Note
///
/// annotations_directory_item, encoded_array_item and class_data_item objects are 4 bytes
/// aligns, so their offset cannot be odd.
///
/// To distinguish prelinked value (offset inside the code_item section) to actual values (offset
/// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow
/// to distinguish the offset of the first item (equal to zero before linking) and the value
/// 0 used to indicate an abscence of item.
fn insert_class_def_item(&mut self, class_id: &IdType) -> Result<()> {
let idx = self.class_defs_list.len();
self.class_defs
@ -757,7 +1121,7 @@ impl DexWriter {
let class_data_off = if class.has_data_item() {
let class_data_off = self.section_manager.get_size(Section::ClassDataItem);
self.insert_class_data_item(class_id)?;
class_data_off
class_data_off + 1
} else {
0
};
@ -767,7 +1131,17 @@ impl DexWriter {
let static_values_off = if class.has_static_values_array() {
let static_values_off = self.section_manager.get_size(Section::EncodedArrayItem);
self.insert_class_static_values(class_id)?;
static_values_off
static_values_off + 1
} else {
0
};
let (class, _) = self.class_defs.get(class_id).unwrap();
let annotations_off = if class.has_annotations() {
let annotations_off = self
.section_manager
.get_size(Section::AnnotationsDirectoryItem);
self.insert_annotations(class_id)?;
annotations_off + 1
} else {
0
};
@ -799,9 +1173,9 @@ impl DexWriter {
NO_INDEX.0
},
annotations_off: 0, // TODO
class_data_off, // need relinking once offset of class_data section is known
static_values_off, // need relinking once offset of encoded_array section is known
annotations_off, // need relinking once offset of class_def_item section is known
class_data_off, // need relinking once offset of class_data section is known
static_values_off, // need relinking once offset of encoded_array section is known
});
self.section_manager.add_elt(Section::ClassDefItem, None);
Ok(())
@ -960,10 +1334,11 @@ impl DexWriter {
/// Linking can only occur once all sections are entirelly generated.
fn link_class_data_occurences(&mut self) -> Result<()> {
debug!("Link the class_data_item entries in class_def_items");
for (class, idx) in self.class_defs.values() {
if class.has_data_item() {
self.class_defs_list[*idx].class_data_off +=
self.section_manager.get_offset(Section::ClassDataItem);
for class_def in self.class_defs_list.iter_mut() {
// prelink value is set to offset in the section + 1 (to distinguish with 0)
if class_def.class_data_off != 0 {
class_def.class_data_off +=
self.section_manager.get_offset(Section::ClassDataItem) - 1;
}
}
Ok(())
@ -976,16 +1351,20 @@ impl DexWriter {
/// Linking can only occur once all sections are entirelly generated.
fn link_static_values(&mut self) -> Result<()> {
debug!("Link the static_values entries in class_def_items");
for (class, idx) in self.class_defs.values() {
if class.has_static_values_array() {
self.class_defs_list[*idx].class_data_off +=
self.section_manager.get_offset(Section::EncodedArrayItem);
for class_def in self.class_defs_list.iter_mut() {
if class_def.static_values_off != 0 {
class_def.static_values_off +=
self.section_manager.get_offset(Section::EncodedArrayItem) - 1;
}
}
Ok(())
}
/// Link the offsets of code item in class_data_items.
///
/// # Warning
///
/// Linking can only occur once all sections are entirelly generated.
fn link_code_item(&mut self) -> Result<()> {
debug!("Link the code_item entries in class_data_items");
for data in &mut self.class_data_list {
@ -1003,8 +1382,55 @@ impl DexWriter {
Ok(())
}
/// Link all annotations objects.
///
/// # Warning
///
/// Linking can only occur once all sections are entirelly generated.
fn link_annotations(&mut self) -> Result<()> {
for annotation in self.annotations_directory_items.iter_mut() {
if annotation.class_annotations_off != 0 {
annotation.class_annotations_off +=
self.section_manager.get_offset(Section::AnnotationSetItem) - 1;
}
for field_annotation in annotation.field_annotations.iter_mut() {
if field_annotation.annotations_off != 0 {
field_annotation.annotations_off +=
self.section_manager.get_offset(Section::AnnotationSetItem) - 1;
}
}
for method_annotation in annotation.method_annotations.iter_mut() {
if method_annotation.annotations_off != 0 {
method_annotation.annotations_off +=
self.section_manager.get_offset(Section::AnnotationSetItem) - 1;
}
}
for parameter_annotation in annotation.parameter_annotations.iter_mut() {
if parameter_annotation.annotations_off != 0 {
parameter_annotation.annotations_off += self
.section_manager
.get_offset(Section::AnnotationSetRefList)
- 1;
}
}
}
for annotation_set in self.annotation_set_items.iter_mut() {
for entry in annotation_set.entries.iter_mut() {
entry.annotation_off += self.section_manager.get_offset(Section::AnnotationItem);
}
}
for list in self.annotation_set_lists.iter_mut() {
for annotation in list.list.iter_mut() {
if annotation.annotations_off != 0 {
annotation.annotations_off +=
self.section_manager.get_offset(Section::AnnotationSetItem) - 1;
}
}
}
Ok(())
}
fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> {
// TODO: SPLIT THIS IN METHODS !!!
self.section_manager.reset();
self.section_manager.add_elt(Section::HeaderItem, None);
@ -1027,6 +1453,7 @@ impl DexWriter {
self.link_class_data_occurences()?;
self.link_static_values()?;
self.link_code_item()?;
self.link_annotations()?;
debug!("Serialize the dex file");
// TODO: compute checksum, hash, ect
@ -1083,29 +1510,38 @@ impl DexWriter {
self.section_manager.incr_section_size(Section::TypeList, 1);
offset += 1;
}
// TODO: AnnotationSetRefList,
// TODO: AnnotationSetItem,
// AnnotationSetRefList section
for list in &self.annotation_set_lists {
list.serialize(writer)?;
}
// AnnotationSetItem section
for set in &self.annotation_set_items {
set.serialize(writer)?;
}
// ClassDataItem section
for data in &self.class_data_list {
data.serialize(writer)?;
}
// CodeItem section
for code_item in &self.code_items {
// TODO: !!! fix the handler stub issue with handler offset that change when the
// type_idx change
code_item.serialize(writer)?
}
for string in &self.string_data_list {
string.serialize(writer)?;
}
// TODO: DebugInfoItem,
// TODO: AnnotationItem,
// AnnotationItem section
for annot in &self.annotation_items {
annot.serialize(writer)?;
}
// TODO: EncodedArrayItem: partialy done
for array in &self.encoded_array_items {
array.serialize(writer)?;
}
// TODO: AnnotationsDirectoryItem,
// AnnotationsDirectoryItem section
for dir in &self.annotations_directory_items {
dir.serialize(writer)?;
}
// TODO: HiddenapiClassDataItem,
Ok(())

View file

@ -232,4 +232,9 @@ impl Field {
}
flags
}
/// If the fields has annotations
pub fn has_annotations(&self) -> bool {
!self.annotations.is_empty()
}
}

View file

@ -268,4 +268,13 @@ impl Method {
flags
}
/// If the fields has annotations
pub fn has_annotations(&self) -> bool {
!self.annotations.is_empty()
|| !self
.parameters_annotations
.iter()
.any(|list| !list.is_empty())
}
}

View file

@ -10,7 +10,7 @@ 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 {
pub struct AnnotationsDirectoryItem {
/// 0 if they are no annotation, else offset of an [`crate::AnnotationSetItem`].
pub class_annotations_off: u32,
// pub fields_size: u32,
@ -27,7 +27,7 @@ pub struct AnnotationDirectoryItem {
pub parameter_annotations: Vec<ParameterAnnotation>,
}
impl AnnotationDirectoryItem {
impl AnnotationsDirectoryItem {
pub fn fields_size_field(&self) -> u32 {
self.field_annotations.len() as u32
}
@ -39,7 +39,7 @@ impl AnnotationDirectoryItem {
}
}
impl Serializable for AnnotationDirectoryItem {
impl Serializable for AnnotationsDirectoryItem {
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
self.class_annotations_off.serialize(output)?;
self.fields_size_field().serialize(output)?;

View file

@ -116,7 +116,10 @@ use syn::{
/// ```
///
///
#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type, suffix, default_variant))]
#[proc_macro_derive(
Serializable,
attributes(until, prefix, prefix_type, suffix, default_variant)
)]
pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
@ -723,7 +726,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
},
}
});
let mut default_variant = None;
let mut default_variant = None;
for v in &data.variants {
let v_params = ParamsVariant::parse(&v.attrs);
if v_params.default_variant.is_some() && v_params.prefix.is_some() {