v0.1.6
A verification release: the claims now rest on real applications with known source, not only on artifacts we built ourselves.
New gate: tests/app_truth.rs
Every existing truth gate ran on our own inputs — source_truth compiles a 160-line fixture, ground_truth diffs against .symtab on hello-world samples. This one decompiles real applications whose source is known and judges the output against that source, which is the only check that can catch a recovery chain rotting at scale.
Corpus: locally built flutter-samples apps (DAE_DEMO_ROOT, defaulting to a sibling checkout) — material_3_demo at 5,107 source lines and animations at 2,108. Measured, and asserted with a 0.90 floor:
| check | material_3_demo | animations |
|---|---|---|
public classes/mixins/enums in lib/ recovered |
85/86 = 98.8% | 35/35 = 100% |
| source string literals present in the output | 292/306 = 95.4% | 111/114 = 97.4% |
| source files mapping to a recovered library | 18/18 = 100% | 21/23 = 91% |
The single missed type is enum Value { first, second } — its library is in the output, so the enum was tree-shaken rather than misparsed. The two unmapped animations files are examples nothing references.
The fast half (plain export + source comparison) takes ~2 s and runs in the normal suite; the --decompile + dart analyze half is #[ignore]d at 247 s for both. Negative-tested by raising the class floor to 1.01, which fails and names Value.
Also measured on shipping apps
- Reqable.app (macOS arm64, 26 MB, Dart 3.3.4, a
verifiedprofile): 70,996 table entries, 0 warnings, 1,808 functions at 94.9% structured,dart analyze0 errors. material_3_demo: 15,082 functions, 985,900 statements, 92.5% structured, 3 unmapped lines, 0 errors, 94.7% of direct calls resolved to a name.animations: 11,102 functions, 696,396 statements, 92.5% structured, 3 unmapped lines, 0 errors.
Added to the five Android builds already matching aotopsy exactly (57,960 / 79,327 / 30,782 / 19,752 / 22,623, all at 0 warnings).
What is out of scope, stated plainly
Of the 41 APKs in the local corpus exactly 8 ship a lib/arm64-v8a/libapp.so. Three of those eight are not standard Flutter AOT snapshots, and dae says so rather than guessing:
- WeChat — its
libapp.sois a 21-byteCSOSplaceholder inside the APK itself (confirmed withunzip -l, not an extraction mistake). The real payload lives elsewhere. - DingTalk — features string carries
enable_aion+llvm_compiler: a vendor fork that replaced the Dart AOT compiler with an LLVM backend. Its snapshot version hash matches no known SDK, so detection falls back to a low-confidence structural probe. - Tonghuashun — genuine Dart 2.7.2, and it behaves identically to the reference
hello_2.7.2sample: strings and the object layer export, but the instruction table is recoverable neither from the snapshot header nor from Code-cluster text offsets, so there are no function addresses. That is the documented ≤2.9 ceiling, not an artifact-specific failure.
Two allocation reductions — and an honest null result
Both verified byte-identical over 985,900 statements:
replace_wordcopied its input one byte at a time viaout.push(b[i] as char)— a char conversion plus a UTF-8 encode per byte. It now moves whole slices withpush_str, andsubst_regsskips the call entirely when the operand text cannot contain the register.- Three output buffers grew from zero (per-function body, per-function disassembly comment, per-library file); all three are now sized from the statement / instruction / function counts.
Neither produced a measurable speedup, and we are not claiming one. Profiling a debug-symbol build attributed the time to format_inner, Formatter::pad / pad_integral, RawVecInner::finish_grow and _platform_memmove — i.e. the ~1M format!("... // {addr:#x}") calls, not the register substitution first suspected. Interleaved A/B on animations gave 67.7 / 61.5 s before and 63.1 / 67.7 s after: the same binary varies by more than the effect on a host carrying unrelated load. The changes stay because they are strictly less work and provably output-identical. The real fix — replacing those format! calls with write! into reused buffers across render_op — is a larger refactor, deliberately not attempted in a release commit.
Docs
README and DECOMPILER carry the real-app tables above and the out-of-scope classification. The gates list now names app_truth first, since it is the strongest one.
Verified
48 tests under DAE_REQUIRE_GATES=1 · full scorecard 26 artifacts / 0 errors · regress_all 25/25 byte-identical · check_profiles 47/47 · clippy 0 · Lark / Weibo / Reqable.app still at 79,327 / 22,623 / 70,996 table entries.
Install
cargo install dae-rs # crate name is dae-rs; the binary is `dae`
brew install ejfkdev/tap/dae
scoop bucket add ejfkdev https://github.com/ejfkdev/scoop-bucket; scoop install daeOr grab a binary for Linux / macOS / Windows (x64 and arm64) below.