Skip to content

Commit 5b7f32b

Browse files
URGENT ADFA-3604: Fix R8 shrink/optimize crashes live on stage (#1609)
ADFA-3604: Fix R8 shrink/optimize bugs found in on-device testing On-device verification of the shrunk release build (requested in review) surfaced five distinct R8 bugs, each fixed here: 1. R8's optimize pass retargeted a call to CoGo's own isTestMode() to the unrelated, device-missing dalvik.system.VMRuntime.isTestMode, crashing every release build on launch. Disabled optimization (-dontoptimize); shrinking is what actually cuts dex size, optimization was the unsafe part given the R8/Kotlin 2.3.0 metadata mismatch already on record in this file (see the existing StopWatch workaround). 2. Protobuf-lite resolves generated message fields by name via reflection; shrinking removed a "field with no direct bytecode reference" (SyncMetaModels$SyncMeta.projectModelInfo_), crashing project sync. 3-5. lsp/kotlin's own service registrations (kt-lsp.xml and AnalysisApiServiceProviders.kt's ::class-literal registrar, which PicoContainer instantiates reflectively) and Caffeine's runtime cache implementation selection all hit the same class of bug: a reflective lookup R8 can't trace from static analysis. Each fix revealed another instance elsewhere in the same dependency graph, so rather than keep discovering them one on-device crash at a time, keep the whole lsp/kotlin runtime dependency graph (org.jetbrains.kotlin, Caffeine, kotlin-reflect, kotlin-script, coroutines internals, streamex, Trove) whole. Verified end to end on a physical device: clean Kotlin analysis session init, and a live diagnostic ("Expecting member declaration") confirming the analysis engine correctly parses and resolves code post-shrink. Net result: release dex settles at ~85 MB (down from the 119.4 MB baseline), short of the initially-measured 28.8 MB because that number came from a build that silently corrupted the Kotlin LSP -- this is the verified-correct number. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent bd022d8 commit 5b7f32b

1 file changed

Lines changed: 54 additions & 0 deletions

File tree

app/proguard-rules.pro

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
-dontnote **
55
-dontobfuscate
66

7+
# ADFA-3604: R8's optimize pass (inlining/method merging) can silently
8+
# retarget a call to the wrong method when it fails to parse Kotlin 2.3.0
9+
# metadata (see "R8: An error occurred when parsing kotlin metadata" in the
10+
# build log) -- observed corrupting calls to utils.TestModeUtilsKt.isTestMode
11+
# into calls to the unrelated, device-missing dalvik.system.VMRuntime.isTestMode,
12+
# crashing every release build on launch. Shrinking (dead-code removal) is
13+
# what actually cuts dex size; the optimize passes are the risky part given
14+
# this R8/Kotlin version mismatch, so disable only optimization.
15+
-dontoptimize
16+
717
-keep class javax.** { *; }
818
-keep class jdkx.** { *; }
919

@@ -19,6 +29,42 @@
1929
# Builder model implementations
2030
-keep class com.itsaky.androidide.builder.model.** { *; }
2131

32+
# lsp/kotlin registers its own IntelliJ project/application services -- some
33+
# by class name in lsp/kotlin/src/main/resources/META-INF/kt-lsp/kt-lsp.xml,
34+
# the rest via ::class literals in
35+
# lsp/kotlin/.../registrar/AnalysisApiServiceProviders.kt, which PicoContainer
36+
# then instantiates reflectively via each class's no-arg constructor. A
37+
# ::class literal doesn't count as an actual `new` call to R8, so it kept
38+
# stripping "unused" no-arg constructors one class at a time as each was
39+
# discovered on-device (ClassNotFoundException on DirectInheritorsProvider,
40+
# then a PicoInitializationException on ModuleDependentsProvider's missing
41+
# constructor). Keep the whole package rather than list every implementation
42+
# class in AnalysisApiServiceProviders.kt individually.
43+
-keep class com.itsaky.androidide.lsp.kotlin.compiler.services.** { *; }
44+
45+
# Kotlin Analysis API (bundled in subprojects/kotlin-analysis-api, used by the
46+
# Kotlin LSP). subprojects/kotlin-analysis-api/consumer-rules.pro keeps every
47+
# class this jar's own IntelliJ plugin XML descriptors and ServiceLoader
48+
# entries reference by name, but on-device testing kept surfacing distinct
49+
# reflection paths that narrow list didn't cover -- not just inside this jar,
50+
# but in other lsp/kotlin runtime dependencies too (Caffeine picks a cache
51+
# implementation from dozens of codegenned variant classes at runtime; a
52+
# protobuf-lite message field is resolved by name string; lsp/kotlin's own
53+
# kt-lsp.xml above; and a NullPointerException deep in IntelliJ's own
54+
# JavaCoreApplicationEnvironment bootstrap, verified absent on an unshrunk
55+
# debug build). Each fix was quick but the next gap kept appearing elsewhere
56+
# in the same dependency graph, so rather than keep discovering them one
57+
# on-device crash at a time, keep every runtime dependency lsp/kotlin pulls in
58+
# whole. This gives up the dex-size reduction for this whole dependency graph;
59+
# see ADFA-3604 for the size trade-off.
60+
-keep class org.jetbrains.kotlin.** { *; }
61+
-keep class com.github.benmanes.caffeine.** { *; }
62+
-keep class kotlin.reflect.** { *; }
63+
-keep class kotlin.script.** { *; }
64+
-keep class kotlinx.coroutines.internal.** { *; }
65+
-keep class one.util.streamex.** { *; }
66+
-keep class gnu.trove.** { *; }
67+
2268
# Eclipse
2369
-keep class org.eclipse.** { *; }
2470

@@ -88,6 +134,14 @@
88134

89135
-keep class com.itsaky.androidide.treesitter.** { *; }
90136

137+
# Protobuf lite: the generated runtime resolves message fields by name via
138+
# reflection (the info string baked into newMessageInfo()), so shrinking a
139+
# "field with no direct bytecode reference" breaks it at runtime with a
140+
# NoSuchFieldException (observed on SyncMetaModels$SyncMeta.projectModelInfo_).
141+
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
142+
<fields>;
143+
}
144+
91145
# Retrofit 2
92146
-dontwarn retrofit2.**
93147
-keep class retrofit2.** { *; }

0 commit comments

Comments
 (0)