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;
-};