Skip to content

[TrimmableTypeMap] Reflection-free TrimmableTypeMapType/ValueManager#11801

Draft
simonrozsival wants to merge 37 commits into
mainfrom
dev/simonrozsival/trimmable-managers
Draft

[TrimmableTypeMap] Reflection-free TrimmableTypeMapType/ValueManager#11801
simonrozsival wants to merge 37 commits into
mainfrom
dev/simonrozsival/trimmable-managers

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Jun 29, 2026

Copy link
Copy Markdown
Member

Summary

Adds the reflection-free TrimmableTypeMapValueManager and TrimmableTypeMapTypeManager, plus the opt-in wiring in JNIEnvInit (RuntimeFeature.TrimmableTypeMap) that selects them. Also includes the JavaConvert collection-factory refactor used by the trimmable path. NativeAOT default stays managed; trimmable is opt-in.

Status

All of the earlier prerequisites have now merged into main, and this PR has been rebased on latest main:

With those in main, this PR is no longer stacked or blocked — it contains only the value/type manager implementations on top of them.

@simonrozsival simonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Jun 29, 2026
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-managers branch from 9f999e1 to 4632939 Compare June 30, 2026 10:07
Base automatically changed from dev/simonrozsival/trimmable-typemap-core to main June 30, 2026 12:48
simonrozsival and others added 23 commits June 30, 2026 15:32
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
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>
Adds TrimmableTypeMapValueManager + reflection-free TrimmableTypeMapTypeManager and
wires opt-in selection (RuntimeFeature.TrimmableTypeMap) in JNIEnvInit. Builds on the
CoreCLR JavaMarshal split. Default stays managed; depends on array-codegen (#11753)
and other slices to be fully functional at runtime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Revert Color.cs/JavaProxyThrowable.cs formatting, RuntimeFeature feature-gates
(suppressions cover it), and the ITypeMap array-proxy rename (deferred to array PR).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The trimmable typemap path now uses the dedicated TrimmableTypeMapValueManager
(selected in JNIEnvInit.CreateValueManager), so the RuntimeFeature.TrimmableTypeMap
branch in JavaMarshalValueManager.CreatePeer is unreachable. Remove it along with
the now-unused ResolvePeerType/IsIncompatibleCast helpers; that logic lives in
JavaMarshalValueManagerHelper used by the dedicated value manager.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The rebase auto-merge duplicated the JavaArrayProxy attribute class in
JavaPeerProxy.cs (one terse copy from this branch, one documented copy from
core); remove the duplicate. Also align JavaPeerProxy<T>.GetContainerFactory to
use the JavaPeerContainerFactory<T>.Instance singleton (its constructor is
private) instead of 'new'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-managers branch from 74e5d3d to e15e6b9 Compare June 30, 2026 13:34
simonrozsival and others added 2 commits June 30, 2026 15:47
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>
- TrimmableTypeMapValueManager: remove no-op Dispose(bool) override (sealed
  class) and use ArgumentNullException.ThrowIfNull in ConstructPeerCore.
- TrimmableTypeMapTypeManager: promote GetBuiltInTypeForSimpleReference to a
  class-level method (drops empty XML doc tags) and make GetTypeForSimpleReference
  resolve the first match directly instead of allocating the iterator/enumerator.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
simonrozsival and others added 2 commits June 30, 2026 16:00
Replace the hard-coded (JniObjectReferenceOptions)(1<<1)/(1<<2) literals for
DisposeSource and DoNotRegisterTarget with constant expressions derived from the
public CopyAndDispose / CopyAndDoNotRegister members, so the bits stay pinned to
Java.Interop's public ABI instead of duplicating magic numbers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Concrete types that supply their own Java peer via [JniTypeSignature(GenerateJavaPeer=false)] (DoNotGenerateAcw=true) have no activation ctor or invoker, so ModelBuilder emitted no JavaPeerProxy/TypeMapAssociation for them. At runtime TryGetJniNameForManagedType failed and 'new CrossReferenceBridge()' fell back to the generic mono.android.runtime.JavaObject peer, throwing ArrayStoreException when stored in a typed Java array. This aborted the whole CoreCLRTrimmable/NativeAOT device suites (Zero tests ran).

Emit a minimal proxy + association for concrete self-peer types so the managed→Java JNI name resolves. Adds a regression test.
@simonrozsival

Copy link
Copy Markdown
Member Author

CoreCLRTrimmable device-test crash analysis (recording so we don't lose it)

Observed on the public dotnet-android pipeline, build 1487907 (run Mono.Android.NET_Tests-CoreCLRTrimmable, macOS > Tests > APKs 2).

Symptom

The step reported, after 52s:

Zero tests ran [+0/x0/?0] (52s 325ms)
Exit code: 1
  Error output: Error: Instrumentation did not report a resultsPath in the bundle.

"Zero tests ran" is misleading — it doesn't mean no tests executed, it means the test host never received a results file.

What actually happened

From the captured logcat artifact (net11.0-CoreCLRTrimmable/logcat-Mono.Android.NET_Tests-CoreCLRTrimmable.txt), the CoreCLRTrimmable app process (pid 4663) ran 263 tests before dying:

  • 251 passed, 1 failed, 10 skipped
  • App start 14:59:32 → crash 15:00:2149s of real test execution (so the 52s wasn't a hang/idle — it was doing work the whole time).

It crashed with a native SIGSEGV in the CoreCLR GC, mid-test:

06-30 15:00:21.301  4663  4686 I TestInstrumentation: [START] Xamarin.Android.NetTests.AndroidMessageHandlerTests.ServerCertificateCustomValidationCallback_ApprovesRequestWithInvalidCertificate
06-30 15:00:21.320  4663  4673 E DOTNET  : Fatal error. Got a SIGSEGV while executing native code.
06-30 15:00:21.324  4663  4673 F libc    : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4 in tid 4673 (HeapTaskDaemon), pid 4663 (droid.NET_Tests)
06-30 15:00:21.629  2657  2657 I Zygote  : Process 4663 exited due to signal 11 (Segmentation fault)
06-30 15:00:21.712  2674  2696 W ActivityManager: Crash of app Mono.Android.NET_Tests running instrumentation … TestInstrumentation

Fault addr 0x4 on the HeapTaskDaemon thread = a near-null deref inside the GC. It happened right after ServerCertificateCustomValidationCallback_ApproveRequest failed, during the next ServerCertificateCustomValidationCallback_* test.

Why it reported 0/0/0

Because the process died mid-run, the on-device instrumentation's final Finish() never executed, so no TRX was written and no resultsPath was reported → the host adapter threw "Instrumentation did not report a resultsPath in the bundle." and MTP discarded all 263 executed tests.

Follow-ups

  1. The reporting gap (tests lost on crash) is fixed by [tests] Stream on-device test results to MTP as they finish #11833 — results now stream to MTP per-test, so a mid-run crash only loses the in-flight test and the crash itself is reported as a failure.
  2. The ServerCertificateCustomValidationCallback_* crash matches Custom AndroidMessageHandler.ServerCertificateCustomValidationCallback that returns false may cause application crash while debugging. #8608 and is [Ignore]d in [tests] Stream on-device test results to MTP as they finish #11833 to unblock CoreCLR/Trimmable device runs; the native GC crash root cause still needs diagnosing separately.

Note: this manifested on PR #11802's validation run, but the crash is in the CoreCLR GC during an HTTP/TLS test and looks unrelated to the trimmable-typemap changes (the NativeAOT config in the same job passed).

simonrozsival added a commit to simonrozsival/android that referenced this pull request Jul 1, 2026
The [Ignore] for the crashing AndroidMessageHandlerTests
.ServerCertificateCustomValidationCallback_* tests (dotnet#8608)
belongs with the trimmable-typemap work; move it to dotnet#11801. This PR is
now scoped to the MTP streaming-results change only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
simonrozsival and others added 5 commits July 1, 2026 11:32
AndroidMessageHandlerTests.ServerCertificateCustomValidationCallback_*
crash the test process with a native SIGSEGV (#8608),
which under CoreCLR/Trimmable takes down the whole instrumentation run
and drops every result. Ignore them until the native crash is
diagnosed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… base

ManagedTypeManager derived from the minimal JniRuntime.JniTypeManager base,
whose GetType()/GetTypes()/GetTypeSignatureCore() do not dispatch to the plural
GetTypesForSimpleReference/GetSimpleReferences overrides. That left the only
references to ManagedTypeMapping as dead code, so the trimmer removed the type
and the managed TypeMappingStep crashed with 'Unable to find
Microsoft.Android.Runtime.ManagedTypeMapping type' (IL1012 / NETSDK1144),
failing every NativeAOT MSBuild test.

Restore the ReflectionJniTypeManager base (as on main) so the lookup dispatch
works, and re-add the singular GetTypeForSimpleReference override so both the
GetType and GetTypes paths consult ManagedTypeMapping.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copilot `copilot-cli` or other AIs were used to author this trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant