From 0ea421e72a22c5e50c307b0fd16a909b5358e260 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Sun, 5 Jul 2026 00:18:52 +0200 Subject: [PATCH 1/2] [Tests] Skip JniNativeMethodRegistration tests on trimmable/NativeAOT RegisterNativeMethods_JniNativeMethodRegistration and RegisterNativeMethods_JniNativeMethodRegistration_ManyMethods exercise the JniNativeMethodRegistration[] RegisterNatives path, which marshals managed delegates and requires dynamic code (IL3050) - unsupported under the trimmable typemap and NativeAOT, where native registration goes through the runtime registerNatives path instead. Add [Category ("NativeAOTIgnore")] and [Category ("TrimmableTypeMapUnsupported")] so TestInstrumentation excludes them on NativeAOT (PublishAot) and trimmable typemap runs, matching the other unsupported Java.Interop tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs index 9120d400345..24430db66fb 100644 --- a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs +++ b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs @@ -219,6 +219,8 @@ public unsafe void RegisterNativeMethods_JniNativeMethod () static int NativeAdd (IntPtr jnienv, IntPtr self, int a, int b) => a + b; [Test] + [Category ("NativeAOTIgnore")] + [Category ("TrimmableTypeMapUnsupported")] [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Test exercises non-AOT-compatible JniNativeMethodRegistration[] registration path.")] public unsafe void RegisterNativeMethods_JniNativeMethodRegistration () { @@ -253,6 +255,8 @@ public unsafe void RegisterNativeMethods_JniNativeMethodRegistration () static int ManagedAdd (IntPtr jnienv, IntPtr self, int a, int b) => a + b; [Test] + [Category ("NativeAOTIgnore")] + [Category ("TrimmableTypeMapUnsupported")] [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Test exercises non-AOT-compatible JniNativeMethodRegistration[] registration path with many methods.")] public unsafe void RegisterNativeMethods_JniNativeMethodRegistration_ManyMethods () { From d0395efb421b00875b4e20cdef05abb40ecf5946 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 8 Jul 2026 11:20:08 +0200 Subject: [PATCH 2/2] [tests] Use blittable RegisterNatives in JNI callback tests Keep the RegisterNatives coverage enabled under NativeAOT by switching the affected tests from JniNativeMethodRegistration[] delegate marshaling to the blittable JniNativeMethod span overload with UnmanagedCallersOnly function pointers. This avoids the dynamic-code requirement while preserving the single-method and many-method registration coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Java.Interop/JniTypeTest.cs | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs index 24430db66fb..5bdbd9fc9e0 100644 --- a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs +++ b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs @@ -219,20 +219,16 @@ public unsafe void RegisterNativeMethods_JniNativeMethod () static int NativeAdd (IntPtr jnienv, IntPtr self, int a, int b) => a + b; [Test] - [Category ("NativeAOTIgnore")] - [Category ("TrimmableTypeMapUnsupported")] - [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Test exercises non-AOT-compatible JniNativeMethodRegistration[] registration path.")] - public unsafe void RegisterNativeMethods_JniNativeMethodRegistration () + public unsafe void RegisterNativeMethods_JniNativeMethod_UnmanagedCallersOnly () { using (var nativeClass = new JniType ("net/dot/jni/test/RegisterNativesTestType")) { - // Test the JniNativeMethodRegistration[] registration path (the one that marshals to blittable) - var methods = new JniNativeMethodRegistration [1]; - methods [0] = new JniNativeMethodRegistration ( - "add", - "(II)I", - new AddDelegate (ManagedAdd)); - - JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, methods, methods.Length); + Span methods = stackalloc JniNativeMethod [1]; + fixed (byte* namePtr = "add"u8) + fixed (byte* sigPtr = "(II)I"u8) { + methods [0] = new JniNativeMethod (namePtr, sigPtr, + (IntPtr) (delegate* unmanaged) &ManagedAdd); + JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, methods); + } // Call the native method from Java to verify registration worked var ctor = JniEnvironment.InstanceMethods.GetMethodID (nativeClass.PeerReference, "", "()V"); @@ -250,28 +246,26 @@ public unsafe void RegisterNativeMethods_JniNativeMethodRegistration () } } - delegate int AddDelegate (IntPtr jnienv, IntPtr self, int a, int b); - + [UnmanagedCallersOnly] static int ManagedAdd (IntPtr jnienv, IntPtr self, int a, int b) => a + b; [Test] - [Category ("NativeAOTIgnore")] - [Category ("TrimmableTypeMapUnsupported")] - [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Test exercises non-AOT-compatible JniNativeMethodRegistration[] registration path with many methods.")] - public unsafe void RegisterNativeMethods_JniNativeMethodRegistration_ManyMethods () + public unsafe void RegisterNativeMethods_JniNativeMethod_UnmanagedCallersOnly_ManyMethods () { using (var nativeClass = new JniType ("net/dot/jni/test/RegisterNativesTestType")) { - // Test the heap allocation path (> 32 methods) to ensure the marshalling loop handles it - var methods = new JniNativeMethodRegistration [40]; - for (int i = 0; i < methods.Length; ++i) { - methods [i] = new JniNativeMethodRegistration ( - "add", - "(II)I", - new AddDelegate (ManagedAdd)); - } + // Keep coverage for a large registration set while using the NativeAOT-compatible + // blittable overload instead of the delegate-marshaling overload. + var methods = new JniNativeMethod [40]; + fixed (byte* namePtr = "add"u8) + fixed (byte* sigPtr = "(II)I"u8) { + for (int i = 0; i < methods.Length; ++i) { + methods [i] = new JniNativeMethod (namePtr, sigPtr, + (IntPtr) (delegate* unmanaged) &ManagedAdd); + } - // This should not crash even with > 32 methods - JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, methods, methods.Length); + // This should not crash even with > 32 methods. + JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, methods); + } // Verify the registration worked by calling the method var ctor = JniEnvironment.InstanceMethods.GetMethodID (nativeClass.PeerReference, "", "()V"); @@ -290,4 +284,3 @@ public unsafe void RegisterNativeMethods_JniNativeMethodRegistration_ManyMethods } } } -