Investigate eliminating generated ThresholdType and ThresholdClass overrides
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Start with generated files under obj/.../mcw/*.cs and trace Android.Runtime.XAPeerMembers.UsesVirtualDispatch(), GetPeerMembers(), and Java.Lang.Object/Throwable threshold accessors. Compare exact, subclass, invoker, cross-assembly, and legacy binding cases across the listed runtimes. Done means establishing whether modern overrides can be omitted without changing dispatch, preserving legacy semantics, and recording the required metadata and size comparisons.
Written by the indexing model from the issue text.
Description
Android framework version
net11.0-android (Preview) / .NET 12 investigation
Affected platform version
dotnet/android main; generated Mono.Android.dll and generated Java binding assemblies.
Description
The XAJavaInterop1 generator currently emits these overrides on nearly every generated bound class and invoker:
[DebuggerBrowsable (DebuggerBrowsableState.Never)]
[EditorBrowsable (EditorBrowsableState.Never)]
protected override IntPtr ThresholdClass
=> _members.JniPeerType.PeerReference.Handle;
[DebuggerBrowsable (DebuggerBrowsableState.Never)]
[EditorBrowsable (EditorBrowsableState.Never)]
protected override Type ThresholdType
=> _members.ManagedPeerType;
API 37 Mono.Android.dll contains approximately:
- 6,776 generated
ThresholdClassoverrides; - 7,447 generated
ThresholdTypeoverrides; - 28,446 associated
DebuggerBrowsable/EditorBrowsableattribute applications; - roughly 14,000 property rows, getter methods, method-semantics rows, bodies, and related metadata entries.
This appears to be an opportunity of more than 1 MB in the untrimmed framework assembly, but removal must not be attempted without understanding the legacy dispatch contract.
XAPeerMembers currently uses the properties to decide virtual versus nonvirtual managed-to-Java dispatch:
protected override bool UsesVirtualDispatch (IJavaPeerable value, Type? declaringType)
{
var peerType = GetThresholdType (value);
if (peerType != null)
return peerType == value.GetType ();
return base.UsesVirtualDispatch (value, declaringType);
}
It also uses ThresholdClass when selecting peer members for nonvirtual dispatch. This historically prevents incorrect recursion or dispatch when managed subclasses override Java virtual methods.
At first glance, modern generated types already expose equivalent information through:
value.JniPeerMembers.ManagedPeerType
value.JniPeerMembers.JniPeerType
The base JniPeerMembers.UsesVirtualDispatch() implementation already compares value.GetType() with value.JniPeerMembers.ManagedPeerType. We should investigate whether new-format binding assemblies can rely on this information and stop generating the per-type threshold overrides.
This must be considered separately from legacy binding compatibility. Old binding assemblies may:
- contain generated threshold overrides;
- contain hand-written threshold overrides;
- depend on
XAPeerMembershonoring those values; - derive from framework binding types across assembly boundaries; or
- use older generator/runtime dispatch assumptions.
A possible compatibility model is:
- Keep the protected virtual
ThresholdTypeandThresholdClassmembers onJava.Lang.Object/Throwableso old binaries remain loadable. - Stop emitting overrides in newly generated/version-marked bindings.
- Use
JniPeerMembers.ManagedPeerTypeandJniPeerTypefor modern bindings. - Detect and continue honoring threshold overrides from legacy binding assemblies, potentially keyed by an assembly/generator-format marker or a cached override-shape check.
The investigation should determine whether this split is correct or whether there are dispatch cases where the explicit threshold values carry information not available through JniPeerMembers.
Related size work: #12670.
Steps to Reproduce
- Generate
Mono.Android.dllfor API 37 with XAJavaInterop1. - Count generated
ThresholdTypeandThresholdClassoverrides inobj/.../mcw/*.csor the resulting assembly. - Trace
XAPeerMembers.UsesVirtualDispatch()andGetPeerMembers()through exact binding types, managed subclasses, Java subclasses, and invoker types. - Prototype omitting the generated overrides for a version-marked modern binding format while preserving the base virtual properties.
- Compare behavior against legacy binding assemblies built by older generators.
The prototype should cover at least:
- an exact generated binding type;
- a managed subclass overriding a Java virtual method;
- a Java-derived runtime type;
- abstract classes and interface invokers;
- cross-assembly inheritance;
- an old binding assembly containing generated threshold overrides;
- a legacy binding with a hand-written/custom threshold override;
- virtual and explicit nonvirtual calls;
- MonoVM, CoreCLR, and NativeAOT/trimmable-typemap configurations where applicable; and
- recursion prevention and correct Java method selection.
Acceptance criteria for an eventual implementation:
- New-format generated bindings omit
ThresholdType/ThresholdClassoverrides when they carry no additional information. - Managed-subclass and Java virtual-dispatch behavior remains unchanged.
- Legacy binding assemblies continue to load and preserve their threshold semantics.
- The protected base API remains binary compatible unless a separate breaking-change decision is made.
- Tests prove that no managed override recursion or wrong JNI nonvirtual class selection is introduced.
- Before/after
Mono.Android.dllmetadata, method, property, attribute, and final trimmed-app sizes are recorded.
Did you find any workaround?
No workaround is needed for correctness. The current generated properties preserve established behavior but impose repeated assembly-size and metadata costs.
Relevant log output
API 37 generated counts:
ThresholdClass overrides: 6,776
ThresholdType overrides: 7,447
Current runtime consumers:
- Android.Runtime.XAPeerMembers.UsesVirtualDispatch()
- Android.Runtime.XAPeerMembers.GetPeerMembers()
- Java.Lang.Object.GetThresholdType()/GetThresholdClass()
- Java.Lang.Throwable.GetThresholdType()/GetThresholdClass()
- Dominant language
- C#
- Stars
- 2.1k
- Forks
- 581
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 212
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/android
-
Area: App+Library Build
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Area: Debugger enhancement needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
Area: Mono.Android
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
dotnet/android#9192 · 4 comments · 1 reaction ·
Maintainers usually reply within 1 day
-
needs-triage
Difficulty 5/5 Over a week Newbie friendliness 25/100
dotnet/android#12981 · 1 comment ·
Maintainers usually reply within 1 day
Similar issues
-
agentic-workflows untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
VS Code
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
AlamoEngine-Tools/pg-starwarsgame-lsp#207 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
elsa-workflows/elsa-core#8593 ·
Maintainers usually reply within 1 day
-
WebSocket upgrade check is case-sensitive for `Upgrade`Possibly taken @S0CloseYetS0Far claimed this today. Openbug good first issue severity:low
Difficulty 1/5 Under an hour Newbie friendliness 88/100
-
node: disconnection push notification is logged as "Unknown notification" instead of the disconnect warningPossibly taken @datatrigger claimed this today. Openbug 🐞 Untriaged user issue
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
valkey-io/valkey-glide#7278 ·
Maintainers usually reply within 2 days