diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index cb05c9da9b1..34199244c40 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -397,25 +397,6 @@ protected override IEnumerable GetSimpleReferences (Type type) delegate Delegate GetCallbackHandler (); - static MethodInfo? dynamic_callback_gen; - - // See ExportAttribute.cs - static Delegate CreateDynamicCallback (MethodInfo method) - { - if (dynamic_callback_gen == null) { - var assembly = Assembly.Load ("Mono.Android.Export"); - if (assembly == null) - throw new InvalidOperationException ("To use methods marked with ExportAttribute, Mono.Android.Export.dll needs to be referenced in the application"); - var type = assembly.GetType ("Java.Interop.DynamicCallbackCodeGenerator"); - if (type == null) - throw new InvalidOperationException ("The referenced Mono.Android.Export.dll does not match the expected version. The required type was not found."); - dynamic_callback_gen = type.GetMethod ("Create"); - if (dynamic_callback_gen == null) - throw new InvalidOperationException ("The referenced Mono.Android.Export.dll does not match the expected version. The required method was not found."); - } - return (Delegate)dynamic_callback_gen.Invoke (null, new object [] { method })!; - } - // [Export] callback delegates are created dynamically via DynamicCallbackCodeGenerator and are not // cached in static fields (unlike non-[Export] connector delegates). Without rooting them here, // CoreCLR's GC can collect them between JNI registration and first invocation, causing a crash. @@ -547,7 +528,11 @@ public override void RegisterNativeMembers (JniType nativeClass, Type type, Read if (minfo == null) throw new InvalidOperationException (FormattableString.Invariant ($"Specified managed method '{mname.ToString ()}' was not found. Signature: {signature.ToString ()}")); - callback = CreateDynamicCallback (minfo); + + var exportAttribute = minfo.GetCustomAttribute () + ?? throw new InvalidOperationException (FormattableString.Invariant ($"Specified managed method '{mname.ToString ()}' does not have [Export] attribute. Signature: {signature.ToString ()}")); + + callback = exportAttribute.CreateDynamicCallback (minfo); lock (prevent_delegate_gc_lock) { prevent_delegate_gc.Add (callback); } diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index cefbc6c694e..a30ba3626ba 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -167,6 +167,7 @@ IntPtr GetRegisterJniNativesFnPtr () => [UnmanagedCallConv (CallConvs = new[] { typeof (CallConvCdecl) })] private static unsafe partial void xamarin_app_init (IntPtr env, delegate* unmanaged get_function_pointer); + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "The AndroidTypeManager branch is only reached when RuntimeFeature.TrimmableTypeMap is false; the linker substitutes the feature switch and trims this branch in trimmable apps.")] internal static JniRuntime.JniTypeManager CreateTypeManager (JnienvInitializeArgs args) { if (RuntimeFeature.TrimmableTypeMap) { diff --git a/src/Mono.Android/PublicAPI/API-35/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-35/PublicAPI.Unshipped.txt index 75505207238..867f297909b 100644 --- a/src/Mono.Android/PublicAPI/API-35/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-35/PublicAPI.Unshipped.txt @@ -25,6 +25,7 @@ REMOVED virtual Xamarin.Android.Net.AndroidClientHandler.SetupRequest(System.Net REMOVED virtual Xamarin.Android.Net.AndroidClientHandler.WriteRequestContentToOutput(System.Net.Http.HttpRequestMessage! request, Java.Net.HttpURLConnection! httpConnection, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.get -> bool Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.set -> void +Java.Interop.BaseExportAttribute Java.Interop.JavaArrayProxy abstract Java.Interop.JavaArrayProxy.CreateManagedArray(int length) -> System.Array! abstract Java.Interop.JavaArrayProxy.GetArrayTypes() -> System.Type![]! diff --git a/src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Unshipped.txt index 75505207238..867f297909b 100644 --- a/src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Unshipped.txt @@ -25,6 +25,7 @@ REMOVED virtual Xamarin.Android.Net.AndroidClientHandler.SetupRequest(System.Net REMOVED virtual Xamarin.Android.Net.AndroidClientHandler.WriteRequestContentToOutput(System.Net.Http.HttpRequestMessage! request, Java.Net.HttpURLConnection! httpConnection, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.get -> bool Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.set -> void +Java.Interop.BaseExportAttribute Java.Interop.JavaArrayProxy abstract Java.Interop.JavaArrayProxy.CreateManagedArray(int length) -> System.Array! abstract Java.Interop.JavaArrayProxy.GetArrayTypes() -> System.Type![]! diff --git a/src/Mono.Android/PublicAPI/API-36/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-36/PublicAPI.Unshipped.txt index 4def2ad9232..45e15c9a177 100644 --- a/src/Mono.Android/PublicAPI/API-36/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-36/PublicAPI.Unshipped.txt @@ -4401,6 +4401,7 @@ virtual Java.Util.IdentityHashMap.Remove(Java.Lang.Object? key, Java.Lang.Object virtual Java.Util.IdentityHashMap.Replace(Java.Lang.Object? key, Java.Lang.Object? oldValue, Java.Lang.Object? newValue) -> bool Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.get -> bool Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.set -> void +Java.Interop.BaseExportAttribute Java.Interop.JavaArrayProxy abstract Java.Interop.JavaArrayProxy.CreateManagedArray(int length) -> System.Array! abstract Java.Interop.JavaArrayProxy.GetArrayTypes() -> System.Type![]! diff --git a/src/Mono.Android/PublicAPI/API-37/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-37/PublicAPI.Unshipped.txt index 75505207238..867f297909b 100644 --- a/src/Mono.Android/PublicAPI/API-37/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-37/PublicAPI.Unshipped.txt @@ -25,6 +25,7 @@ REMOVED virtual Xamarin.Android.Net.AndroidClientHandler.SetupRequest(System.Net REMOVED virtual Xamarin.Android.Net.AndroidClientHandler.WriteRequestContentToOutput(System.Net.Http.HttpRequestMessage! request, Java.Net.HttpURLConnection! httpConnection, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.get -> bool Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.set -> void +Java.Interop.BaseExportAttribute Java.Interop.JavaArrayProxy abstract Java.Interop.JavaArrayProxy.CreateManagedArray(int length) -> System.Array! abstract Java.Interop.JavaArrayProxy.GetArrayTypes() -> System.Type![]! diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc index 6ad0abc158a..065272635fa 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc @@ -11,13 +11,13 @@ "Size": 2717688 }, "lib/arm64-v8a/libclrjit.so": { - "Size": 2836936 + "Size": 2835128 }, "lib/arm64-v8a/libcoreclr.so": { - "Size": 4890752 + "Size": 4891056 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1264112 + "Size": 1324320 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72112 diff --git a/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/BaseExportAttribute.cs b/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/BaseExportAttribute.cs new file mode 100644 index 00000000000..1e0d164a788 --- /dev/null +++ b/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/BaseExportAttribute.cs @@ -0,0 +1,26 @@ +using System; +using System.Reflection; + +namespace Java.Interop; + +#if !JCW_ONLY_TYPE_NAMES +public +#endif // !JCW_ONLY_TYPE_NAMES +abstract class BaseExportAttribute : Attribute +{ + internal abstract Delegate CreateDynamicCallback (MethodInfo method); + + static MethodInfo? dynamic_callback_gen; + + private protected static Delegate CreateDynamicCallbackCore (MethodInfo method) + { + // We're loading the Mono.Android.Export assembly dynamically to avoid problems with circular dependencies. + // The Type.GetType (...)?.GetMethod (...) call is chained intentionally: the trimmer only recognizes the + // GetMethod intrinsic when the constant Type value flows directly into it, so routing it through a local breaks dataflow. + dynamic_callback_gen ??= Type.GetType ("Java.Interop.DynamicCallbackCodeGenerator, Mono.Android.Export")?.GetMethod ("Create") + ?? throw new InvalidOperationException ("To use methods marked with [Export] or [ExportField], Mono.Android.Export.dll must be referenced by the application and contain a matching Java.Interop.DynamicCallbackCodeGenerator.Create method."); + + return (Delegate?)dynamic_callback_gen.Invoke (null, [method]) + ?? throw new InvalidOperationException (FormattableString.Invariant ($"Unable to create dynamic callback for method '{method.Name}' on type '{method.DeclaringType?.FullName}'")); + } +} diff --git a/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/ExportAttribute.cs b/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/ExportAttribute.cs index 1761bf4466e..8618d5a058f 100644 --- a/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/ExportAttribute.cs +++ b/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/ExportAttribute.cs @@ -1,5 +1,5 @@ using System; -using System.Diagnostics.CodeAnalysis; +using System.Reflection; namespace Java.Interop { @@ -7,20 +7,15 @@ namespace Java.Interop { [AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple=false, Inherited=false)] -#if !NETSTANDARD2_0 - [RequiresUnreferencedCode ("[ExportAttribute] uses dynamic features.")] -#endif #if !JCW_ONLY_TYPE_NAMES public #endif // !JCW_ONLY_TYPE_NAMES - partial class ExportAttribute : Attribute { + partial class ExportAttribute : BaseExportAttribute { - [DynamicDependency (DynamicallyAccessedMemberTypes.All, "Java.Interop.DynamicCallbackCodeGenerator", "Mono.Android.Export")] public ExportAttribute () { } - [DynamicDependency (DynamicallyAccessedMemberTypes.All, "Java.Interop.DynamicCallbackCodeGenerator", "Mono.Android.Export")] public ExportAttribute (string name) { Name = name; @@ -30,6 +25,11 @@ public ExportAttribute (string name) public string? SuperArgumentsString {get; set;} public Type []? Throws {get; set;} internal string []? ThrownNames {get; set;} // msbuild internal use + + internal override Delegate CreateDynamicCallback (MethodInfo method) + { + return CreateDynamicCallbackCore (method); + } } } diff --git a/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/ExportFieldAttribute.cs b/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/ExportFieldAttribute.cs index 888636f60f0..36f1ad25188 100644 --- a/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/ExportFieldAttribute.cs +++ b/src/Xamarin.Android.NamingCustomAttributes/Java.Interop/ExportFieldAttribute.cs @@ -1,5 +1,5 @@ using System; -using System.Diagnostics.CodeAnalysis; +using System.Reflection; namespace Java.Interop { @@ -10,16 +10,18 @@ namespace Java.Interop { #if !JCW_ONLY_TYPE_NAMES public #endif // !JCW_ONLY_TYPE_NAMES - partial class ExportFieldAttribute : Attribute { + partial class ExportFieldAttribute : BaseExportAttribute { - [DynamicDependency (DynamicallyAccessedMemberTypes.All, "Java.Interop.DynamicCallbackCodeGenerator", "Mono.Android.Export")] public ExportFieldAttribute (string name) { Name = name; } public string Name {get; set;} + + internal override Delegate CreateDynamicCallback (MethodInfo method) + { + return CreateDynamicCallbackCore (method); + } } } - - diff --git a/src/Xamarin.Android.NamingCustomAttributes/Xamarin.Android.NamingCustomAttributes.projitems b/src/Xamarin.Android.NamingCustomAttributes/Xamarin.Android.NamingCustomAttributes.projitems index ccfa68d876d..3deec04673e 100644 --- a/src/Xamarin.Android.NamingCustomAttributes/Xamarin.Android.NamingCustomAttributes.projitems +++ b/src/Xamarin.Android.NamingCustomAttributes/Xamarin.Android.NamingCustomAttributes.projitems @@ -14,6 +14,7 @@ + diff --git a/tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt b/tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt index 05022f14024..e9145ee011b 100644 --- a/tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt +++ b/tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt @@ -47,4 +47,5 @@ MembersMustExist : Member 'public void Android.Telecom.CallControl.RequestVideoS TypesMustExist : Type 'Android.Text.IInputType' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Xamarin.Android.Net.AndroidClientHandler' does not exist in the implementation but it does exist in the contract. CannotRemoveBaseTypeOrInterface : Type 'Android.Views.InputMethods.EditorInfo' does not implement interface 'Android.Text.IInputType' in the implementation but it does in the contract. +CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'Java.Interop.ExportAttribute' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'targetType' on member 'Android.Graphics.ColorValueMarshaler.CreateGenericValue(Java.Interop.JniObjectReference, Java.Interop.JniObjectReferenceOptions, System.Type)' in the contract but not the implementation.