[English] | 简体中文
ddc is a three-crate workspace. The machine-specific front-end lives in
ddc-dex/ddc-dec; the machine-neutral core (CFG structuring, statement
conversion, Java emission) is jdc-core
— the same core jcdc drives for JVM
classfiles:
ddc-dex (machine-specific) jdc-core (machine-neutral)
──────────────────────── ─────────────────────────
DEX parsing (header/ids/class_def/code) cfg::Cfg blocks + edges
Dalvik decode (all opcodes + payloads) ir::BlockResult stmts + term
register→IR lifting (expr/phi) ───► structure/sese region tree
machine idioms (ctor fold, SB, …) convert::Converter Stmt tree
refinement passes (copy fwd, types) emit::Printer Java source
DexFile: header / string (MUTF-8) / type / proto / field / method / class_def — lazy, fault-tolerant parsing (out-of-range indices return sentinels instead of failing).insn: every Dalvik opcode (0x00–0xff, including the 038 additionsinvoke-polymorphic/invoke-custom/ method handles) and the three payload pseudo-instructions; all instructions carry absolute jump targets. The nibble layouts follow the two familiesB|A|op(12x/11n/22c/22t/22s) andA|G|op(35c).code:code_item+ try tables +encoded_catch_handler(both handler_off offset conventions).annotations: encoded_value / nested-class annotations / static field initial values.
- lift.rs: the register file is modeled as a per-register value view:
Live(v)(a local, statement already emitted),Pending(e)(a pure expression, stored for delayed nesting),PendingCall(e)(a call result consumed exactly once). Non-pure views materialize at block exits; only pure values, locals and opennewviews propagate across blocks. Constructor folding (new-instance+invoke-direct <init>), d8's statement-chainedStringBuilder.appendwith discarded results, andfill-array-datafilling are handled here. - method.rs: fixpoint iteration + merged phis (diverging registers emit
assignments ordered by write-pc; values referencing sibling phis are
snapshotted into temporaries so register rotation
a=b; b=a%bis expressed correctly). When the entry is a loop head (d8 back-edges to pc 0) the entry-side phi initializes from aLocalDefat the top of the body; exception-handler entries approximate with the try-entry state. - passes.rs: catch-parameter binding, ternary folding (
if/elseassigning the same variable),StringBuilderchains →+concatenation (statement-level append absorption),synchronizedrecovery (d8 monitor patterns), single-use temporary copy-forward (pure values anywhere, impure only adjacent; multi-assignment references guard against stale captures), evidence-driven type inference (call-site parameter/return/field/receiver), booleanization and null comparisons, declaration cleanup. - ctx.rs: the
jdc_core::Ctximplementation for DEX (nested classes from dalvik annotations + the$heuristic; type hierarchy walks; constructor arity queries). - classdec.rs: class headers / fields (static initializers) / method
signatures / nested member-class inline rendering; anonymous / local /
-$$Lambda$classes each get their own file.
Ships a minimal ZIP reader (stored + deflate); APK dexes merge in numeric
classes.dex, classes2.dex, … order (first definition wins for duplicate
classes). AXML decoding (string pools UTF-16/UTF-8, typedValue rendering)
lives in axml.rs; manifest facts and resource entry extraction in
manifest.rs; the progressive browse subcommands in browse.rs;
bilingual message selection in lang.rs.
All standard versions: 035, 037, 038, 039, 040, 041 (magic validated; unknown versions rejected — 036 was an unofficial odex-era marker that ART also rejects). The container layout is identical across versions; the differences are feature signals:
- 035: the base format (the vast majority of APKs)
- 037+:
invoke-polymorphic(45cc/4rcc),invoke-custom(35c/3rc),const-method-handle,const-method-type - 038/039: d8 marks these for min-api ≥ 26 / 28 (default methods / invokedynamic without desugaring signals)
- 040/041: ART internal markers (verification state); container unchanged
- The
call_site_id(map 0x0007) andmethod_handle(map 0x0008,HHHHlayout) tables are parsed from the map_list. LambdaMetafactorysites render as real Java: non-capturing lambdas as(a0) -> Cls.lambda$run$0(a0), method references asCls::name(SAM parameters taken from the instantiated-type linkage arguments).StringConcatFactorysites parse the recipe and fold back into+concatenation expressions.const-method-handle→Cls::nameliterals.