dotnet / dotnet/android

Investigate eliminating generated class_ref compatibility getters

Open
#12,680 0 comments 0 reactions 0 assignees View on GitHub
Area: Mono.Android
Dominant language
C#
Stars
2.1k
Forks
579
Avg merge
1d 20h
Merged PRs (30d)
257

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:

```csharp
internal static new IntPtr class_ref {
get { return _members.JniPeerType.PeerReference.Handle; }
}
```

It also emits the adjacent interface/static-member form:

```csharp
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:

```csharp
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:

```csharp
_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:

1. Inventory and migrate all in-tree hand-written `class_ref` uses to `_members.JniPeerType.PeerReference.Handle` or an equivalent owner-specific helper.
2. Verify that generated output has no remaining consumers of `class_ref`/`java_class_ref`.
3. Stop emitting the properties only for a versioned new generator format.
4. Retain legacy emission for old/default generator modes during transition.
5. 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

1. Generate `Mono.Android.dll` for API 37 using XAJavaInterop1.
2. Count `class_ref` and `java_class_ref` declarations and references in `obj/.../mcw/*.cs`.
3. Observe that generated sources declare thousands of getters but do not otherwise reference the names.
4. Inventory non-generated `src/Mono.Android/**/*.cs` references.
5. Replace those references with the owning `_members.JniPeerType.PeerReference.Handle` and regenerate without compatibility getters.
6. 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

```shell
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
```

Contributor guide

No contributing guide indexed for this repository

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.

Assessment

Tech stack
csharp
Domain
build-system, tooling
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.