From b5243f0eefe9dee998b1cabfc2e1dbfab0647944 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 9 Oct 2026 00:20:38 +0200 Subject: [PATCH 1/3] [Mono.Android] Type-check JNI callback delegates Dispatch built-in JNI callbacks by concrete delegate type rather than their simple names. A custom delegate with a colliding name must use the Reflection.Emit fallback with its original delegate type and signature. The 40 built-in mappings and their callback wrappers remain unchanged. Context: https://github.com/dotnet/android/issues/11467 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Android.Runtime/JNINativeWrapper.g.cs | 163 +++++++++--------- .../Android.Runtime/JNINativeWrapper.g.tt | 7 +- .../Android.Runtime/JnienvArrayMarshaling.cs | 13 ++ 3 files changed, 97 insertions(+), 86 deletions(-) diff --git a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs index 9c7244b849a..28609920b38 100644 --- a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs +++ b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics; -using System.Runtime.CompilerServices; namespace Android.Runtime { @@ -457,87 +456,87 @@ internal static void Wrap_JniMarshal_PPLIIIIIIII_V (this _JniMarshal_PPLIIIIIIII private static Delegate CreateBuiltInDelegate (Delegate dlg, Type delegateType) { - switch (delegateType.Name) { - case nameof (_JniMarshal_PP_V): - return new _JniMarshal_PP_V (Unsafe.As<_JniMarshal_PP_V> (dlg).Wrap_JniMarshal_PP_V); - case nameof (_JniMarshal_PP_I): - return new _JniMarshal_PP_I (Unsafe.As<_JniMarshal_PP_I> (dlg).Wrap_JniMarshal_PP_I); - case nameof (_JniMarshal_PP_Z): - return new _JniMarshal_PP_Z (Unsafe.As<_JniMarshal_PP_Z> (dlg).Wrap_JniMarshal_PP_Z); - case nameof (_JniMarshal_PPI_V): - return new _JniMarshal_PPI_V (Unsafe.As<_JniMarshal_PPI_V> (dlg).Wrap_JniMarshal_PPI_V); - case nameof (_JniMarshal_PPI_L): - return new _JniMarshal_PPI_L (Unsafe.As<_JniMarshal_PPI_L> (dlg).Wrap_JniMarshal_PPI_L); - case nameof (_JniMarshal_PPI_I): - return new _JniMarshal_PPI_I (Unsafe.As<_JniMarshal_PPI_I> (dlg).Wrap_JniMarshal_PPI_I); - case nameof (_JniMarshal_PPI_J): - return new _JniMarshal_PPI_J (Unsafe.As<_JniMarshal_PPI_J> (dlg).Wrap_JniMarshal_PPI_J); - case nameof (_JniMarshal_PPL_I): - return new _JniMarshal_PPL_I (Unsafe.As<_JniMarshal_PPL_I> (dlg).Wrap_JniMarshal_PPL_I); - case nameof (_JniMarshal_PPL_L): - return new _JniMarshal_PPL_L (Unsafe.As<_JniMarshal_PPL_L> (dlg).Wrap_JniMarshal_PPL_L); - case nameof (_JniMarshal_PPL_V): - return new _JniMarshal_PPL_V (Unsafe.As<_JniMarshal_PPL_V> (dlg).Wrap_JniMarshal_PPL_V); - case nameof (_JniMarshal_PPL_Z): - return new _JniMarshal_PPL_Z (Unsafe.As<_JniMarshal_PPL_Z> (dlg).Wrap_JniMarshal_PPL_Z); - case nameof (_JniMarshal_PPJ_Z): - return new _JniMarshal_PPJ_Z (Unsafe.As<_JniMarshal_PPJ_Z> (dlg).Wrap_JniMarshal_PPJ_Z); - case nameof (_JniMarshal_PPII_V): - return new _JniMarshal_PPII_V (Unsafe.As<_JniMarshal_PPII_V> (dlg).Wrap_JniMarshal_PPII_V); - case nameof (_JniMarshal_PPII_L): - return new _JniMarshal_PPII_L (Unsafe.As<_JniMarshal_PPII_L> (dlg).Wrap_JniMarshal_PPII_L); - case nameof (_JniMarshal_PPLI_V): - return new _JniMarshal_PPLI_V (Unsafe.As<_JniMarshal_PPLI_V> (dlg).Wrap_JniMarshal_PPLI_V); - case nameof (_JniMarshal_PPLZ_V): - return new _JniMarshal_PPLZ_V (Unsafe.As<_JniMarshal_PPLZ_V> (dlg).Wrap_JniMarshal_PPLZ_V); - case nameof (_JniMarshal_PPLL_V): - return new _JniMarshal_PPLL_V (Unsafe.As<_JniMarshal_PPLL_V> (dlg).Wrap_JniMarshal_PPLL_V); - case nameof (_JniMarshal_PPLF_V): - return new _JniMarshal_PPLF_V (Unsafe.As<_JniMarshal_PPLF_V> (dlg).Wrap_JniMarshal_PPLF_V); - case nameof (_JniMarshal_PPLI_L): - return new _JniMarshal_PPLI_L (Unsafe.As<_JniMarshal_PPLI_L> (dlg).Wrap_JniMarshal_PPLI_L); - case nameof (_JniMarshal_PPLL_L): - return new _JniMarshal_PPLL_L (Unsafe.As<_JniMarshal_PPLL_L> (dlg).Wrap_JniMarshal_PPLL_L); - case nameof (_JniMarshal_PPLL_Z): - return new _JniMarshal_PPLL_Z (Unsafe.As<_JniMarshal_PPLL_Z> (dlg).Wrap_JniMarshal_PPLL_Z); - case nameof (_JniMarshal_PPIL_Z): - return new _JniMarshal_PPIL_Z (Unsafe.As<_JniMarshal_PPIL_Z> (dlg).Wrap_JniMarshal_PPIL_Z); - case nameof (_JniMarshal_PPIIL_V): - return new _JniMarshal_PPIIL_V (Unsafe.As<_JniMarshal_PPIIL_V> (dlg).Wrap_JniMarshal_PPIIL_V); - case nameof (_JniMarshal_PPLII_I): - return new _JniMarshal_PPLII_I (Unsafe.As<_JniMarshal_PPLII_I> (dlg).Wrap_JniMarshal_PPLII_I); - case nameof (_JniMarshal_PPLII_Z): - return new _JniMarshal_PPLII_Z (Unsafe.As<_JniMarshal_PPLII_Z> (dlg).Wrap_JniMarshal_PPLII_Z); - case nameof (_JniMarshal_PPLII_V): - return new _JniMarshal_PPLII_V (Unsafe.As<_JniMarshal_PPLII_V> (dlg).Wrap_JniMarshal_PPLII_V); - case nameof (_JniMarshal_PPIII_V): - return new _JniMarshal_PPIII_V (Unsafe.As<_JniMarshal_PPIII_V> (dlg).Wrap_JniMarshal_PPIII_V); - case nameof (_JniMarshal_PPLLJ_Z): - return new _JniMarshal_PPLLJ_Z (Unsafe.As<_JniMarshal_PPLLJ_Z> (dlg).Wrap_JniMarshal_PPLLJ_Z); - case nameof (_JniMarshal_PPILL_V): - return new _JniMarshal_PPILL_V (Unsafe.As<_JniMarshal_PPILL_V> (dlg).Wrap_JniMarshal_PPILL_V); - case nameof (_JniMarshal_PPLIL_Z): - return new _JniMarshal_PPLIL_Z (Unsafe.As<_JniMarshal_PPLIL_Z> (dlg).Wrap_JniMarshal_PPLIL_Z); - case nameof (_JniMarshal_PPLLL_V): - return new _JniMarshal_PPLLL_V (Unsafe.As<_JniMarshal_PPLLL_V> (dlg).Wrap_JniMarshal_PPLLL_V); - case nameof (_JniMarshal_PPLLL_L): - return new _JniMarshal_PPLLL_L (Unsafe.As<_JniMarshal_PPLLL_L> (dlg).Wrap_JniMarshal_PPLLL_L); - case nameof (_JniMarshal_PPLLL_Z): - return new _JniMarshal_PPLLL_Z (Unsafe.As<_JniMarshal_PPLLL_Z> (dlg).Wrap_JniMarshal_PPLLL_Z); - case nameof (_JniMarshal_PPIZI_L): - return new _JniMarshal_PPIZI_L (Unsafe.As<_JniMarshal_PPIZI_L> (dlg).Wrap_JniMarshal_PPIZI_L); - case nameof (_JniMarshal_PPIIII_V): - return new _JniMarshal_PPIIII_V (Unsafe.As<_JniMarshal_PPIIII_V> (dlg).Wrap_JniMarshal_PPIIII_V); - case nameof (_JniMarshal_PPLLLL_V): - return new _JniMarshal_PPLLLL_V (Unsafe.As<_JniMarshal_PPLLLL_V> (dlg).Wrap_JniMarshal_PPLLLL_V); - case nameof (_JniMarshal_PPLZZL_Z): - return new _JniMarshal_PPLZZL_Z (Unsafe.As<_JniMarshal_PPLZZL_Z> (dlg).Wrap_JniMarshal_PPLZZL_Z); - case nameof (_JniMarshal_PPLIIII_V): - return new _JniMarshal_PPLIIII_V (Unsafe.As<_JniMarshal_PPLIIII_V> (dlg).Wrap_JniMarshal_PPLIIII_V); - case nameof (_JniMarshal_PPZIIII_V): - return new _JniMarshal_PPZIIII_V (Unsafe.As<_JniMarshal_PPZIIII_V> (dlg).Wrap_JniMarshal_PPZIIII_V); - case nameof (_JniMarshal_PPLIIIIIIII_V): - return new _JniMarshal_PPLIIIIIIII_V (Unsafe.As<_JniMarshal_PPLIIIIIIII_V> (dlg).Wrap_JniMarshal_PPLIIIIIIII_V); + switch (dlg) { + case _JniMarshal_PP_V callback: + return new _JniMarshal_PP_V (callback.Wrap_JniMarshal_PP_V); + case _JniMarshal_PP_I callback: + return new _JniMarshal_PP_I (callback.Wrap_JniMarshal_PP_I); + case _JniMarshal_PP_Z callback: + return new _JniMarshal_PP_Z (callback.Wrap_JniMarshal_PP_Z); + case _JniMarshal_PPI_V callback: + return new _JniMarshal_PPI_V (callback.Wrap_JniMarshal_PPI_V); + case _JniMarshal_PPI_L callback: + return new _JniMarshal_PPI_L (callback.Wrap_JniMarshal_PPI_L); + case _JniMarshal_PPI_I callback: + return new _JniMarshal_PPI_I (callback.Wrap_JniMarshal_PPI_I); + case _JniMarshal_PPI_J callback: + return new _JniMarshal_PPI_J (callback.Wrap_JniMarshal_PPI_J); + case _JniMarshal_PPL_I callback: + return new _JniMarshal_PPL_I (callback.Wrap_JniMarshal_PPL_I); + case _JniMarshal_PPL_L callback: + return new _JniMarshal_PPL_L (callback.Wrap_JniMarshal_PPL_L); + case _JniMarshal_PPL_V callback: + return new _JniMarshal_PPL_V (callback.Wrap_JniMarshal_PPL_V); + case _JniMarshal_PPL_Z callback: + return new _JniMarshal_PPL_Z (callback.Wrap_JniMarshal_PPL_Z); + case _JniMarshal_PPJ_Z callback: + return new _JniMarshal_PPJ_Z (callback.Wrap_JniMarshal_PPJ_Z); + case _JniMarshal_PPII_V callback: + return new _JniMarshal_PPII_V (callback.Wrap_JniMarshal_PPII_V); + case _JniMarshal_PPII_L callback: + return new _JniMarshal_PPII_L (callback.Wrap_JniMarshal_PPII_L); + case _JniMarshal_PPLI_V callback: + return new _JniMarshal_PPLI_V (callback.Wrap_JniMarshal_PPLI_V); + case _JniMarshal_PPLZ_V callback: + return new _JniMarshal_PPLZ_V (callback.Wrap_JniMarshal_PPLZ_V); + case _JniMarshal_PPLL_V callback: + return new _JniMarshal_PPLL_V (callback.Wrap_JniMarshal_PPLL_V); + case _JniMarshal_PPLF_V callback: + return new _JniMarshal_PPLF_V (callback.Wrap_JniMarshal_PPLF_V); + case _JniMarshal_PPLI_L callback: + return new _JniMarshal_PPLI_L (callback.Wrap_JniMarshal_PPLI_L); + case _JniMarshal_PPLL_L callback: + return new _JniMarshal_PPLL_L (callback.Wrap_JniMarshal_PPLL_L); + case _JniMarshal_PPLL_Z callback: + return new _JniMarshal_PPLL_Z (callback.Wrap_JniMarshal_PPLL_Z); + case _JniMarshal_PPIL_Z callback: + return new _JniMarshal_PPIL_Z (callback.Wrap_JniMarshal_PPIL_Z); + case _JniMarshal_PPIIL_V callback: + return new _JniMarshal_PPIIL_V (callback.Wrap_JniMarshal_PPIIL_V); + case _JniMarshal_PPLII_I callback: + return new _JniMarshal_PPLII_I (callback.Wrap_JniMarshal_PPLII_I); + case _JniMarshal_PPLII_Z callback: + return new _JniMarshal_PPLII_Z (callback.Wrap_JniMarshal_PPLII_Z); + case _JniMarshal_PPLII_V callback: + return new _JniMarshal_PPLII_V (callback.Wrap_JniMarshal_PPLII_V); + case _JniMarshal_PPIII_V callback: + return new _JniMarshal_PPIII_V (callback.Wrap_JniMarshal_PPIII_V); + case _JniMarshal_PPLLJ_Z callback: + return new _JniMarshal_PPLLJ_Z (callback.Wrap_JniMarshal_PPLLJ_Z); + case _JniMarshal_PPILL_V callback: + return new _JniMarshal_PPILL_V (callback.Wrap_JniMarshal_PPILL_V); + case _JniMarshal_PPLIL_Z callback: + return new _JniMarshal_PPLIL_Z (callback.Wrap_JniMarshal_PPLIL_Z); + case _JniMarshal_PPLLL_V callback: + return new _JniMarshal_PPLLL_V (callback.Wrap_JniMarshal_PPLLL_V); + case _JniMarshal_PPLLL_L callback: + return new _JniMarshal_PPLLL_L (callback.Wrap_JniMarshal_PPLLL_L); + case _JniMarshal_PPLLL_Z callback: + return new _JniMarshal_PPLLL_Z (callback.Wrap_JniMarshal_PPLLL_Z); + case _JniMarshal_PPIZI_L callback: + return new _JniMarshal_PPIZI_L (callback.Wrap_JniMarshal_PPIZI_L); + case _JniMarshal_PPIIII_V callback: + return new _JniMarshal_PPIIII_V (callback.Wrap_JniMarshal_PPIIII_V); + case _JniMarshal_PPLLLL_V callback: + return new _JniMarshal_PPLLLL_V (callback.Wrap_JniMarshal_PPLLLL_V); + case _JniMarshal_PPLZZL_Z callback: + return new _JniMarshal_PPLZZL_Z (callback.Wrap_JniMarshal_PPLZZL_Z); + case _JniMarshal_PPLIIII_V callback: + return new _JniMarshal_PPLIIII_V (callback.Wrap_JniMarshal_PPLIIII_V); + case _JniMarshal_PPZIIII_V callback: + return new _JniMarshal_PPZIIII_V (callback.Wrap_JniMarshal_PPZIIII_V); + case _JniMarshal_PPLIIIIIIII_V callback: + return new _JniMarshal_PPLIIIIIIII_V (callback.Wrap_JniMarshal_PPLIIIIIIII_V); default: return null; } diff --git a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt index 425dd99bcdb..a7cb0c01a1a 100644 --- a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt +++ b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt @@ -248,7 +248,6 @@ var delegateTypes = new [] { #> using System; using System.Diagnostics; -using System.Runtime.CompilerServices; namespace Android.Runtime { @@ -282,12 +281,12 @@ foreach (var info in delegateTypes) { #> private static Delegate CreateBuiltInDelegate (Delegate dlg, Type delegateType) { - switch (delegateType.Name) { + switch (dlg) { <# foreach (var info in delegateTypes) { #> - case nameof (<#= info.Type #>): - return new <#= info.Type #> (Unsafe.As<<#= info.Type #>> (dlg).Wrap<#= info.Type #>); + case <#= info.Type #> callback: + return new <#= info.Type #> (callback.Wrap<#= info.Type #>); <# } #> diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs index c1764cc73c9..d0ce9e9787d 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs @@ -19,6 +19,17 @@ namespace Android.RuntimeTests { [TestFixture] public class JnienvArrayMarshaling { + [Test] + [Category ("NativeAOTIgnore")] + public void CreateDelegate_PreservesCustomDelegateWithBuiltInName () + { + static int Callback (IntPtr jnienv, IntPtr klazz) => jnienv.ToInt32 () + klazz.ToInt32 (); + + var callback = new _JniMarshal_PP_I (Callback); + var wrapped = (_JniMarshal_PP_I) JNINativeWrapper.CreateDelegate (callback); + Assert.AreEqual (42, wrapped (new IntPtr (19), new IntPtr (23))); + } + [Test] public void MarshalInt32ArrayArray () { @@ -725,5 +736,7 @@ static void AssertArrays (string message, IList actual, params T[] expecte for (int i = 0; i < expected.Length; ++i) Assert.AreEqual (expected [i], actual [i], message); } + + delegate int _JniMarshal_PP_I (IntPtr jnienv, IntPtr klazz); } } From e75a1c385b6d3dca7d76ecf8475342883c8fed66 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 9 Oct 2026 08:52:50 +0200 Subject: [PATCH 2/3] [tests] Remove delegate dispatch regression test Remove the new CreateDelegate regression test as requested. Its call to CreateDelegate also triggers IL3050 during NativeAOT compilation, even though the test is excluded from NativeAOT execution. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Android.Runtime/JnienvArrayMarshaling.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs index d0ce9e9787d..c1764cc73c9 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs @@ -19,17 +19,6 @@ namespace Android.RuntimeTests { [TestFixture] public class JnienvArrayMarshaling { - [Test] - [Category ("NativeAOTIgnore")] - public void CreateDelegate_PreservesCustomDelegateWithBuiltInName () - { - static int Callback (IntPtr jnienv, IntPtr klazz) => jnienv.ToInt32 () + klazz.ToInt32 (); - - var callback = new _JniMarshal_PP_I (Callback); - var wrapped = (_JniMarshal_PP_I) JNINativeWrapper.CreateDelegate (callback); - Assert.AreEqual (42, wrapped (new IntPtr (19), new IntPtr (23))); - } - [Test] public void MarshalInt32ArrayArray () { @@ -736,7 +725,5 @@ static void AssertArrays (string message, IList actual, params T[] expecte for (int i = 0; i < expected.Length; ++i) Assert.AreEqual (expected [i], actual [i], message); } - - delegate int _JniMarshal_PP_I (IntPtr jnienv, IntPtr klazz); } } From 434386d872f0751ced1138de9d486b613f8ed06e Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 9 Oct 2026 09:07:53 +0200 Subject: [PATCH 3/3] Remove unused argument --- src/Mono.Android/Android.Runtime/JNINativeWrapper.cs | 5 ++--- src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs | 2 +- src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs b/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs index 47e360f99ae..09f71f3346e 100644 --- a/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs +++ b/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs @@ -36,13 +36,12 @@ public static Delegate CreateDelegate (Delegate dlg) if (dlg.Method == null) throw new ArgumentException (); - var delegateType = dlg.GetType (); - var result = CreateBuiltInDelegate (dlg, delegateType); + var result = CreateBuiltInDelegate (dlg); if (result != null) return result; if (Logger.LogAssembly) { - RuntimeNativeMethods.monodroid_log (LogLevel.Debug, LogCategories.Assembly, $"Falling back to System.Reflection.Emit for delegate type '{delegateType}': {dlg.Method}"); + RuntimeNativeMethods.monodroid_log (LogLevel.Debug, LogCategories.Assembly, $"Falling back to System.Reflection.Emit for delegate type '{dlg.GetType ()}': {dlg.Method}"); } get_runtime_types (); diff --git a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs index 28609920b38..6ad36fd9c5c 100644 --- a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs +++ b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs @@ -454,7 +454,7 @@ internal static void Wrap_JniMarshal_PPLIIIIIIII_V (this _JniMarshal_PPLIIIIIIII } } - private static Delegate CreateBuiltInDelegate (Delegate dlg, Type delegateType) + private static Delegate CreateBuiltInDelegate (Delegate dlg) { switch (dlg) { case _JniMarshal_PP_V callback: diff --git a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt index a7cb0c01a1a..9edd2addc56 100644 --- a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt +++ b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt @@ -279,7 +279,7 @@ foreach (var info in delegateTypes) { <# } #> - private static Delegate CreateBuiltInDelegate (Delegate dlg, Type delegateType) + private static Delegate CreateBuiltInDelegate (Delegate dlg) { switch (dlg) { <#