dotnet / dotnet/android

[TrimmableTypeMap] R8 shrinking removes unreferenced AndroidJavaSource (user Java) classes under NativeAOT

Open
#11,774 1 comment 0 reactions 0 assignees View on GitHub
needs-triage
Dominant language
C#
Stars
2.1k
Forks
579
Avg merge
1d 20h
Merged PRs (30d)
257

Description

### Summary

Under the trimmable typemap (NativeAOT), **R8 shrinking removes user-authored `AndroidJavaSource` Java classes** that aren't referenced from the managed dependency graph. This breaks multidex behavior and, more importantly, can break real apps that call user Java via JNI/reflection (which ILC's managed graph can't see).

Reproduced via `BuildAfterMultiDexIsNotRequired(NativeAOT)`:

```
Expected: file exists
But was: ".../obj/Release/android/bin/classes2.dex"
```

### Diagnosis

`CreateMultiDexRequiredApplication` adds two `AndroidJavaSource` files (`ManyMethods.java`, `ManyMethods2.java`), each with 32768 methods (`Bind=False`, unreferenced) → 65536 methods > the 64K dex limit → must produce `classes2.dex`.

Local repro (NativeAOT, arm64):
- The Java is compiled: `obj/Release/.../android/bin/classes/ManyMethods.class` is 1.7 MB each.
- The classes **zip** that D8/R8 consumes (`classes.zip`, 3.9 MB) **contains** `ManyMethods.class` / `ManyMethods2.class`.
- But the produced `classes.dex` is only ~275 KB and contains **none** of those methods, and no `classes2.dex` is produced.

Because `AndroidEnableMultiDex=True` selects **R8** (`Xamarin.Android.D8.targets`, `_UseR8`), and `_R8EnableShrinking=True` for `AndroidLinkTool=r8`, R8 shrinks the unreferenced `ManyMethods` classes away. The trimmable NativeAOT proguard config (`GenerateNativeAotProguardConfiguration`, built from the ILC DGML + acw-map) keeps ACW/JCW types and `android.R`/`android.Manifest`, but **not** user `AndroidJavaSource` classes — so R8 drops them.

The legacy/CoreCLR path keeps them (the `CoreCLR` variant of the test passes).

### Why this matters beyond the test

`AndroidJavaSource` (`Bind=False`) classes are user-authored Java, often invoked from native/JNI or reflection. R8 shrinking them based on the *managed* reachability graph is unsound and can silently remove Java the app needs at runtime.

### Fix direction (needs design)
- Keep user `AndroidJavaSource`-derived classes in the trimmable NativeAOT proguard config (emit `-keep` for them), **or**
- Don't apply R8 *shrinking* to the user Java side under NativeAOT (the managed side is already trimmed by ILC; the Java side's reachability isn't captured by the managed graph), **or**
- Treat the app's own compiled Java classes as kept program classes.

### Acceptance criteria
- [ ] `BuildAfterMultiDexIsNotRequired(NativeAOT)` passes (`classes2.dex` produced when required, absent when not).
- [ ] Unreferenced user `AndroidJavaSource` classes survive into the dex on NativeAOT.
- [ ] No unnecessary dex bloat for normal apps.

### References
- Repro test: `BuildAfterMultiDexIsNotRequired` (`src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs`), `CreateMultiDexRequiredApplication`.
- Code: `src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets` (`_UseR8`, `_R8EnableShrinking`), `src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targets` (`GenerateNativeAotProguardConfiguration`).

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with BuildAfterMultiDexIsNotRequired and CreateMultiDexRequiredApplication in src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs. Read Xamarin.Android.D8.targets alongside Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targets, especially GenerateNativeAotProguardConfiguration, then run the NativeAOT test. Done means user AndroidJavaSource classes survive R8, the expected classes2.dex is produced, and the non-multidex case still passes without unnecessary bloat.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, csharp, java
Domain
build-system, mobile
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.