diff --git a/Documentation/docs-mobile/building-apps/build-properties.md b/Documentation/docs-mobile/building-apps/build-properties.md index 013745c6ecc..b05b9e55500 100644 --- a/Documentation/docs-mobile/building-apps/build-properties.md +++ b/Documentation/docs-mobile/building-apps/build-properties.md @@ -273,8 +273,7 @@ Possible values include: Xamarin.Android 6.1 or later. Xamarin.Android 6.1 and later bind `Mono.Android.dll` with this value. -- **JavaInterop1**: experimental value, currently the default for the - NativeAOT runtime. +- **JavaInterop1**: this value is obsolete and is no longer supported. The default value is `XAJavaInterop1`. diff --git a/Documentation/docs-mobile/messages/index.md b/Documentation/docs-mobile/messages/index.md index 96354520d09..161af91779f 100644 --- a/Documentation/docs-mobile/messages/index.md +++ b/Documentation/docs-mobile/messages/index.md @@ -203,7 +203,7 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla + XA4229: Unrecognized \`TransformFile\` root element: {element}. + XA4230: Error parsing XML: {exception} + [XA4231](xa4231.md): The Android class parser value 'jar2xml' is deprecated and will be removed in a future version of .NET for Android. Update the project properties to use 'class-parse'. -+ [XA4232](xa4232.md): The Android code generation target 'XamarinAndroid' is deprecated and will be removed in a future version of .NET for Android. Update the project properties to use 'XAJavaInterop1'. ++ [XA4232](xa4232.md): The Android code generation target '{0}' is no longer supported. The '_AndroidJcwCodegenTarget' property only supports 'XAJavaInterop1'; 'XamarinAndroid' and 'JavaInterop1' are not supported. + [XA4234](xa4234.md): '<{item}>' item '{itemspec}' is missing required attribute '{name}'. + [XA4235](xa4235.md): Maven artifact specification '{artifact}' is invalid. The correct format is 'group_id:artifact_id'. + [XA4236](xa4236.md): Cannot download Maven artifact '{group}:{artifact}'. - {jar}: {exception} - {aar}: {exception} diff --git a/Documentation/docs-mobile/messages/xa4232.md b/Documentation/docs-mobile/messages/xa4232.md index d5563af89d7..f257169f08f 100644 --- a/Documentation/docs-mobile/messages/xa4232.md +++ b/Documentation/docs-mobile/messages/xa4232.md @@ -1,33 +1,34 @@ --- -title: .NET for Android warning XA4232 -description: XA4232 warning code -ms.date: 04/11/2024 +title: .NET for Android error XA4232 +description: XA4232 error code +ms.date: 07/17/2026 f1_keywords: - "XA4232" --- -# .NET for Android warning XA4232 +# .NET for Android error XA4232 ## Example messages ``` -warning XA4232: The Android code generation target 'XamarinAndroid' is deprecated and will be removed in a future version of .NET for Android. Update the project properties to use 'XAJavaInterop1'. +error XA4232: The Android code generation target 'XamarinAndroid' is no longer supported. The '_AndroidJcwCodegenTarget' property only supports 'XAJavaInterop1'; 'XamarinAndroid' and 'JavaInterop1' are not supported. +error XA4232: The Android code generation target 'JavaInterop1' is no longer supported. The '_AndroidJcwCodegenTarget' property only supports 'XAJavaInterop1'; 'XamarinAndroid' and 'JavaInterop1' are not supported. ``` ## Issue -The Android code generation target `XamarinAndroid` has been deprecated. +The project sets the internal `_AndroidJcwCodegenTarget` MSBuild property to +`XamarinAndroid` or `JavaInterop1`. These code generation targets are no longer +supported for any .NET for Android runtime. ## Solution -To resolve this warning, update the **Android Codegen target** setting in the -Visual Studio project property pages or the **Code generation target** setting -in Visual Studio for Mac to **XAJavaInterop1**. This corresponds to the -`XAJavaInterop1` value for the `AndroidCodegenTarget` MSBuild property in the -_.csproj_ file: +Remove any `_AndroidJcwCodegenTarget` assignment that uses `XamarinAndroid` or +`JavaInterop1`, for example: ```xml - - XAJavaInterop1 - +<_AndroidJcwCodegenTarget>JavaInterop1 ``` + +The property defaults to `XAJavaInterop1`. If a build customization must set +it explicitly, update the value to `XAJavaInterop1`. diff --git a/Documentation/guides/OneDotNet.md b/Documentation/guides/OneDotNet.md index 142901020c4..a032f094414 100644 --- a/Documentation/guides/OneDotNet.md +++ b/Documentation/guides/OneDotNet.md @@ -88,7 +88,7 @@ will not be supported. supported. `$(AndroidCodegenTarget)` will be `XAJavaInterop1` by default. -`XamarinAndroid` will not be supported. +`XamarinAndroid` and `JavaInterop1` will not be supported. `$(AndroidManifest)` will default to `AndroidManifest.xml` in the root of projects as `Properties\AssemblyInfo.cs` is no longer used in diff --git a/Documentation/guides/OneDotNetBindingProjects.md b/Documentation/guides/OneDotNetBindingProjects.md index 9e0829c1bb9..caf9db20463 100644 --- a/Documentation/guides/OneDotNetBindingProjects.md +++ b/Documentation/guides/OneDotNetBindingProjects.md @@ -43,8 +43,9 @@ will always be used. ### `` -`XamarinAndroid` is no longer a valid option for ``. `XAJavaInterop1` -is now the default and only supported option. +`XamarinAndroid` and `JavaInterop1` are no longer valid options for +``. `XAJavaInterop1` is now the default and only +supported option. If you have hand-bound code in your `Additions` files that interacts with the generated binding plumbing (which is rare), it may need to be updated to be compatible with `XAJavaInterop1`. diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index b7a35e2e08d..d164c21d7ec 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -37,7 +37,7 @@ class-parse XAJavaInterop1 - <_AndroidJcwCodegenTarget Condition=" '$(_AndroidJcwCodegenTarget)' == '' and '$(_AndroidRuntime)' != 'NativeAOT' ">XAJavaInterop1 + <_AndroidJcwCodegenTarget Condition=" '$(_AndroidJcwCodegenTarget)' == '' ">XAJavaInterop1 <_AndroidTypeMapImplementation Condition=" '$(_AndroidTypeMapImplementation)' == '' and '$(_AndroidRuntime)' != 'NativeAOT' ">llvm-ir true true diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 7511835ba25..26a25c407a1 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -16,7 +16,6 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_AndroidRuntimePackRuntime>NativeAOT <_AndroidUseWorkloadNativeLinker Condition=" '$(_AndroidUseWorkloadNativeLinker)' == '' ">true - <_AndroidJcwCodegenTarget Condition=" '$(_AndroidJcwCodegenTarget)' == '' ">JavaInterop1 <_AndroidTypeMapImplementation Condition=" '$(_AndroidTypeMapImplementation)' == '' ">trimmable true diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs index 0d77d814434..3b4201e1b58 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs @@ -1405,7 +1405,7 @@ public static string XA4231 { } /// - /// Looks up a localized string similar to The Android code generation target '{0}' is deprecated and will be removed in a future version of .NET for Android. Update the project properties to use 'XAJavaInterop1'.. + /// Looks up a localized string similar to The Android code generation target '{0}' is no longer supported. The '_AndroidJcwCodegenTarget' property only supports 'XAJavaInterop1'; 'XamarinAndroid' and 'JavaInterop1' are not supported.. /// public static string XA4232 { get { diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx index 3acea7e9bd5..839d4630dcc 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx @@ -725,8 +725,8 @@ In this message, "root element" refers to the root element of an XML file. {0} - The name of the current class parser value - The Android code generation target '{0}' is deprecated and will be removed in a future version of .NET for Android. Update the project properties to use 'XAJavaInterop1'. - The following are literal names and should not be translated: XAJavaInterop1 + The Android code generation target '{0}' is no longer supported. The '_AndroidJcwCodegenTarget' property only supports 'XAJavaInterop1'; 'XamarinAndroid' and 'JavaInterop1' are not supported. + The following are literal names and should not be translated: _AndroidJcwCodegenTarget, XAJavaInterop1, XamarinAndroid, JavaInterop1 {0} - The name of the current code generation target diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/InvalidConfigTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/InvalidConfigTests.cs index 2ba7c3f8863..a2c2989ab89 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/InvalidConfigTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/InvalidConfigTests.cs @@ -44,5 +44,27 @@ public void XA0119AAB () } } + [Test] + public void UnsupportedJcwCodegenTargetIsRejected ( + [Values ("XamarinAndroid", "JavaInterop1")] string codegenTarget, + [Values (AndroidRuntime.MonoVM, AndroidRuntime.CoreCLR, AndroidRuntime.NativeAOT)] AndroidRuntime runtime) + { + var project = new XamarinAndroidApplicationProject { + IsRelease = runtime == AndroidRuntime.NativeAOT, + }; + if (runtime == AndroidRuntime.MonoVM) { + project.SetProperty ("_DisableCheckForUnsupportedMonoMobileRuntime", "true"); + } + project.SetRuntime (runtime); + project.SetProperty ("_AndroidJcwCodegenTarget", codegenTarget); + using (var builder = CreateApkBuilder ()) { + builder.Target = "_CheckForInvalidConfigurationAndPlatform"; + builder.ThrowOnBuildFailure = false; + Assert.IsFalse (builder.Build (project), "Build should have failed."); + StringAssertEx.Contains ("error XA4232:", builder.LastBuildOutput, "Build should fail with XA4232."); + StringAssertEx.Contains (codegenTarget, builder.LastBuildOutput, "Error should identify the unsupported code generation target."); + } + } + } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc index dcdca9358c9..6f147838d41 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc @@ -5,10 +5,10 @@ "Size": 3124 }, "classes.dex": { - "Size": 22804 + "Size": 404664 }, "lib/arm64-v8a/libUnnamedProject.so": { - "Size": 5747648 + "Size": 5731688 }, "res/drawable-hdpi-v4/icon.png": { "Size": 2178 @@ -35,5 +35,5 @@ "Size": 1904 } }, - "PackageSize": 2401051 + "PackageSize": 2474779 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 220abcdaeb8..5159244a4d3 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -3003,6 +3003,14 @@ because xbuild doesn't support framework reference assemblies. + + + + Components [scc_index]; @@ -142,7 +142,7 @@ CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size } // caller must ensure that scc.Count > 1 -void BridgeProcessingShared::add_circular_references (const StronglyConnectedComponent &scc) noexcept +void BridgeProcessing::add_circular_references (const StronglyConnectedComponent &scc) noexcept { auto get_control_block = [&scc](size_t index) -> JniObjectReferenceControlBlock& { abort_unless (scc.Contexts [index] != nullptr, "Context in SCC must not be null"); @@ -176,7 +176,7 @@ void BridgeProcessingShared::add_circular_references (const StronglyConnectedCom } } -void BridgeProcessingShared::add_cross_reference (size_t source_index, size_t dest_index) noexcept +void BridgeProcessing::add_cross_reference (size_t source_index, size_t dest_index) noexcept { CrossReferenceTarget from = select_cross_reference_target (source_index); CrossReferenceTarget to = select_cross_reference_target (dest_index); @@ -186,15 +186,11 @@ void BridgeProcessingShared::add_cross_reference (size_t source_index, size_t de } } -bool BridgeProcessingShared::add_reference (jobject from, jobject to) noexcept +bool BridgeProcessing::add_reference (jobject from, jobject to) noexcept { abort_if_invalid_pointer_argument (from, "from"); abort_if_invalid_pointer_argument (to, "to"); - if (maybe_call_gc_user_peerable_add_managed_reference (env, from, to)) { - return true; - } - if (!env->IsInstanceOf (from, IGCUserPeer_class)) [[unlikely]] { jclass java_class = env->GetObjectClass (from); log_missing_add_references_method (java_class); @@ -207,7 +203,7 @@ bool BridgeProcessingShared::add_reference (jobject from, jobject to) noexcept return true; } -void BridgeProcessingShared::clear_references_if_needed (const HandleContext &context) noexcept +void BridgeProcessing::clear_references_if_needed (const HandleContext &context) noexcept { if (context.is_collected ()) { return; @@ -227,14 +223,10 @@ void BridgeProcessingShared::clear_references_if_needed (const HandleContext &co control_block->refs_added = 0; } -void BridgeProcessingShared::clear_references (jobject handle) noexcept +void BridgeProcessing::clear_references (jobject handle) noexcept { abort_if_invalid_pointer_argument (handle, "handle"); - if (maybe_call_gc_user_peerable_clear_managed_references (env, handle)) { - return; - } - if (!env->IsInstanceOf (handle, IGCUserPeer_class)) [[unlikely]] { jclass java_class = env->GetObjectClass (handle); log_missing_clear_references_method (java_class); @@ -246,7 +238,7 @@ void BridgeProcessingShared::clear_references (jobject handle) noexcept abort_on_pending_java_exception ("A Java exception was thrown by monodroidClearReferences during GC bridge processing"sv); } -void BridgeProcessingShared::abort_on_pending_java_exception (std::string_view message) noexcept +void BridgeProcessing::abort_on_pending_java_exception (std::string_view message) noexcept { if (!env->ExceptionCheck ()) [[likely]] { return; @@ -257,7 +249,7 @@ void BridgeProcessingShared::abort_on_pending_java_exception (std::string_view m Helpers::abort_application (LOG_GC, message); } -void BridgeProcessingShared::take_global_ref (HandleContext &context) noexcept +void BridgeProcessing::take_global_ref (HandleContext &context) noexcept { abort_unless (context.control_block != nullptr, "Control block must not be null"); abort_unless (context.control_block->handle_type == JNIWeakGlobalRefType, "Expected weak global reference type for handle"); @@ -283,7 +275,7 @@ void BridgeProcessingShared::take_global_ref (HandleContext &context) noexcept env->DeleteWeakGlobalRef (weak); } -void BridgeProcessingShared::take_weak_global_ref (const HandleContext &context) noexcept +void BridgeProcessing::take_weak_global_ref (const HandleContext &context) noexcept { abort_unless (context.control_block != nullptr, "Control block must not be null"); abort_unless (context.control_block->handle_type == JNIGlobalRefType, "Expected global reference type for handle"); @@ -310,7 +302,7 @@ void BridgeProcessingShared::take_weak_global_ref (const HandleContext &context) env->DeleteGlobalRef (handle); } -void BridgeProcessingShared::cleanup_after_java_collection () noexcept +void BridgeProcessing::cleanup_after_java_collection () noexcept { for (size_t i = 0; i < cross_refs->ComponentCount; i++) { const StronglyConnectedComponent &scc = cross_refs->Components [i]; @@ -328,7 +320,7 @@ void BridgeProcessingShared::cleanup_after_java_collection () noexcept } } -void BridgeProcessingShared::abort_unless_all_collected_or_all_alive (const StronglyConnectedComponent &scc) noexcept +void BridgeProcessing::abort_unless_all_collected_or_all_alive (const StronglyConnectedComponent &scc) noexcept { if (scc.Count == 0) { return; @@ -367,7 +359,7 @@ void CrossReferenceTarget::mark_refs_added_if_needed () noexcept } [[gnu::always_inline]] -void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] jclass java_class) noexcept +void BridgeProcessing::log_missing_add_references_method ([[maybe_unused]] jclass java_class) noexcept { log_error (LOG_DEFAULT, "Failed to find monodroidAddReferences method"); #if DEBUG @@ -383,7 +375,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] } [[gnu::always_inline]] -void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused]] jclass java_class) noexcept +void BridgeProcessing::log_missing_clear_references_method ([[maybe_unused]] jclass java_class) noexcept { log_error (LOG_DEFAULT, "Failed to find monodroidClearReferences method"); #if DEBUG @@ -399,7 +391,7 @@ void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused } [[gnu::always_inline]] -void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noexcept +void BridgeProcessing::log_weak_to_gref (jobject weak, jobject handle) noexcept { if (handle != nullptr) { if ((log_categories & LOG_GREF) != 0) [[unlikely]] { @@ -423,7 +415,7 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe } [[gnu::always_inline]] -void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept +void BridgeProcessing::log_weak_ref_collected (jobject weak) noexcept { if (!Logger::gc_spew_enabled ()) [[likely]] { return; @@ -434,7 +426,7 @@ void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept } [[gnu::always_inline]] -void BridgeProcessingShared::log_take_weak_global_ref (jobject handle) noexcept +void BridgeProcessing::log_take_weak_global_ref (jobject handle) noexcept { if (!Logger::gref_log ()) [[likely]] { return; @@ -444,7 +436,7 @@ void BridgeProcessingShared::log_take_weak_global_ref (jobject handle) noexcept } [[gnu::always_inline]] -void BridgeProcessingShared::log_weak_gref_new (jobject handle, jobject weak) noexcept +void BridgeProcessing::log_weak_gref_new (jobject handle, jobject weak) noexcept { if ((log_categories & LOG_GREF) != 0) [[unlikely]] { OSBridge::_monodroid_weak_gref_new (handle, OSBridge::get_object_ref_type (env, handle), @@ -456,7 +448,7 @@ void BridgeProcessingShared::log_weak_gref_new (jobject handle, jobject weak) no } [[gnu::always_inline]] -void BridgeProcessingShared::log_gref_delete (jobject handle) noexcept +void BridgeProcessing::log_gref_delete (jobject handle) noexcept { if ((log_categories & LOG_GREF) != 0) [[unlikely]] { OSBridge::_monodroid_gref_log_delete (handle, OSBridge::get_object_ref_type (env, handle), @@ -467,7 +459,7 @@ void BridgeProcessingShared::log_gref_delete (jobject handle) noexcept } [[gnu::always_inline]] -void BridgeProcessingShared::log_weak_ref_delete (jobject weak) noexcept +void BridgeProcessing::log_weak_ref_delete (jobject weak) noexcept { if ((log_categories & LOG_GREF) != 0) [[unlikely]] { OSBridge::_monodroid_weak_gref_delete (weak, OSBridge::get_object_ref_type (env, weak), @@ -478,7 +470,7 @@ void BridgeProcessingShared::log_weak_ref_delete (jobject weak) noexcept } [[gnu::always_inline]] -void BridgeProcessingShared::log_gc_summary () noexcept +void BridgeProcessing::log_gc_summary () noexcept { if (!Logger::gc_spew_enabled ()) [[likely]] { return; diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh deleted file mode 100644 index 70bef1ca4fc..00000000000 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include - -struct CrossReferenceTarget -{ - bool is_temporary_peer; - union - { - jobject temporary_peer; - HandleContext* context; - }; - - jobject get_handle () const noexcept; - void mark_refs_added_if_needed () noexcept; -}; - -class BridgeProcessingShared -{ -public: - explicit BridgeProcessingShared (MarkCrossReferencesArgs *args) noexcept; - static void initialize_on_runtime_init (JNIEnv *jniEnv, jclass runtimeClass) noexcept; - void process () noexcept; -private: - JNIEnv* env; - MarkCrossReferencesArgs *cross_refs; - std::unordered_map temporary_peers; - - static inline jclass GCUserPeer_class = nullptr; - static inline jmethodID GCUserPeer_ctor = nullptr; - - // Cached `mono.android.IGCUserPeer` interface and its methods. The method IDs are looked up - // once from the interface class and are valid for virtual dispatch on every implementing peer, - // so we avoid a per-edge GetObjectClass + GetMethodID lookup during bridge processing. - static inline jclass IGCUserPeer_class = nullptr; - static inline jmethodID IGCUserPeer_monodroidAddReference = nullptr; - static inline jmethodID IGCUserPeer_monodroidClearReferences = nullptr; - - void prepare_for_java_collection () noexcept; - void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept; - void take_weak_global_ref (const HandleContext &context) noexcept; - - void add_circular_references (const StronglyConnectedComponent &scc) noexcept; - void add_cross_reference (size_t source_index, size_t dest_index) noexcept; - CrossReferenceTarget select_cross_reference_target (size_t scc_index) noexcept; - bool add_reference (jobject from, jobject to) noexcept; - - void cleanup_after_java_collection () noexcept; - void cleanup_scc_for_java_collection (const StronglyConnectedComponent &scc) noexcept; - void abort_unless_all_collected_or_all_alive (const StronglyConnectedComponent &scc) noexcept; - void take_global_ref (HandleContext &context) noexcept; - - void clear_references_if_needed (const HandleContext &context) noexcept; - void clear_references (jobject handle) noexcept; - - // If a Java exception is pending on `env`, describe it, clear it, and abort. Bridge - // processing has no safe way to recover from an exception thrown by a peer's reference - // callbacks, and leaving an exception pending would make subsequent JNI calls undefined. - void abort_on_pending_java_exception (std::string_view message) noexcept; - - void log_missing_add_references_method (jclass java_class) noexcept; - void log_missing_clear_references_method (jclass java_class) noexcept; - void log_weak_to_gref (jobject weak, jobject handle) noexcept; - void log_weak_ref_collected (jobject weak) noexcept; - void log_take_weak_global_ref (jobject handle) noexcept; - void log_weak_gref_new (jobject handle, jobject weak) noexcept; - void log_gref_delete (jobject handle) noexcept; - void log_weak_ref_delete (jobject weak) noexcept; - void log_gc_summary () noexcept; - - // These methods must be implemented by every host individually - // Both methods below return `true` if they processed the call - virtual auto maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept -> bool = 0; - virtual auto maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept -> bool = 0; -}; diff --git a/src/native/clr/include/host/bridge-processing.hh b/src/native/clr/include/host/bridge-processing.hh index 2d638c0e6ba..eb54d96e815 100644 --- a/src/native/clr/include/host/bridge-processing.hh +++ b/src/native/clr/include/host/bridge-processing.hh @@ -1,24 +1,76 @@ #pragma once -#include +#include +#include +#include -#include "bridge-processing-shared.hh" +#include +#include +#include -class BridgeProcessing final : public BridgeProcessingShared +struct CrossReferenceTarget { -public: - explicit BridgeProcessing (MarkCrossReferencesArgs *args) noexcept - : BridgeProcessingShared (args) - {} + bool is_temporary_peer; + union + { + jobject temporary_peer; + HandleContext* context; + }; + + jobject get_handle () const noexcept; + void mark_refs_added_if_needed () noexcept; +}; +class BridgeProcessing +{ +public: + explicit BridgeProcessing (MarkCrossReferencesArgs *args) noexcept; + static void initialize_on_runtime_init (JNIEnv *jniEnv, jclass runtimeClass) noexcept; + void process () noexcept; private: - auto maybe_call_gc_user_peerable_add_managed_reference ([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject from, [[maybe_unused]] jobject to) noexcept -> bool override final - { - return false; // no-op for CoreCLR, we didn't process the call - } + JNIEnv* env; + MarkCrossReferencesArgs *cross_refs; + std::unordered_map temporary_peers; - auto maybe_call_gc_user_peerable_clear_managed_references ([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject handle) noexcept -> bool override final - { - return false; // no-op for CoreCLR, we didn't process the call - } + static inline jclass GCUserPeer_class = nullptr; + static inline jmethodID GCUserPeer_ctor = nullptr; + + // Cached `mono.android.IGCUserPeer` interface and its methods. The method IDs are looked up + // once from the interface class and are valid for virtual dispatch on every implementing peer, + // so we avoid a per-edge GetObjectClass + GetMethodID lookup during bridge processing. + static inline jclass IGCUserPeer_class = nullptr; + static inline jmethodID IGCUserPeer_monodroidAddReference = nullptr; + static inline jmethodID IGCUserPeer_monodroidClearReferences = nullptr; + + void prepare_for_java_collection () noexcept; + void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept; + void take_weak_global_ref (const HandleContext &context) noexcept; + + void add_circular_references (const StronglyConnectedComponent &scc) noexcept; + void add_cross_reference (size_t source_index, size_t dest_index) noexcept; + CrossReferenceTarget select_cross_reference_target (size_t scc_index) noexcept; + bool add_reference (jobject from, jobject to) noexcept; + + void cleanup_after_java_collection () noexcept; + void cleanup_scc_for_java_collection (const StronglyConnectedComponent &scc) noexcept; + void abort_unless_all_collected_or_all_alive (const StronglyConnectedComponent &scc) noexcept; + void take_global_ref (HandleContext &context) noexcept; + + void clear_references_if_needed (const HandleContext &context) noexcept; + void clear_references (jobject handle) noexcept; + + // If a Java exception is pending on `env`, describe it, clear it, and abort. Bridge + // processing has no safe way to recover from an exception thrown by a peer's reference + // callbacks, and leaving an exception pending would make subsequent JNI calls undefined. + void abort_on_pending_java_exception (std::string_view message) noexcept; + + void log_missing_add_references_method (jclass java_class) noexcept; + void log_missing_clear_references_method (jclass java_class) noexcept; + void log_weak_to_gref (jobject weak, jobject handle) noexcept; + void log_weak_ref_collected (jobject weak) noexcept; + void log_take_weak_global_ref (jobject handle) noexcept; + void log_weak_gref_new (jobject handle, jobject weak) noexcept; + void log_gref_delete (jobject handle) noexcept; + void log_weak_ref_delete (jobject weak) noexcept; + void log_gc_summary () noexcept; }; diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 9f4ce16fb25..570a97a8fe9 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -27,7 +27,6 @@ set(XAMARIN_NAOT_ANDROID_STATIC_LIB "${XAMARIN_NAOT_ANDROID_LIB}-static") set(CLR_SOURCES_PATH "../../clr") set(XAMARIN_MONODROID_SOURCES - bridge-processing.cc host.cc host-environment.cc host-jni.cc diff --git a/src/native/nativeaot/host/bridge-processing.cc b/src/native/nativeaot/host/bridge-processing.cc deleted file mode 100644 index b87a49b431b..00000000000 --- a/src/native/nativeaot/host/bridge-processing.cc +++ /dev/null @@ -1,56 +0,0 @@ -#include - -#include -#include -#include - -using namespace xamarin::android; - -void BridgeProcessing::naot_initialize_on_runtime_init (JNIEnv *env) noexcept -{ - GCUserPeerable_class = env->FindClass ("net/dot/jni/GCUserPeerable"); - if (GCUserPeerable_class == nullptr) [[unlikely]] { - Helpers::abort_application ( - LOG_DEFAULT, - "Failed to find net/dot/jni/GCUserPeerable class while initializing GC bridge processing."sv - ); - } - - GCUserPeerable_class = static_cast(OSBridge::lref_to_gref (env, GCUserPeerable_class)); - GCUserPeerable_jiAddManagedReference = env->GetMethodID (GCUserPeerable_class, "jiAddManagedReference", "(Ljava/lang/Object;)V"); - GCUserPeerable_jiClearManagedReferences = env->GetMethodID (GCUserPeerable_class, "jiClearManagedReferences", "()V"); - - if (GCUserPeerable_jiAddManagedReference == nullptr || GCUserPeerable_jiClearManagedReferences == nullptr) [[unlikely]] { - constexpr auto ABSENT = "absent"sv; - constexpr auto PRESENT = "present"sv; - - Helpers::abort_application ( - LOG_DEFAULT, - std::format ( - "Failed to find GCUserPeerable method(s): jiAddManagedReference ({}); jiClearManagedReferences ({})"sv, - GCUserPeerable_jiAddManagedReference == nullptr ? ABSENT : PRESENT, - GCUserPeerable_jiClearManagedReferences == nullptr ? ABSENT : PRESENT - ) - ); - } -} - -auto BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept -> bool -{ - if (!env->IsInstanceOf (from, GCUserPeerable_class)) { - return false; - } - - env->CallVoidMethod (from, GCUserPeerable_jiAddManagedReference, to); - return true; -} - -auto BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept -> bool -{ - if (!env->IsInstanceOf (handle, GCUserPeerable_class)) { - return false; - } - - env->CallVoidMethod (handle, GCUserPeerable_jiClearManagedReferences); - return true; -} diff --git a/src/native/nativeaot/host/host.cc b/src/native/nativeaot/host/host.cc index b6d87483c78..40b2e360864 100644 --- a/src/native/nativeaot/host/host.cc +++ b/src/native/nativeaot/host/host.cc @@ -1,4 +1,3 @@ -#include #include #include #include @@ -64,7 +63,6 @@ void Host::OnInit (jstring language, jstring filesDir, jstring cacheDir, JnienvI OSBridge::initialize_on_runtime_init (env, runtimeClass); GCBridge::initialize_on_runtime_init (env, runtimeClass); - BridgeProcessing::naot_initialize_on_runtime_init (env); // We expect the struct to be initialized by the managed land the way it sees fit, we set only the // fields we support. diff --git a/src/native/nativeaot/include/host/bridge-processing.hh b/src/native/nativeaot/include/host/bridge-processing.hh deleted file mode 100644 index 17f533aae0b..00000000000 --- a/src/native/nativeaot/include/host/bridge-processing.hh +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include - -class BridgeProcessing final : public BridgeProcessingShared -{ -public: - explicit BridgeProcessing (MarkCrossReferencesArgs *args) noexcept - : BridgeProcessingShared (args) - {} - - static void naot_initialize_on_runtime_init (JNIEnv *env) noexcept; - -private: - auto maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept -> bool override final; - auto maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept -> bool override final; - -private: - static inline jclass GCUserPeerable_class = nullptr; - static inline jmethodID GCUserPeerable_jiAddManagedReference = nullptr; - static inline jmethodID GCUserPeerable_jiClearManagedReferences = nullptr; -};