Releases: ejfkdev/ddc
Release list
v0.1.8
v0.1.8 — quality rounds 59-64 + determinism hardening
Code quality (gold standard: full javac + android.jar, 4 real-world APKs):
- LOCAL-OK-gated renames for field-obscured nested types (JLS 6.4.2
shadow family, flutter g$i shape): a full-corpus dex reference scan
(new refscan module) gates each rename to provably file-local blast
radius, plus the shadow-package twin rule. Ungated global renaming
was measured catastrophic on conflict-dense corpora (weibo +36916). - Conditional-super restructuring, 5 mechanical shapes: if/else branch
merge with per-arg ternaries, straight-line inline-hoist, duplicate
delegation dedup (R8 path-duplicated Kotlin default-arg bridges),
throw-guard branches, chained guards via recursive normalization.
"super must be first statement" errors: 513 -> 142. - Enum constant ctor args resolved through clinit reaching-definitions
(R8 register reuse across constant constructions): no more
DEBUG(var0)dummy-name garbage. - Static-field owner on synthetic holders prints the concrete anonymous
name (LongMath$1.$SwitchMap$..., wasObject.$SwitchMap$...).
Determinism (byte-identical output across runs restored):
- Root cause: a process-wide shared fallback VarInfo cached whichever
dangling var id hit it first (jdc-core) — now per-id. - Accessor code snapshotted before image retirement (inline_accessors
cross-image reads raced retirement). - CodeItem::parse only reads the catch-handler list when tries exist
(spec 8.3.1) — exact-sized snapshots used to fail parsing, silently
disabling accessor inlining corpus-wide. - jdc-core walk gains a deterministic work budget (Σ universe per
visit) behind the wall-clock backstop.
Gold standard totals (vs v0.1.7): reqable 15987 -> 14658,
weibo 155335 -> 154560, lark 68842 -> 64222, weixin 298275 -> 297003
(net -7996). Decomp times unchanged (weibo ~6.5s, weixin ~12s).
67 tests green; zero compiler/clippy warnings in ddc + jdc-core 0.2.3
(its never_loop correctness lint fixed); jdc-core bumped to 0.2.3.
Full Changelog: v0.1.7...v0.1.8
v0.1.7
v0.1.7 — correctness: framework nesting, clinit returns, field casts
- External framework nested references now dot correctly
(android.os.Parcelable.Creator instead of Parcelable$Creator). - Static initializers no longer contain illegal returns
("return outside method": 184 -> 2 on reqable). - Field reads on Object-widened receivers cast to the declaring class.
- Full-javac gate (reqable + android.jar): 20,895 semantic errors
(-50.5% vs 0.1.5); syntax gate zero across the corpus; deterministic. - jdc-core 0.2.2.
Full Changelog: v0.1.6...v0.1.7
v0.1.6
v0.1.6 — output quality: eight fix rounds, semantic errors halved
- Real local-variable names from debug_info; true
enumdeclarations. - Name-collision classes eliminated (nested chains, sanitizer
collapses, covariant overrides, package-leaf shadowing — alone
-12.5k "cannot find symbol" on reqable). - null/long/cast/super correctness fixes; directional type evidence.
- Full-javac gate (with android.jar) on reqable: 42,251 -> 21,193
semantic errors (-49.9%). Syntax gate stays at zero across the
1.18M-file corpus; output deterministic; performance unchanged. - jdc-core 0.2.1.
Full Changelog: v0.1.5...v0.1.6
v0.1.5
v0.1.5 — output quality: real local names + compilability fixes
- Real local-variable names from debug_info (type-gated, cross-block
unified): s() now readsString s(byte[] enc, int k)without/i. - Lab-package feedback fully root-caused: 0 javac errors (0.1.4: 39).
- javac full-compile gate fixes: nested-class name-chain renames
(lark already-defined 22316 -> 93), nested reference prefixes,
catch-parameter double declaration, super() ordering, duplicate
covariant methods (weibo 14871 -> 971). - Perf round: weixin 18.2 -> 10.6s, qq -25%, WhatsApp -38%;
deterministic output run-to-run. - jdc-core 0.2.0, ddc-dex 0.2.1.
Full Changelog: v0.1.4...v0.1.5
v0.1.4
v0.1.4
Output quality — four new body-cleanup passes, all on by default:
- jadx-style local names: a synthetic
v12/p3never survives when a
better name exists (Kotlin Intrinsics parameter strings, the defining
call, jadx's type-alias table with the class-name fallback). - Kotlin null-check elision: statement-position
Intrinsics.checkNotNull…calls drop after their strings feed the
naming pass (lark: 59,106 → 9). - Synthetic-accessor inlining:
access$NNNstatic bridges inline at
their call sites — identity, field getter, method forwarder, with d8
APM trace wrappers tolerated (lark: 43% of 9,214 call sites). - IntDef constant rendering, now built-in:
setVisibility(8)renders
asandroid.view.View.GONEwith no flags — the android-37 domain
table ships as a readable, diffable
platform_symbols.txtthat build.rs compresses into the binary;
--symbols <platform-dir>overrides.
Verified: lark (118,502 files) and weixin (239,599 files) pass the
javac parse gate with zero errors; timing within noise (reqable
0.29s, lark 6.4s, weixin 17.5s). 67 tests, clippy zero.
Full Changelog: v0.1.3...v0.1.4
v0.1.3
v0.1.3
- Fixed: ddc aborted before doing any work on archives whose central-
directory size crossed the EOCD entry-count field — the u16 field was
read as u32, soVec::with_capacityasked the allocator for ~200 GB
and the process died on the spot. Found, root-caused and fixed by
@dashanlkk in #1 (with a regression test that reproduces the original
202 GB allocation). - Fixed: quitting a pager (
ddc app.apk -o - | less) no longer panics
with "failed printing to stdout: Broken pipe" — SIGPIPE keeps its
default disposition.
What's Changed
- fix: read the EOCD entry count as u16 (ddc aborted on archives with a large cd_size) by @dashanlkk in #1
New Contributors
- @dashanlkk made their first contribution in #1
Full Changelog: v0.1.2...v0.1.3
v0.1.2
v0.1.2
Highlights since v0.1.1:
-
Full-corpus validation: 39 real-world APKs (6.4 GB, 5.4M classes)
decompile to 4,081,503 files that ALL pass javac's parse phase —
zero syntax errors. Seven obfuscated-name defects found and fixed
along the way (case-renamed class headers, the lone-underscore
reserved identifier, restricted type names like 'var', digit-start
names, R8 trailing-'$' class names, Kotlin bare yield() calls,
fake dex entries no longer abort a run). docs/validation.md. -
ddc info is now the one orientation command: an app-context header
(label resolved through resources.arsc, package, version, launcher,
sdk, size, md5) above the per-dex table. -
Performance: the per-invoke proto re-parse is gone (fixpoint phase
CPU 23.5s -> 11.3s on lark, total decompile CPU -35%); method
names/descriptors share per-dex tables as Arc (peak RSS
-81MB on lark). -
weixin full-decompile stack overflow fixed (forward_single_use
inlining a def-reference cycle). -
Parser: dex-LIKE entries (encrypted payloads) are skipped with a
notice instead of failing the whole run.
jdc-core 0.1.10; ddc-dex 0.2.0 (breaking: proto_params -> &[u32]).
Full Changelog: v0.1.1...v0.1.2
v0.1.1
ddc 0.1.1 — javac-verified output
Full decompile: weibo (226MB, 98k classes) 5.98s, qq (374MB) 18s,
whatsapp 139MB 9s with case-variant class pairs all preserved
(99,276 files). 20+ progressive subcommands answering in <=0.9s.
Every output file of seven real APKs (1,085,000 files) passes javac
with zero syntax errors. Bilingual CLI and docs. Built on
jdc-core 0.1.9.
Also in this tag: zero compiler/clippy warnings, quality round
(alloc-free fast paths, single-allocation descriptors).
Full Changelog: v0.1.0...v0.1.1
v0.1.0
ddc 0.1.0 — first public release DEX → Java decompiler in Rust. Full decompile of a 226MB/20-dex APK in ~6s; 20+ progressive-analysis subcommands answering in milliseconds; DEX 035-041, XAPK/APKS/APKM containers, invoke-custom; bilingual CLI; reproducible output. Built on jdc-core 0.1.6.