[TrimmableTypeMap] Java.Interop bump and update to ValueManagers and TypeManagers#11799
Merged
Merged
Conversation
45c8058 to
0d7a2a0
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
3f0d176 to
0021abf
Compare
…+ JI bump) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the runtime interop layer to align with the newer Java.Interop value-manager shape by splitting the CoreCLR “JavaMarshal” value-manager behavior into a dedicated CoreClrJavaMarshalValueManager and extracting shared helpers, while removing superseded value-manager implementations.
Changes:
- Introduces
CoreClrJavaMarshalValueManager(reflection-backed) andJavaMarshalValueManagerHelperfor shared peer-type resolution and cast checking. - Removes the old
JavaMarshalValueManager,SimpleValueManager, andAndroidReflectionJniValueManagerimplementations and updates runtime wiring (JNIEnvInit) and project compilation items accordingly. - Adjusts trimming/DAM annotations and api-compat acceptable breakage expectations related to removed
DynamicallyAccessedMembersattributes.
Show a summary per file
| File | Description |
|---|---|
| tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt | Updates acceptable breakages for removed DAM attribute on ColorValueMarshaler.CreateGenericValue. |
| src/Mono.Android/Mono.Android.csproj | Removes old value manager compile items; adds new helper + CoreCLR value manager compile items. |
| src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs | Removes DAM annotations/suppressions and keeps typemap-based type lookups. |
| src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs | Refactors invoker lookup implementation; now directly uses Assembly.GetType/MakeGenericType. |
| src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManagerHelper.cs | Adds shared helper for peer-type normalization and assignability/cast checking. |
| src/Mono.Android/Microsoft.Android.Runtime/CoreClrJavaMarshalValueManager.cs | Adds new reflection-backed CoreCLR value manager implementation. |
| src/Mono.Android/Android.Runtime/JNIEnvInit.cs | Updates value-manager selection to return CoreClrJavaMarshalValueManager for CoreCLR/NativeAOT runtime feature switches. |
| src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs | Removes DAM annotation from CreateGenericValue parameter. |
| src/Mono.Android/Android.Runtime/AndroidRuntime.cs | Removes now-unneeded DAM constants/annotations and adjusts trimming suppressions. |
| src/Mono.Android/Android.Graphics/Color.cs | Removes DAM annotation from ColorValueMarshaler.CreateGenericValue and reformats signature. |
| src/Mono.Android/Android.App/IntentFilterAttribute.Partial.cs | Switches file to #nullable enable. |
| src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs | Removes obsolete value manager implementation. |
| src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs | Removes superseded value manager implementation. |
| src/Mono.Android/Microsoft.Android.Runtime/AndroidReflectionJniValueManager.cs | Removes superseded reflection value manager base implementation. |
Copilot's findings
- Files reviewed: 15/15 changed files
- Comments generated: 2
Fixes CS0246: 'JavaMarshalValueManager' could not be found. The default value manager now resolves to the internal CoreClrJavaMarshalValueManager (accessible via InternalsVisibleTo), matching the pattern in JNIEnvInit. Suppress the correct trimming warnings (IL2026/IL3050) instead of IL3000. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…alueManager CreatePeerInstance only walked the Java superclass chain, so a Java object returned through a base-interface signature (e.g. an anonymous class that implements a derived interface) was marshalled to the base interface's invoker instead of the most-derived interface proxy. When targetType is an interface, also enumerate the Java class's interfaces (recursively into super-interfaces) and select the most-derived registered .NET type assignable to targetType, mirroring the interface-walk already used by TrimmableTypeMap.GetProxyForJavaObject. Fixes JavaInterfaceLookup_BaseInterfaceReturnType_UsesDerivedInterfaceProxy, TrustManagerFactory_GetTrustManagers_ReturnsIX509TrustManager and the dependent ServerCertificateCustomValidationCallback_* tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… managers Extract the Class.getInterfaces() recursive walk into a shared JavaInterfaceHierarchy.FindFirst helper and use it from both CoreClrJavaMarshalValueManager.CreatePeerInstance and TrimmableTypeMap.TryMatchInterfaces, removing the duplicated JNI plumbing (getInterfaces method id + traversal) from both. No behavior change; consolidates the interface-derived-proxy resolution added in the previous commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The value-manager/type-manager split reduces the IL3050 AOT-analysis warnings a basic NativeAOT app produces from 10 to 6: three distinct warnings (reflection-backed ManagedTypeManager ctor, JNIEnv.MakeArrayType, JNINativeWrapper.CreateDelegate), each surfaced twice in the MSBuild summary. Count verified from build.log of the apk and aab NativeAOT runs in CI build 1485907. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The value-manager/type-manager split shrinks the release arm64 app: - NativeAOT: libUnnamedProject.so -10.87%, package 2,474,779 -> 2,286,363 B - CoreCLR: libassembly-store.so -7.19%, package 7,497,147 -> 7,312,827 B apkdiff flags these via --descrease-is-regression, so refresh the reference descriptions. New .apkdesc files taken from the apk/aab BuildReleaseArm64 test attachments in CI build 1485907. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mono.Android.csproj uses an explicit <Compile> item list, so the new JavaInterfaceHierarchy.cs was not compiled, causing CS0103 'JavaInterfaceHierarchy does not exist' in TrimmableTypeMap.cs and CoreClrJavaMarshalValueManager.cs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mmable-typemap-core # Conflicts: # src/Mono.Android/Mono.Android.csproj # src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs
…TypeMap value-manager branch
jonathanpeppers
approved these changes
Jun 30, 2026
simonrozsival
added a commit
that referenced
this pull request
Jun 30, 2026
This file was inadvertently reverted to #nullable disable during the rebase onto main; it carries nullable reference annotations and main (#11799) had it as #nullable enable. Restore to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
simonrozsival
added a commit
that referenced
this pull request
Jul 2, 2026
…11801) ## Summary This PR adds the two runtime managers that make the **trimmable typemap** a fully functional, reflection-free peer-resolution path: - **`TrimmableTypeMapValueManager`** — creates and tracks Java↔managed peers without reflection or `Activator`. - **`TrimmableTypeMapTypeManager`** — resolves managed↔Java type mappings by delegating to the generated `TrimmableTypeMap`. Both are selected through the existing `RuntimeFeature.TrimmableTypeMap` switch and wired into `JNIEnvInit`. The trimmable path is **opt-in**; the NativeAOT default remains `managed`, and Mono/CoreCLR defaults are unchanged. Also included is the `JavaConvert` collection-factory refactor and a generator fix that the trimmable managers depend on. ## Background The classic Java.Interop runtime resolves the managed `Type` for a Java instance (and vice-versa) using `Type.GetType`, `MakeGenericType`, and `Activator`-style construction. That is incompatible with trimming and NativeAOT: the trimmer can't see which types are reachable, and the types can't be created at runtime under AOT. The **trimmable typemap** replaces those reflection calls with a build-time-generated map of `JavaPeerProxy` objects (produced by `Microsoft.Android.Sdk.TrimmableTypeMap`). Each proxy knows how to construct a specific peer and describes its JNI ↔ managed association statically. Earlier PRs landed the generator, the proxy shapes (array / generic-base / interface / unresolvable peers), and the CoreCLR `JavaMarshal` value-manager split. **This PR adds the runtime managers that consume that generated map.** ## What's in this PR ### 1. `TrimmableTypeMapValueManager` (new) A `JniRuntime.JniValueManager` that performs peer creation with **no reflection**: - **Peer lifetime** (`AddPeer` / `PeekPeer` / `RemovePeer` / `FinalizePeer` / `CollectPeers` / `GetSurfacedPeers`) is delegated to `JavaMarshalRegisteredPeers`, i.e. the CoreCLR `JavaMarshal` GC-bridge machinery. `WaitForGCBridgeProcessing` is intentionally a no-op (documented: the wait can't close the bridge race on CoreCLR, where JNI wrapper threads hold their own `JniObjectReference` copies). - **`CreatePeer`** resolves the requested target type (mapping `object`/`IJavaPeerable` → `Java.Interop.JavaObject`, `Exception` → `JavaException`) and asks `TrimmableTypeMap.Instance.CreateInstance (handle, resolvedType)` to build the peer from a generated proxy. - **`ActivatePeer`** throws `PlatformNotSupportedException` — reflection-based activation is not part of this path. - **`NotFoundFallback`** carefully reproduces the base `JniValueManager.CreatePeer` contract so `JavaCast`/`JavaAs` still surface the correct outcome when no proxy is found: - target type has no Java mapping → `ArgumentException` - Java instance not assignable to the target's Java class → `null` (so `JavaAs` returns null / `JavaCast` throws `InvalidCastException`) - compatible classes but no proxy → `NotSupportedException` (a genuine generator gap, with a message pointing at the missing proxy) The assignability check honors `RuntimeFeature.IsAssignableFromCheck` and mirrors the legacy cast diagnostic when assembly logging is enabled. ### 2. `TrimmableTypeMapTypeManager` (new) A `JniRuntime.JniTypeManager` that has exactly two live responsibilities and throws for everything else it doesn't need: - **Managed → Java** via `GetTypeSignatureCore`, backed by a `ConcurrentDictionary<Type, JniTypeSignature>` cache. - **Java → managed** via `GetTypes` / `GetTypeForSimpleReference`, delegating to `TrimmableTypeMap`. - **Array handling** diverges by runtime: NativeAOT reads a pre-generated array-proxy map (types can't be built at runtime), while CoreCLR builds array/generic types dynamically to save app size (suppressions are scoped to the CoreCLR-only branch). ### 3. `JNIEnvInit` wiring `CreateValueManager` / `CreateTypeManager` now return the trimmable managers when `RuntimeFeature.TrimmableTypeMap` is set. The existing Mono / CoreCLR / managed selection is preserved, and the manager constructions were refactored into small local helpers so trimming suppressions (`IL2026` / `IL3050`) apply only to the exact branch that needs them. `RegisterJniNatives` is likewise gated so the reflection-based JNI registration path isn't emitted for the trimmable typemap. ### 4. `JavaConvert` collection-factory refactor Generic collection marshalling (`IDictionary<,>` → `JavaDictionary<,>`, `IList<>` → `JavaList<>`, `ICollection<>` → `JavaCollection<>`) is split into two branches: a **factory-based converter** on the trimmable path (no `MakeGenericType`) and the classic `MakeGenericType` path elsewhere, with the reflection-requiring code isolated behind narrowly-scoped suppressions. Also adds `Nullable<T>` converter handling (null reference → null value). ### 5. Generator fix (`ModelBuilder`) Emits a managed→Java typemap entry for **self-peer types** (`[JniTypeSignature(GenerateJavaPeer=false)]` or MCW bindings with no activation ctor). These are constructed managed-side with `new`, but their JNI name must still resolve so the correct Java class is instantiated; without the association they fell back to the generic `mono.android.runtime.JavaObject` peer and threw `ArrayStoreException` when placed into a typed Java array. ### 6. Cleanup Removes the dead `TrimmableTypeMap` branch from `JavaMarshalValueManager` (that logic now lives in the dedicated `TrimmableTypeMapValueManager`). ## Behavioral impact - **Opt-in only.** With `RuntimeFeature.TrimmableTypeMap` unset, behavior is unchanged. NativeAOT still defaults to `managed`. - No new user-facing / localized strings; error messages point at the generator when a proxy is genuinely missing. ## Status All earlier prerequisites have **merged into `main`**, and this PR is **rebased on latest `main`**, so it is no longer stacked or blocked — it contains only the value/type-manager implementations (plus the supporting `JavaConvert` and generator changes) on top of them: - #11799 — CoreCLR `JavaMarshal` split + Java.Interop bump - #11753 array proxies · #11749 generic base · #11751 unresolvable peers · #11769 interface proxies - #11794 multidex/manifest base · #11796 manifest parity · #11798 R8 keep ## Testing - `TrimmableTypeMapTypeManagerTests` and `TypeMapModelBuilderTests` updated/extended for the new type resolution and the self-peer generator fix. - Export tests enabled for the trimmable typemap; `TrimmableTypeMapUnsupported` cases excluded. - NativeAOT warning-count and CoreCLR `apkdesc` baselines updated to match.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bumps external/Java.Interop and aligns Mono.Android's reflection-based type and value managers with the new base-class contracts. The Java.Interop bump relaxes the
[DynamicallyAccessedMembers](DAM) requirements on the virtual members ofJniRuntime.ReflectionJniTypeManager/JniRuntime.ReflectionJniValueManager, so the Mono.Android overrides no longer need to repeat those annotations and instead rely on targeted trimmer/AOT suppressions.This is a standalone slice of #11617 with no dependency on the trimmable type-map scanner/emitter or array codegen work.
Changes
external/Java.Interop→8d544738a(from70493645c).[DynamicallyAccessedMembers]annotations from the overrides inAndroidTypeManager,ManagedTypeManager, andTrimmableTypeMapTypeManager(GetInvokerTypeCore,GetTypeForSimpleReference,RegisterNativeMembers,ActivatePeer), replacing them with[UnconditionalSuppressMessage]where the trimmer still needs reassurance.JavaMarshalValueManagernow extendsJniRuntime.ReflectionJniValueManagerdirectly. It is markedsealedand carries[RequiresDynamicCode]/[RequiresUnreferencedCode], uses the baseEnsureNotDisposed ()helper, and drops its own dispose tracking andActivatePeeroverride.AndroidReflectionJniValueManagerandSimpleValueManager(and theirMono.Android.csprojentries).JNIEnvInit.CreateValueManagercreates the value manager through a local helper with the appropriate trimming/AOT suppressions for both the CoreCLR and NativeAOT paths.Tests / baselines
Android.Graphics.ColorValueMarshaler.CreateGenericValue'stargetTypeparameter no longer carries a DAM attribute (inherited from the Java.Interop base-class change).SimpleDotNetCoreCLR/NativeAOT apkdesc size baselines and the NativeAOTBuildHasNoWarningscount, plusBuildTest2.Context
Carved out of #11617.
JavaMarshalRegisteredPeersextraction already merged via #11750. The trimmable managers, scanner/emitter, manifest, and R8 changes ship in their own PRs (#11749/#11751/#11753/#11769/#11794/#11796/#11798).Mono.Android compiles cleanly (0 errors).