Skip to content

[Android] java.lang.VerifyError: type=Long (High Half) in R8 Release Build (Compose TextRange Bug) #597

Description

@bpappin

Bug Description

During R8 optimization, MMR trips over a bug that's actually in the recent compose, causing this exception:

Fatal Exception: java.lang.VerifyError: Verifier rejected class iv6: void iv6.l(in4, w57, boolean, long, in4, in4, in4, in4, ih6, tm4, iq4, int, int, int) failed to verify: void iv6.l(in4, w57, boolean, long, in4, in4, in4, in4, ih6, tm4, iq4, int, int, int): [0x267] copy2 v10<-v3 type=Long (High Half)/Long (High Half) (declaration of 'iv6' appears in /data/app/~~FDucHwvEm8gMuMxX-pNA2Q==/app.evolyn-ZMxrX0cz6TfUiPrIc4gV7g==/base.apk)
       at com.mikepenz.markdown.compose.components.MarkdownComponentsKt.j(MarkdownComponents.kt:1)
       at androidx.compose.ui.text.TextRangeKt.TextRange(TextRange.kt:97)
       at androidx.compose.ui.text.TextRange.<clinit>(TextRange.kt:92)
       at androidx.compose.ui.text.TextRange.access$getZero$cp(TextRange.kt:48)
       at androidx.compose.ui.text.TextRange$Companion.getZero-d9O1mEE(TextRange.java:92)
       at androidx.compose.ui.text.input.TextInputServiceAndroid.<init>(TextInputServiceAndroid.android.kt:90)
       at androidx.compose.ui.text.input.TextInputServiceAndroid.<init>(TextInputServiceAndroid.android.kt:58)
       at androidx.compose.ui.text.input.TextInputServiceAndroid.<init>(TextInputServiceAndroid.android.kt:122)
       at androidx.compose.ui.platform.AndroidComposeView.getLegacyTextInputServiceAndroid(AndroidComposeView.android.kt:828)
       at androidx.compose.ui.platform.AndroidComposeView.getTextInputService(AndroidComposeView.android.kt:840)
       ...

Steps to Reproduce

This is a pretty typical release optimization bug.

  1. Add MMR to a compose screen
  2. Build a full R8 optimized release
  3. when executed, an exception causes the app to crash.

Expected Behavior

THe app should alunch and markdown be rendered.

Screenshots

If applicable, add screenshots to help explain your problem.

Environment

  • Library Version: 0.43.0 (for multiplatform-markdown-renderer)
  • Platform: Android (specifically in Release builds with R8 minification enabled)
  • Device: Pixel 10 Pro XL
  • OS Version: Android 16
  • Kotlin Version: 2.4.0
  • Compose Version: 1.11.1 (Compose Multiplatform)
  • AGP Version: 9.2.1
  • Error Trigger: VerifyError: Verifier rejected class... [0x267] copy-cat2 v10<-v3 type=Long (High Half)/Long (High Half)

Additional Context

The Core Bug is actually in Google's R8 / Compose: The bug is fundamentally an R8 compiler failure when trying to aggressively optimize Compose's TextRange and Color (which are part of androidx.compose). The MMR library isn't doing anything wrong; it just happens to be calling TextRange in a specific, dense way that causes R8 to trip over itself during inlining.

Ideally, the Google R8 team fixes the compiler (which they do frequently in new AGP updates). Secondarily, the AndroidX Compose team could bundle those -keep rules inside the official Compose library's consumer-rules.pro.

However MMR could add a consumer-rules.pro to the library that says -keep class com.mikepenz.markdown.compose.** { *; } as a temporary workaround to stop R8 from inlining the specific code and triggering the bug.

Since this is a known, bleeding-edge issue with Kotlin 2.x and Compose Multiplatform 1.7+, library authors often push these temporary consumer rules until the underlying Android toolchain is patched.

The MMR Library fix

This is really a bug that should be fixed by others, but aside from waiting an unknown amount of time, we can fix this at the MMR level, and remove it when its no longer an issue. Add a consumer-rules.pro to the library with this content:

# consumer-rules.pro

# Workaround for R8 bytecode verification bug (copy-cat2 type=Long)
# occurring with Jetpack Compose inline Long classes (like TextRange).
-keepclassmembers class com.mikepenz.markdown.compose.** { *; }

# Alternatively, just keeping the signatures is often enough to stop R8 
# from mangling the Dalvik registers during aggressive inlining:
-keepattributes Signature, InnerClasses, EnclosingMethod

in the Android source set build.gradle.kts file:

android {
    defaultConfig {
        // This tells the Android Gradle Plugin to package these rules into the AAR
        // so that any app consuming this library automatically applies them.
        consumerProguardFiles("consumer-rules.pro")
    }
}

Anyone who imports the library should inherit those rules and prevent the crash.

Workaround for library consumers

The workaround I am using, applied locally (in my apps/androidApp module) essentially mimics what those consumer rules would do, but applied at the application level.

I enabled custom ProGuard rules for the release build In my apps/androidApp/build.gradle.kts, I explicitly instructed the release build to use a custom rules file:

proguardFiles(
    getDefaultProguardFile("proguard-android-optimize.txt"),
    "proguard-rules.pro"
)

I wrote rules targeting the specific failing classes In apps/androidApp/proguard-rules.pro, so that the R8 compiler doesn't optimize the specific Compose inline classes (TextRange and Color) and the MMR library that triggers the bug.

# Workaround for R8 bytecode verification bug with Jetpack Compose inline Long classes
-keepclassmembers class androidx.compose.ui.text.TextRange { *; }
-keepclassmembers class androidx.compose.ui.graphics.Color { *; }

# Protect the specific library call site that triggers the R8 register corruption
-keepclassmembers class com.mikepenz.markdown.compose.** { *; }
-keep class com.mikepenz.markdown.compose.** { *; }

# Preserve signatures to prevent invalid Dalvik bytecode generation during inlining
-keepattributes Signature, InnerClasses, EnclosingMethod

Doing this at the app level, shields the vulnerable code from R8's aggressive release minification.

Checklist

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions