use std::collections::BTreeMap;
use droidsaw_dex::DexString;
use droidsaw_dex::annotation::{
AnnotationDirectoryItem, AnnotationItem, EncodedAnnotation, EncodedValue,
};
use droidsaw_dex::decode::EncodedMethod;
use droidsaw_dex::emit_dex::emit_class_data_item;
use droidsaw_dex::header::DexHeader;
use droidsaw_dex::ids::{ClassDefItem, MethodIdItem, MethodIdx, ProtoIdItem, StringIdx, TypeIdx};
use droidsaw_dex::parser::DexFile;
pub const DUP_TYPE_IDX: u32 = 0;
fn zero_header() -> DexHeader {
DexHeader {
magic: [0u8; 8],
checksum: 0,
signature: [0u8; 20],
file_size: 0,
header_size: 0,
endian_tag: 0,
link_size: 0,
link_off: 0,
map_off: 0,
string_ids_size: 0,
string_ids_off: 0,
type_ids_size: 0,
type_ids_off: 0,
proto_ids_size: 0,
proto_ids_off: 0,
field_ids_size: 0,
field_ids_off: 0,
method_ids_size: 0,
method_ids_off: 0,
class_defs_size: 0,
class_defs_off: 0,
data_size: 0,
data_off: 0,
}
}
#[allow(clippy::too_many_arguments)]
fn build(
strings: Vec<DexString>,
type_descriptors: Vec<String>,
protos: Vec<ProtoIdItem>,
methods: Vec<MethodIdItem>,
canonical: ClassDefItem,
shadow: ClassDefItem,
annotations: BTreeMap<u32, AnnotationDirectoryItem>,
annotation_sets: BTreeMap<u32, Vec<u32>>,
annotation_items: BTreeMap<u32, AnnotationItem>,
) -> DexFile {
let mut dex = DexFile {
header: zero_header(),
strings,
string_data_offs: Vec::new(),
type_descriptors,
protos,
fields: Vec::new(),
methods,
class_defs: vec![canonical, shadow],
annotations,
type_lists: BTreeMap::new(),
class_datas: BTreeMap::new(),
raw_class_data_bytes: BTreeMap::new(),
code_items: BTreeMap::new(),
debug_infos: BTreeMap::new(),
debug_info_raw_bytes: BTreeMap::new(),
debug_info_section_layout: Vec::new(),
annotation_set_section_layout: Vec::new(),
input_checksums_canonical: true,
annotation_sets,
annotation_set_ref_lists: BTreeMap::new(),
annotation_items,
annotation_item_widths: BTreeMap::new(),
encoded_arrays: BTreeMap::new(),
encoded_array_widths: BTreeMap::new(),
method_handles: Vec::new(),
map_entries: Vec::new(),
call_site_ids: Vec::new(),
parse_errors: Vec::new(),
class_def_index: Vec::new(),
};
dex.rebuild_class_def_index();
dex
}
fn class_def(annotations_off: u32, class_data_off: u32) -> ClassDefItem {
ClassDefItem {
class_idx: TypeIdx(DUP_TYPE_IDX),
access_flags: 0,
superclass_idx: None,
interfaces_off: 0,
source_file_idx: None,
annotations_off,
class_data_off,
static_values_off: 0,
}
}
pub struct NativeFixture {
pub dex: DexFile,
pub raw: Vec<u8>,
}
pub fn with_native_method_rows(
canonical_midx: u32,
canonical_native: bool,
shadow_midx: u32,
shadow_native: bool,
) -> NativeFixture {
const ACC_NATIVE: u32 = 0x0100;
let canonical_flags = if canonical_native { ACC_NATIVE } else { 0 };
let shadow_flags = if shadow_native { ACC_NATIVE } else { 0 };
let canonical_blob = emit_class_data_item(
&[],
&[],
&[EncodedMethod {
method_idx: MethodIdx(canonical_midx),
access_flags: canonical_flags,
code_off: 0x100,
}],
&[],
)
.expect("emit canonical class_data");
let shadow_blob = emit_class_data_item(
&[],
&[],
&[EncodedMethod {
method_idx: MethodIdx(shadow_midx),
access_flags: shadow_flags,
code_off: 0x200,
}],
&[],
)
.expect("emit shadow class_data");
let mut raw = vec![0u8];
let canonical_off = u32::try_from(raw.len()).expect("offset fits u32");
raw.extend_from_slice(&canonical_blob);
let shadow_off = u32::try_from(raw.len()).expect("offset fits u32");
raw.extend_from_slice(&shadow_blob);
let type_descriptors = vec!["LDup;".to_string(), "Ljava/lang/Object;".to_string()];
let protos = vec![ProtoIdItem {
shorty_idx: StringIdx(0),
return_type_idx: TypeIdx(1),
parameters_off: 0,
}];
let strings = vec![
DexString::from_decoded_str("V"),
DexString::from_decoded_str("canonMethod"),
DexString::from_decoded_str("shadowMethod"),
];
let max_midx = canonical_midx.max(shadow_midx) as usize;
let mut methods = Vec::with_capacity(max_midx.saturating_add(1));
for i in 0..=max_midx {
let i_u32 = u32::try_from(i).expect("method pool index fits u32");
let name_idx = if i_u32 == canonical_midx {
StringIdx(1)
} else if i_u32 == shadow_midx {
StringIdx(2)
} else {
StringIdx(0)
};
methods.push(MethodIdItem {
class_idx: TypeIdx(DUP_TYPE_IDX),
proto_idx: droidsaw_dex::ids::ProtoIdx(0),
name_idx,
});
}
let dex = build(
strings,
type_descriptors,
protos,
methods,
class_def(0, canonical_off),
class_def(0, shadow_off),
BTreeMap::new(),
BTreeMap::new(),
BTreeMap::new(),
);
NativeFixture { dex, raw }
}
pub fn for_export() -> DexFile {
let type_descriptors = vec!["LDup;".to_string(), "Ljava/lang/Object;".to_string()];
build(
vec![DexString::from_decoded_str("V")],
type_descriptors,
Vec::new(),
Vec::new(),
class_def(0, 0),
class_def(0, 0),
BTreeMap::new(),
BTreeMap::new(),
BTreeMap::new(),
)
}
pub fn for_react_module() -> DexFile {
const REACT_MODULE_DESC: &str = "Lcom/facebook/react/module/annotations/ReactModule;";
let type_descriptors = vec!["LDup;".to_string(), REACT_MODULE_DESC.to_string()];
let strings = vec![
DexString::from_decoded_str("name"),
DexString::from_decoded_str("CanonicalModule"),
DexString::from_decoded_str("ShadowModule"),
];
let canonical_item_off: u32 = 0x10;
let shadow_item_off: u32 = 0x20;
let mut annotation_items = BTreeMap::new();
let mut canon_elems = BTreeMap::new();
canon_elems.insert(StringIdx(0), EncodedValue::String(StringIdx(1)));
annotation_items.insert(
canonical_item_off,
AnnotationItem {
visibility: 0,
annotation: EncodedAnnotation {
type_idx: TypeIdx(1),
elements: canon_elems,
},
},
);
let mut shadow_elems = BTreeMap::new();
shadow_elems.insert(StringIdx(0), EncodedValue::String(StringIdx(2)));
annotation_items.insert(
shadow_item_off,
AnnotationItem {
visibility: 0,
annotation: EncodedAnnotation {
type_idx: TypeIdx(1),
elements: shadow_elems,
},
},
);
let canonical_set_off: u32 = 0x40;
let shadow_set_off: u32 = 0x50;
let mut annotation_sets = BTreeMap::new();
annotation_sets.insert(canonical_set_off, vec![canonical_item_off]);
annotation_sets.insert(shadow_set_off, vec![shadow_item_off]);
let canonical_dir_off: u32 = 0x80;
let shadow_dir_off: u32 = 0x90;
let mut annotations = BTreeMap::new();
annotations.insert(
canonical_dir_off,
AnnotationDirectoryItem {
class_annotations_off: canonical_set_off,
fields: Vec::new(),
methods: Vec::new(),
parameters: Vec::new(),
},
);
annotations.insert(
shadow_dir_off,
AnnotationDirectoryItem {
class_annotations_off: shadow_set_off,
fields: Vec::new(),
methods: Vec::new(),
parameters: Vec::new(),
},
);
build(
strings,
type_descriptors,
Vec::new(),
Vec::new(),
class_def(canonical_dir_off, 0),
class_def(shadow_dir_off, 0),
annotations,
annotation_sets,
annotation_items,
)
}