Skip to content

[TrimmableTypeMap] Re-enable JavaCast / JavaAs tests under trimmable typemap#11225

Draft
simonrozsival wants to merge 7 commits intomainfrom
dev/simonrozsival/trimmable-typemap-javacast
Draft

[TrimmableTypeMap] Re-enable JavaCast / JavaAs tests under trimmable typemap#11225
simonrozsival wants to merge 7 commits intomainfrom
dev/simonrozsival/trimmable-typemap-javacast

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

@simonrozsival simonrozsival commented Apr 27, 2026

Summary

Fix JavaCast / JavaAs behavior under the trimmable typemap and re-enable 5 device tests that were previously excluded.

Cast error disambiguation

When a trimmable typemap proxy lookup fails, CreatePeer now distinguishes between:

  • Incompatible Java types → returns null (JavaCast wraps to InvalidCastException, JavaAs returns null)
  • Missing typemap entry → throws ArgumentException
  • Compatible types but no proxy → throws NotSupportedException

Previously all failures threw NotSupportedException, which broke JavaCast / JavaAs contracts.

Also adds ResolvePeerType to map universal interfaces (IJavaPeerable, object, Exception) to concrete peer types before proxy lookup, and wraps FindClass in try/catch so missing Java classes surface as ArgumentException rather than leaking ClassNotFoundException.

Closed-generic peer activation

Open-generic proxies (e.g. JavaList<>) cannot newobj closed instantiations (e.g. JavaList<int>) in generated IL. Adds Object.ActivatePeer() which uses RuntimeHelpers.GetUninitializedObject to allocate the closed type, then calls the Java.Lang.Object(IntPtr, JniHandleOwnership) activation ctor via [UnsafeAccessor]. No reflection, fully AOT/trim-safe.

TargetTypeMatches is restructured so open-generic proxies match only closed instantiations of their definition — preventing incorrect matches like JavaArray<> matching any JavaObject-targeted cast.

Interface invoker activation ctor

Interface peers have no constructors. TryResolveActivationCtorOnInvoker resolves the activation ctor from the invoker type so the generator picks the correct ctor signature (XA-style vs JI-style).

Trimmable GetThis.java

The net.dot.jni.test.GetThis Java class (in the Java.Interop submodule) registers native methods via ManagedPeer.registerNativeMembers which the trimmable path rejects. Adds an Android-trimmable variant that omits the desktop static block. Java.Interop-Tests.targets swaps variants based on _AndroidTypeMapImplementation.

Test exclusion updates

Re-enabled (5 tests):

  • JavaCast_BadInterfaceCast
  • JavaCast_BaseToGenericWrapper (closed-generic activation)
  • JavaCast_CheckForManagedSubclasses
  • JavaCast_InvalidTypeCastThrows
  • DisposeAccessesThis (trimmable GetThis.java)

Updated remaining exclusion comments with accurate root-cause descriptions. The JavaProxyObject / JavaProxyThrowable exclusions are tracked at #11170.

Test results

Trimmable + CoreCLR lane: 917 total, 0 errors, 3 failures (pre-existing TryGetJniNameForManagedType_*, out of scope).

Base automatically changed from dev/simonrozsival/trimmable-test-plumbing to main April 27, 2026 21:15
An error occurred while trying to automatically change base from dev/simonrozsival/trimmable-test-plumbing to main April 27, 2026 21:15
@simonrozsival simonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 27, 2026
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-javacast branch from ced5233 to f69f27b Compare April 27, 2026 21:25
simonrozsival and others added 4 commits April 28, 2026 00:50
…te closed generics

Improve JavaCast/JavaAs behavior under the trimmable typemap:

1. Bad-cast disambiguation: When proxy lookup fails, distinguish
   between incompatible Java types (return null → InvalidCastException
   in JavaCast) and missing typemap entries (NotSupportedException).
   Previously all failures threw NotSupportedException.

2. Closed-generic activation: Add Object.ActivatePeer() which uses
   GetUninitializedObject + UnsafeAccessor to call the Java.Lang.Object
   activation ctor on closed generic types (e.g. JavaList<int>).
   Open-generic proxies can't newobj closed instantiations in IL,
   so CreatePeer falls back to this path when the proxy's TargetType
   is an open generic definition.

3. Type resolution: Add ResolvePeerType to map universal interfaces
   (IJavaPeerable, object, Exception) to concrete peer types before
   proxy lookup, mirroring the legacy GetPeerType behavior.

4. TargetTypeMatches: Restructure to check open-generic proxy match
   first (closed instantiation of the definition), avoiding incorrect
   IsAssignableFrom matches on unrelated open generics.

5. FindClass safety: Catch ClassNotFoundException in TryGetProxyFromTargetType
   for types whose Java peer class is not in the APK.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Interface peers have no constructors of their own, so the activation
ctor resolution returns null. Add TryResolveActivationCtorOnInvoker
to resolve the ctor from the invoker type, so the generator can pick
the correct ctor signature (XamarinAndroid vs JavaInterop style).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The GetThis.java class in the Java.Interop submodule uses
ManagedPeer.registerNativeMembers which the trimmable typemap path
rejects. Add a parallel Android-trimmable variant that uses
mono.android.Runtime.register instead, conditionally swapped in
via Java.Interop-Tests.targets when _AndroidTypeMapImplementation
is 'trimmable'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re-enable 5 tests that now pass:
- JavaCast_BadInterfaceCast
- JavaCast_BaseToGenericWrapper (closed-generic via ActivatePeer)
- JavaCast_CheckForManagedSubclasses
- JavaCast_InvalidTypeCastThrows
- DisposeAccessesThis (via trimmable GetThis.java)

Update exclusion comments with accurate root-cause descriptions
for the remaining excluded tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-javacast branch from 885680d to 01577a3 Compare April 27, 2026 22:50
simonrozsival and others added 2 commits April 28, 2026 09:22
Match legacy behavior: Type.GetConstructor() doesn't find inherited
constructors in .NET. When the activation ctor is on a base type
(IsOnLeafType=false) and there's no invoker type, emit NoActivation
(return null) instead of synthesizing via the inherited ctor.

This fixes JavaAs_Exceptions which expects NotSupportedException
when the target type has no own activation ctor.

Re-enables JavaAs_Exceptions test (6 total tests re-enabled in PR).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ResolvePeerType now handles null input directly
- Remove redundant `?? targetType` fallback (resolvedTargetType is
  null iff targetType is null after ResolvePeerType handles null)
- Rename `idx` → `assembly` in TryResolveActivationCtorOnInvoker
  and document assemblyCache size (10–30 entries, O(1) dict probe)
- Add remarks to ActivatePeer documenting why calling
  Java.Lang.Object::.ctor directly is safe for closed generics

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-javacast branch from 18db335 to bb1bfc9 Compare April 28, 2026 09:41
- Catch JniException specifically instead of bare catch in
  TryGetProxyFromTargetType FindClass fallback
- Document why UnsafeAccessorKind.Method (not Constructor) is used
  for InvokeActivationCtor — calls ctor on existing object
- Add comment noting TestJarEntry conditional swap requires clean
  rebuild when switching _AndroidTypeMapImplementation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-javacast branch from bb1bfc9 to 4a2262f Compare April 28, 2026 09:46
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