use ddc_dec::{top_level_classes, ClassOptions, DexPool};
use ddc_dex::DexFile;
fn pool() -> std::sync::Arc<DexPool> {
let bytes = std::fs::read("tests/fixtures/hello.dex").unwrap();
let mut pool = DexPool::new();
pool.add_dex(DexFile::parse(bytes).unwrap());
std::sync::Arc::new(pool)
}
#[test]
fn decompiles_greeter() {
let pool = pool();
let greeter = pool.get("Greeter").expect("Greeter class");
let out = ddc_dec::classdec::decompile_class(
&pool,
greeter,
&ClassOptions::default(),
&std::sync::Mutex::new(Vec::new()),
)
.map_err(|e| anyhow::anyhow!("{:#}", e))
.unwrap();
assert!(out.contains("class Greeter {"), "header missing:\n{}", out);
assert!(
out.contains("private final java.lang.String name;"),
"field missing:\n{}",
out
);
assert!(
out.starts_with("// Decompiled by https://github.com/ejfkdev/ddc "),
"tool header missing:\n{}",
out
);
assert!(
out.contains("// From: dex 0 (DEX "),
"From header missing:\n{}",
out
);
assert!(
out.contains("return \"hi \" + this.name;"),
"concat not folded:\n{}",
out
);
assert!(!out.contains("StringBuilder"), "builder leaked:\n{}", out);
}
#[test]
fn decompiles_hello_main() {
let pool = pool();
let hello = pool.get("Hello").expect("Hello class");
let out = ddc_dec::classdec::decompile_class(
&pool,
hello,
&ClassOptions::default(),
&std::sync::Mutex::new(Vec::new()),
)
.map_err(|e| anyhow::anyhow!("{:#}", e))
.unwrap();
assert!(
out.contains("System.out.println(new Greeter(\"world\").greet());"),
"main not decompiled cleanly:\n{}",
out
);
assert!(
out.contains("counter++;"),
"static field update missing:\n{}",
out
);
}
#[test]
fn top_level_enumeration() {
let pool = pool();
let names = top_level_classes(&pool);
assert_eq!(names, vec!["Greeter".to_string(), "Hello".to_string()]);
}
#[test]
fn array_multiconsume_materializes_once() {
let bytes = std::fs::read("tests/fixtures/arrinit.dex").unwrap();
let mut pool = DexPool::new();
pool.add_dex(DexFile::parse(bytes).unwrap());
let pool = std::sync::Arc::new(pool);
let cls = pool.get("ArrInit").expect("ArrInit class");
let out = ddc_dec::classdec::decompile_class(
&pool,
cls,
&ClassOptions::default(),
&std::sync::Mutex::new(Vec::new()),
)
.map_err(|e| anyhow::anyhow!("{:#}", e))
.unwrap();
assert!(
out.contains("byte[] v0 = {1, 2, 3, 4"),
"fill-array-data literal:\n{}",
out
);
assert!(
out.contains("sparse = v0"),
"sput of the array local:\n{}",
out
);
assert!(
out.contains("v0[0] = (byte) 9;"),
"element write on the local:\n{}",
out
);
assert!(
out.contains("v0[3] = (byte) 7;"),
"second element write:\n{}",
out
);
let count = out.matches("new byte[4]").count();
assert_eq!(count, 1, "allocation count for sparse:\n{}", out);
}