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_ref declarations;
- 1,102
java_class_ref declarations; 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_ref is internal, but custom binding projects can add partial source to the same generated type and may reference it.
- Hand-written
Mono.Android partials must be migrated before framework generation can omit it.
- Some generic or inherited partial types may currently rely on a base type's
class_ref rather than having direct access to the correct _members field.
- 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_ref uses to _members.JniPeerType.PeerReference.Handle or 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.dll for API 37 using XAJavaInterop1.
- Count
class_ref and java_class_ref declarations and references in obj/.../mcw/*.cs.
- Observe that generated sources declare thousands of getters but do not otherwise reference the names.
- Inventory non-generated
src/Mono.Android/**/*.cs references.
- Replace those references with the owning
_members.JniPeerType.PeerReference.Handle and 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_ref getters.
- All in-tree hand-written code uses direct
JniPeerMembers class-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.dll MethodDef, 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
Android framework version
net11.0-android (Preview) / .NET 12 investigation
Affected platform version
dotnet/androidmain; generatedMono.Android.dlland generated Java binding assemblies.Description
The XAJavaInterop1 generator emits a compatibility class-handle getter on generated bound classes:
It also emits the adjacent interface/static-member form:
API 37 generated source currently contains approximately:
class_refdeclarations;java_class_refdeclarations; andModern generated method, field, constructor, and registration code accesses
JniPeerMembersdirectly. 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 96class_refreferences, primarily in older partial implementations for constructors, fields, and explicit nonvirtual calls. Representative examples use it for:These can likely use the owning type's generated
_membersfield directly:or a small shared helper where direct
_membersaccess 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.Mono.Androidpartials must be migrated before framework generation can omit it.class_refrather than having direct access to the correct_membersfield.java_class_ref.A possible staged approach:
class_refuses to_members.JniPeerType.PeerReference.Handleor an equivalent owner-specific helper.class_ref/java_class_ref.class_ref.Related investigations: #12670 and the
ThresholdType/ThresholdClassremoval investigation.Steps to Reproduce
Mono.Android.dllfor API 37 using XAJavaInterop1.class_refandjava_class_refdeclarations and references inobj/.../mcw/*.cs.src/Mono.Android/**/*.csreferences._members.JniPeerType.PeerReference.Handleand regenerate without compatibility getters.The investigation and eventual tests should cover:
java_class_ref;class_ref;Acceptance criteria for an eventual implementation:
class_ref/java_class_refgetters.JniPeerMembersclass-handle access.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