From 22e8482a6851ed1f50885ee2cee6680336122298 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 16 Jul 2026 12:15:18 +0200 Subject: [PATCH 1/6] [NativeAOT] Use XAJavaInterop1 codegen target and unify GC bridge Switch NativeAOT builds from the JavaInterop1 code-generation target to XAJavaInterop1, matching CoreCLR and MonoVM. As a result, the generated Java Callable Wrappers now implement `mono.android.IGCUserPeer` instead of `net.dot.jni.GCUserPeerable`, so the GC bridge processing becomes identical for CoreCLR and NativeAOT. * Remove the NativeAOT-specific `_AndroidJcwCodegenTarget=JavaInterop1` default so NativeAOT falls through to the shared `XAJavaInterop1` default in `Microsoft.Android.Sdk.DefaultProperties.targets`. * Delete the NativeAOT-only `BridgeProcessing` override that called the `GCUserPeerable.jiAddManagedReference`/`jiClearManagedReferences` methods. NativeAOT already compiles the CoreCLR bridge sources and already runs `BridgeProcessingShared::initialize_on_runtime_init` (via `GCBridge::initialize_on_runtime_init`), which caches the `mono.android.IGCUserPeer`/`GCUserPeer` classes. Falling through to that shared path makes the native GC bridge identical to CoreCLR. Dropping the now-unused JavaInterop1 codegen + runtime support (ManagedPeer, JavaProxyObject/JavaProxyThrowable, GCUserPeerable) is a follow-up. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8d949279-d51c-4ce0-baf2-0b1d8e919928 --- ...soft.Android.Sdk.DefaultProperties.targets | 2 +- .../Microsoft.Android.Sdk.NativeAOT.targets | 1 - src/native/nativeaot/host/CMakeLists.txt | 1 - .../nativeaot/host/bridge-processing.cc | 56 ------------------- src/native/nativeaot/host/host.cc | 2 - .../include/host/bridge-processing.hh | 24 -------- 6 files changed, 1 insertion(+), 85 deletions(-) delete mode 100644 src/native/nativeaot/host/bridge-processing.cc delete mode 100644 src/native/nativeaot/include/host/bridge-processing.hh 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 96835c928c8..a958baab588 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)' == '' ">managed true 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; -}; From 705aad2e42c947bdca6ef6d7c561a5bf557a542d Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 16 Jul 2026 12:22:07 +0200 Subject: [PATCH 2/6] [NativeAOT] Collapse BridgeProcessingShared into BridgeProcessing Now that both CoreCLR and NativeAOT use the same IGCUserPeer-based GC bridge, the `maybe_call_gc_user_peerable_*` extension points always return `false` and no host needs to override them. Remove the abstract base / derived split: * Merge `BridgeProcessingShared` into a single concrete `BridgeProcessing` class (delete `bridge-processing-shared.hh`). * Drop the two pure-virtual `maybe_call_gc_user_peerable_*` hooks and the CoreCLR no-op overrides, and inline the IGCUserPeer path directly in `add_reference`/`clear_references`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8d949279-d51c-4ce0-baf2-0b1d8e919928 --- src/native/clr/host/bridge-processing.cc | 58 ++++++------- .../include/host/bridge-processing-shared.hh | 81 ------------------ .../clr/include/host/bridge-processing.hh | 82 +++++++++++++++---- 3 files changed, 92 insertions(+), 129 deletions(-) delete mode 100644 src/native/clr/include/host/bridge-processing-shared.hh diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 91b772c25dd..423ea0fe13c 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -6,7 +6,7 @@ using namespace xamarin::android; -void BridgeProcessingShared::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept +void BridgeProcessing::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept { abort_if_invalid_pointer_argument (env, "env"); abort_if_invalid_pointer_argument (runtimeClass, "runtimeClass"); @@ -28,7 +28,7 @@ void BridgeProcessingShared::initialize_on_runtime_init (JNIEnv *env, jclass run "Failed to load mono.android.IGCUserPeer methods!"); } -BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args) noexcept +BridgeProcessing::BridgeProcessing (MarkCrossReferencesArgs *args) noexcept : env{ OSBridge::ensure_jnienv () }, cross_refs{ args } { @@ -45,7 +45,7 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args) n } } -void BridgeProcessingShared::process () noexcept +void BridgeProcessing::process () noexcept { prepare_for_java_collection (); GCBridge::trigger_java_gc (env); @@ -53,7 +53,7 @@ void BridgeProcessingShared::process () noexcept log_gc_summary (); } -void BridgeProcessingShared::prepare_for_java_collection () noexcept +void BridgeProcessing::prepare_for_java_collection () noexcept { // Each SCC with no IGCUserPeers is represented by a temporary peer held as a JNI local // reference that must stay alive until every cross reference has been added. Reserve enough @@ -109,7 +109,7 @@ void BridgeProcessingShared::prepare_for_java_collection () noexcept } } -void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept +void BridgeProcessing::prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept { // Count == 0 case: Some SCCs might have no IGCUserPeers associated with them, so we must create one if (scc.Count == 0) { @@ -127,7 +127,7 @@ void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, add_circular_references (scc); } -CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size_t scc_index) noexcept +CrossReferenceTarget BridgeProcessing::select_cross_reference_target (size_t scc_index) noexcept { const StronglyConnectedComponent &scc = cross_refs->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; }; From 182c902ed7f716b5d32dd66f4e96924b8652a0f2 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 17 Jul 2026 08:42:06 +0200 Subject: [PATCH 3/6] [Tests] Update NativeAOT APK size reference Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70f63eb7-6599-414c-a947-d860705aa0fa --- .../Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From ea30df42372c70d8da97ea4133a4737375739422 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 17 Jul 2026 11:02:08 +0200 Subject: [PATCH 4/6] [Build] Reject JavaInterop1 JCW codegen target Fail early with XA4232 whenever _AndroidJcwCodegenTarget is set to JavaInterop1, regardless of the selected runtime. Reuse the existing obsolete-codegen diagnostic and cover MonoVM, CoreCLR, and NativeAOT. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9b06128-4d84-4113-87e9-558e6561f6d0 --- .../Properties/Resources.Designer.cs | 2 +- .../Properties/Resources.resx | 4 ++-- .../InvalidConfigTests.cs | 20 +++++++++++++++++++ .../Xamarin.Android.Common.targets | 8 ++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs index 0d77d814434..8f9cc9ecb6a 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. Remove the '_AndroidJcwCodegenTarget' property or set it to 'XAJavaInterop1'.. /// 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..0790846641a 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. Remove the '_AndroidJcwCodegenTarget' property or set it to 'XAJavaInterop1'. + The following are literal names and should not be translated: _AndroidJcwCodegenTarget, XAJavaInterop1 {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..96573488c49 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,25 @@ public void XA0119AAB () } } + [Test] + public void JavaInterop1JcwCodegenTargetIsUnsupported ([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", "JavaInterop1"); + 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 ("_AndroidJcwCodegenTarget", builder.LastBuildOutput, "Error should identify the unsupported property."); + } + } + } } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 220abcdaeb8..dfb852fce36 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. + + + + Date: Fri, 17 Jul 2026 11:14:20 +0200 Subject: [PATCH 5/6] [Docs] Mark JavaInterop1 unsupported Update the Android code generation property and migration guidance to identify XAJavaInterop1 as the only supported target. Refresh XA4232 documentation for the new _AndroidJcwCodegenTarget error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9b06128-4d84-4113-87e9-558e6561f6d0 --- .../building-apps/build-properties.md | 3 +-- Documentation/docs-mobile/messages/index.md | 2 +- Documentation/docs-mobile/messages/xa4232.md | 27 +++++++++---------- Documentation/guides/OneDotNet.md | 2 +- .../guides/OneDotNetBindingProjects.md | 5 ++-- 5 files changed, 19 insertions(+), 20 deletions(-) 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..9d3c406a128 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 'JavaInterop1' is no longer supported. Remove the '_AndroidJcwCodegenTarget' property or set it to 'XAJavaInterop1'. + [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..263fce239a8 100644 --- a/Documentation/docs-mobile/messages/xa4232.md +++ b/Documentation/docs-mobile/messages/xa4232.md @@ -1,33 +1,32 @@ --- -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 'JavaInterop1' is no longer supported. Remove the '_AndroidJcwCodegenTarget' property or set it to 'XAJavaInterop1'. ``` ## Issue -The Android code generation target `XamarinAndroid` has been deprecated. +The project sets the internal `_AndroidJcwCodegenTarget` MSBuild property to +`JavaInterop1`. This code generation target is 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 this property assignment from the project: ```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`. From 46fcc94502a19d33dc445d2706dcb17f0af781d9 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 17 Jul 2026 11:38:27 +0200 Subject: [PATCH 6/6] [Build] Reject legacy JCW codegen targets Extend XA4232 to reject both XamarinAndroid and JavaInterop1 when selected through _AndroidJcwCodegenTarget. Update the diagnostic documentation and cover both values across MonoVM, CoreCLR, and NativeAOT. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9b06128-4d84-4113-87e9-558e6561f6d0 --- Documentation/docs-mobile/messages/index.md | 2 +- Documentation/docs-mobile/messages/xa4232.md | 10 ++++++---- .../Properties/Resources.Designer.cs | 2 +- .../Properties/Resources.resx | 4 ++-- .../Xamarin.Android.Build.Tests/InvalidConfigTests.cs | 8 +++++--- .../Xamarin.Android.Common.targets | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Documentation/docs-mobile/messages/index.md b/Documentation/docs-mobile/messages/index.md index 9d3c406a128..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 'JavaInterop1' is no longer supported. Remove the '_AndroidJcwCodegenTarget' property or set it to '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 263fce239a8..f257169f08f 100644 --- a/Documentation/docs-mobile/messages/xa4232.md +++ b/Documentation/docs-mobile/messages/xa4232.md @@ -11,18 +11,20 @@ f1_keywords: ## Example messages ``` -error XA4232: The Android code generation target 'JavaInterop1' is no longer supported. Remove the '_AndroidJcwCodegenTarget' property or set it to '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 project sets the internal `_AndroidJcwCodegenTarget` MSBuild property to -`JavaInterop1`. This code generation target is no longer supported for any -.NET for Android runtime. +`XamarinAndroid` or `JavaInterop1`. These code generation targets are no longer +supported for any .NET for Android runtime. ## Solution -Remove this property assignment from the project: +Remove any `_AndroidJcwCodegenTarget` assignment that uses `XamarinAndroid` or +`JavaInterop1`, for example: ```xml <_AndroidJcwCodegenTarget>JavaInterop1 diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs index 8f9cc9ecb6a..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 no longer supported. Remove the '_AndroidJcwCodegenTarget' property or set it to '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 0790846641a..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 no longer supported. Remove the '_AndroidJcwCodegenTarget' property or set it to 'XAJavaInterop1'. - The following are literal names and should not be translated: _AndroidJcwCodegenTarget, 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 96573488c49..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 @@ -45,7 +45,9 @@ public void XA0119AAB () } [Test] - public void JavaInterop1JcwCodegenTargetIsUnsupported ([Values (AndroidRuntime.MonoVM, AndroidRuntime.CoreCLR, AndroidRuntime.NativeAOT)] AndroidRuntime runtime) + 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, @@ -54,13 +56,13 @@ public void JavaInterop1JcwCodegenTargetIsUnsupported ([Values (AndroidRuntime.M project.SetProperty ("_DisableCheckForUnsupportedMonoMobileRuntime", "true"); } project.SetRuntime (runtime); - project.SetProperty ("_AndroidJcwCodegenTarget", "JavaInterop1"); + 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 ("_AndroidJcwCodegenTarget", builder.LastBuildOutput, "Error should identify the unsupported property."); + StringAssertEx.Contains (codegenTarget, builder.LastBuildOutput, "Error should identify the unsupported code generation target."); } } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index dfb852fce36..5159244a4d3 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -3008,7 +3008,7 @@ because xbuild doesn't support framework reference assemblies. + Condition=" '$(_AndroidJcwCodegenTarget)' == 'XamarinAndroid' or '$(_AndroidJcwCodegenTarget)' == 'JavaInterop1' " />