Investigate eliminating generated class_ref compatibility getters
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Refactor
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- csharp
- Domain
- build-system, tooling
Research direction
Start with the XAJavaInterop1 output in obj/.../mcw/.cs and the approximately 96 non-generated references under src/Mono.Android/**/.cs; generate API 37 bindings and inventory declarations and uses. Trace ownership for generated classes, interface/static-member forms, generic and inherited partials, then validate constructors, fields, nonvirtual calls, legacy assemblies, and listed runtimes. Done means the compatibility and migration behavior is defined, with before/after metadata and size measurements recorded.
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 emits a compatibility class-handle getter on generated bound classes:
internal static new IntPtr class_ref {
get { return _members.JniPeerType.PeerReference.Handle; }
}
It also emits the adjacent interface/static-member form:
static IntPtr java_class_ref {
get { return _members.JniPeerType.PeerReference.Handle; }
}
API 37 generated source currently contains approximately:
- 5,676
class_refdeclarations; - 1,102
java_class_refdeclarations; and - no generated-source references to either name beyond their declarations.
Modern generated method, field, constructor, and registration code accesses JniPeerMembers directly. The generated getters therefore appear to be legacy compatibility shims rather than active generator infrastructure.
Hand-written source under src/Mono.Android/ still contains approximately 96 class_ref references, primarily in older partial implementations for constructors, fields, and explicit nonvirtual calls. Representative examples use it for:
JNIEnv.GetMethodID (class_ref, ...)
JNIEnv.CallNonvirtualVoidMethod (Handle, class_ref, ...)
JNIEnv.StartCreateInstance (class_ref, ...)
These can likely use the owning type's generated _members field directly:
_members.JniPeerType.PeerReference.Handle
or a small shared helper where direct _members access is not available.
The generated getter itself costs a property row, getter MethodDef, method-semantics row, and method body for every emitted type. Across roughly 6,778 declarations, the gross untrimmed framework cost is expected to be around 0.45–0.50 MB, before any secondary cleanup.
This issue should investigate removing these getters from a new/versioned generator format while preserving compatibility for legacy binding source and assemblies.
Compatibility questions:
class_refis internal, but custom binding projects can add partial source to the same generated type and may reference it.- Hand-written
Mono.Androidpartials must be migrated before framework generation can omit it. - Some generic or inherited partial types may currently rely on a base type's
class_refrather than having direct access to the correct_membersfield. - Static interface fields and alternative/invoker classes may have different ownership requirements for
java_class_ref. - Old generated binding assemblies already contain the getter and should remain binary-compatible without runtime changes.
- Regenerating an existing binding project with the new format could break custom partial source even though the member is internal; the format switch or diagnostic needs to make that intentional.
A possible staged approach:
- Inventory and migrate all in-tree hand-written
class_refuses to_members.JniPeerType.PeerReference.Handleor an equivalent owner-specific helper. - Verify that generated output has no remaining consumers of
class_ref/java_class_ref. - Stop emitting the properties only for a versioned new generator format.
- Retain legacy emission for old/default generator modes during transition.
- Consider a source diagnostic or documented replacement for custom binding partials that reference
class_ref.
Related investigations: #12670 and the ThresholdType/ThresholdClass removal investigation.
Steps to Reproduce
- Generate
Mono.Android.dllfor API 37 using XAJavaInterop1. - Count
class_refandjava_class_refdeclarations and references inobj/.../mcw/*.cs. - Observe that generated sources declare thousands of getters but do not otherwise reference the names.
- Inventory non-generated
src/Mono.Android/**/*.csreferences. - Replace those references with the owning
_members.JniPeerType.PeerReference.Handleand regenerate without compatibility getters. - Build and exercise constructors, static fields/methods, explicit nonvirtual calls, interface invokers, and generic hand-written partials.
The investigation and eventual tests should cover:
- generated classes and nested classes;
- abstract classes and interface invokers;
- static Java fields and methods;
- constructor activation and explicit nonvirtual calls;
- hand-written framework partials;
- generic types and inherited static-member access;
- interface alternative/static-member classes using
java_class_ref; - a legacy binding project with custom partial source referencing
class_ref; - old precompiled binding assemblies; and
- MonoVM, CoreCLR, NativeAOT, and trimmable-typemap configurations where applicable.
Acceptance criteria for an eventual implementation:
- New-format generated bindings do not emit unused
class_ref/java_class_refgetters. - All in-tree hand-written code uses direct
JniPeerMembersclass-handle access. - JNI class ownership and nonvirtual dispatch remain correct.
- Legacy generator output remains available or migration diagnostics/documentation are provided for custom partial source.
- Old precompiled binding assemblies continue to run unchanged.
- Before/after
Mono.Android.dllMethodDef, Property, MethodSemantics, IL, metadata, and trimmed-app sizes are recorded.
Did you find any workaround?
No correctness workaround is needed. Current generated getters remain functional, but hand-written code can generally access the same handle through _members.JniPeerType.PeerReference.Handle.
Relevant log output
API 37 generated source:
class_ref declarations: 5,676
class_ref generated uses: 5,676 (declarations only)
java_class_ref declarations: 1,102
java_class_ref generated uses: 1,102 (declarations only)
Non-generated src/Mono.Android source:
class_ref references: approximately 96
- Dominant language
- C#
- Stars
- 2.1k
- Forks
- 580
- Avg merge
- 2d 19m
- Merged PRs (30d)
- 228
Contributor guide
No contributing guide indexed for this repository
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
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Area: Debugger enhancement needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
-
Area: Mono.Android
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
agentic-workflows needs-triage
-
automated needs-triage skill-runner
Difficulty 5/5 Over a week Newbie friendliness 10/100
Similar issues
-
[Feat] 조합 영역 구분선 개선 Open
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
type/automation type/tech-debt
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
t/bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
ci-failure-cause test-failure
Difficulty 2/5 1-3 hours Newbie friendliness 82/100