diff --git a/Documentation/docs-mobile/TOC.yml b/Documentation/docs-mobile/TOC.yml index c8026a3f16e..25edf535e06 100644 --- a/Documentation/docs-mobile/TOC.yml +++ b/Documentation/docs-mobile/TOC.yml @@ -308,6 +308,8 @@ href: messages/xa4237.md - name: XA4239 href: messages/xa4239.md + - name: XA4240 + href: messages/xa4240.md - name: XA4241 href: messages/xa4241.md - name: XA4242 diff --git a/Documentation/docs-mobile/messages/index.md b/Documentation/docs-mobile/messages/index.md index 314d77839ed..8c4ea0192de 100644 --- a/Documentation/docs-mobile/messages/index.md +++ b/Documentation/docs-mobile/messages/index.md @@ -211,6 +211,7 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla + [XA4236](xa4236.md): Cannot download Maven artifact '{group}:{artifact}'. - {jar}: {exception} - {aar}: {exception} + [XA4237](xa4237.md): Cannot download POM file for Maven artifact '{artifact}'. - Failed to resolve POM for Maven artifact '{dependency}' from '{url}'. - {exception} + [XA4239](xa4239.md): Unknown Maven repository: '{repository}'. ++ [XA4240](xa4240.md): The Java callable wrapper code generation target '{0}' is no longer supported. The '_AndroidJcwCodegenTarget' property only supports 'XAJavaInterop1'; 'XamarinAndroid' and 'JavaInterop1' are not supported. + [XA4241](xa4241.md): Java dependency '{artifact}' is not satisfied. + [XA4242](xa4242.md): Java dependency '{artifact}' is not satisfied. Microsoft maintains the NuGet package '{nugetId}' that could fulfill this dependency. + [XA4243](xa4243.md): Attribute '{name}' is required when using '{name}' for '{element}' item '{itemspec}'. diff --git a/Documentation/docs-mobile/messages/xa4240.md b/Documentation/docs-mobile/messages/xa4240.md new file mode 100644 index 00000000000..9cb4767f2b8 --- /dev/null +++ b/Documentation/docs-mobile/messages/xa4240.md @@ -0,0 +1,34 @@ +--- +title: .NET for Android error XA4240 +description: XA4240 error code +ms.date: 08/24/2026 +f1_keywords: + - "XA4240" +--- + +# .NET for Android error XA4240 + +## Example messages + +``` +error XA4240: The Java callable wrapper code generation target 'XamarinAndroid' is no longer supported. The '_AndroidJcwCodegenTarget' property only supports 'XAJavaInterop1'; 'XamarinAndroid' and 'JavaInterop1' are not supported. +error XA4240: The Java callable wrapper 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 +`XamarinAndroid` or `JavaInterop1`. These Java callable wrapper code generation +targets are no longer supported for any .NET for Android runtime. + +## Solution + +Remove any `_AndroidJcwCodegenTarget` assignment that uses `XamarinAndroid` or +`JavaInterop1`, for example: + +```xml +<_AndroidJcwCodegenTarget>JavaInterop1 +``` + +The property defaults to `XAJavaInterop1`. If a build customization must set +it explicitly, update the value to `XAJavaInterop1`. diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs b/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs index 1d9848cd4db..388d7edacbc 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs @@ -1,6 +1,7 @@ #if INSIDE_MONO_ANDROID_RUNTIME using System; using System.Reflection; +using System.Threading; using Microsoft.Android.Runtime; namespace Android.Runtime @@ -8,6 +9,9 @@ namespace Android.Runtime public static class AndroidRuntimeInternal { internal static readonly Action mono_unhandled_exception; + static int bridgeProcessingGeneration; + + internal static int BridgeProcessingGeneration => Volatile.Read (ref bridgeProcessingGeneration); #pragma warning disable CS0649 // Field is never assigned to. This field is assigned from monodroid-glue.cc. internal static volatile bool BridgeProcessing; // = false @@ -40,6 +44,11 @@ static void MonoUnhandledException (Exception ex) RuntimeNativeMethods.monodroid_debugger_unhandled_exception (ex); } + internal static void NotifyBridgeProcessingFinished () + { + Interlocked.Increment (ref bridgeProcessingGeneration); + } + public static void WaitForBridgeProcessing () { Java.Interop.JniEnvironment.Runtime.ValueManager.WaitForGCBridgeProcessing (); diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index d214cf9576b..507d10ca473 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -120,6 +120,11 @@ public static void WaitForBridgeProcessing () JniEnvironment.Runtime.ValueManager.WaitForGCBridgeProcessing (); } + /// + /// Gets the number of completed CoreCLR or NativeAOT GC bridge processing cycles. + /// + public static int BridgeProcessingGeneration => AndroidRuntimeInternal.BridgeProcessingGeneration; + public static IntPtr AllocObject (string jniClassName) { IntPtr jniClass = JNIEnv.FindClass (jniClassName); diff --git a/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalRegisteredPeers.cs b/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalRegisteredPeers.cs index 18f4ef0aaa9..35799ce1958 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalRegisteredPeers.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalRegisteredPeers.cs @@ -433,6 +433,8 @@ static unsafe void BridgeProcessingFinished (MarkCrossReferencesArgs* mcr) #pragma warning disable CA1416 JavaMarshal.FinishCrossReferenceProcessing (mcr, handlesToFree); #pragma warning restore CA1416 + + AndroidRuntimeInternal.NotifyBridgeProcessingFinished (); } static unsafe ReadOnlySpan ProcessCollectedContexts (MarkCrossReferencesArgs* mcr) diff --git a/src/Mono.Android/PublicAPI/API-35/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-35/PublicAPI.Unshipped.txt index 232ca9037b0..96701c76f82 100644 --- a/src/Mono.Android/PublicAPI/API-35/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-35/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +static Android.Runtime.JNIEnv.BridgeProcessingGeneration.get -> int REMOVED Xamarin.Android.Net.AndroidClientHandler REMOVED Xamarin.Android.Net.AndroidClientHandler.AndroidClientHandler() -> void REMOVED Xamarin.Android.Net.AndroidClientHandler.AssertSelf() -> void diff --git a/src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Unshipped.txt index 232ca9037b0..96701c76f82 100644 --- a/src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +static Android.Runtime.JNIEnv.BridgeProcessingGeneration.get -> int REMOVED Xamarin.Android.Net.AndroidClientHandler REMOVED Xamarin.Android.Net.AndroidClientHandler.AndroidClientHandler() -> void REMOVED Xamarin.Android.Net.AndroidClientHandler.AssertSelf() -> void diff --git a/src/Mono.Android/PublicAPI/API-36/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-36/PublicAPI.Unshipped.txt index 2716042abfc..8ab7b82ed22 100644 --- a/src/Mono.Android/PublicAPI/API-36/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-36/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +static Android.Runtime.JNIEnv.BridgeProcessingGeneration.get -> int REMOVED Xamarin.Android.Net.AndroidClientHandler REMOVED Xamarin.Android.Net.AndroidClientHandler.AndroidClientHandler() -> void REMOVED Xamarin.Android.Net.AndroidClientHandler.AssertSelf() -> void diff --git a/src/Mono.Android/PublicAPI/API-37.1/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-37.1/PublicAPI.Unshipped.txt index 4f7e6a01587..ae1d14901c7 100644 --- a/src/Mono.Android/PublicAPI/API-37.1/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-37.1/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ #nullable enable Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.get -> bool Android.App.ApplicationAttribute.EnableOnBackInvokedCallback.set -> void +static Android.Runtime.JNIEnv.BridgeProcessingGeneration.get -> int diff --git a/src/Mono.Android/PublicAPI/API-37/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-37/PublicAPI.Unshipped.txt index 7fdf466ee6b..61a2234c60d 100644 --- a/src/Mono.Android/PublicAPI/API-37/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-37/PublicAPI.Unshipped.txt @@ -4349,6 +4349,7 @@ static Android.Ranging.Wifi.PD.WifiPdRangingCapabilities.Creator.get -> Android. static Android.Ranging.Wifi.PD.WifiPdRangingParams.Creator.get -> Android.OS.IParcelableCreator! static Android.Ranging.Wifi.Rtt.RttStationRangingParams.Creator.get -> Android.OS.IParcelableCreator! static Android.Ranging.Wifi.Rtt.WifiRttSpecificData.Creator.get -> Android.OS.IParcelableCreator! +static Android.Runtime.JNIEnv.BridgeProcessingGeneration.get -> int static Android.Runtime.JNIEnv.GetClassNameFromInstance(nint jobject) -> string? static Android.Security.NetworkSecurityPolicy.Instance.get -> Android.Security.NetworkSecurityPolicy! static Android.Service.Chooser.ChooserSessionToken.Creator.get -> Android.OS.IParcelableCreator! 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 f03550603d9..c4fc2398da0 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 @@ -43,7 +43,7 @@ class-parse XAJavaInterop1 - <_AndroidJcwCodegenTarget Condition=" '$(_AndroidJcwCodegenTarget)' == '' and '$(_AndroidRuntime)' != 'NativeAOT' ">XAJavaInterop1 + <_AndroidJcwCodegenTarget Condition=" '$(_AndroidJcwCodegenTarget)' == '' ">XAJavaInterop1 trimmable 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 9ec28273a23..deb68276b78 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 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 828c1bbe9df..15af76356e0 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs @@ -1487,6 +1487,15 @@ public static string XA4239 { } } + /// + /// Looks up a localized string similar to The Java callable wrapper code generation target '{0}' is no longer supported. The '_AndroidJcwCodegenTarget' property only supports 'XAJavaInterop1'; 'XamarinAndroid' and 'JavaInterop1' are not supported.. + /// + public static string XA4240 { + get { + return ResourceManager.GetString("XA4240", resourceCulture); + } + } + /// /// Looks up a localized string similar to Java dependency '{0}' is not satisfied.. /// diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx index ebf1bb55b0f..af7a23ede2e 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx @@ -1058,6 +1058,11 @@ To use a custom JDK path for a command line build, set the 'JavaSdkDirectory' MS Unknown Maven repository: '{0}'. The following are literal names and should not be translated: Maven {0} - User supplied Maven repository type + + + The Java callable wrapper 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 Java callable wrapper code generation target The '{0}' MSBuild property has an invalid value of '{1}'. A valid value is one of: {2}. 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 7870753eea0..2714c6090a2 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,5 +45,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 XA4240:", builder.LastBuildOutput, "Build should fail with XA4240."); + 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 8bfd96c2228..82e9d8e2cb7 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -3042,6 +3042,14 @@ because xbuild doesn't support framework reference assemblies. + + + + -#include #include #include @@ -9,126 +8,15 @@ using namespace xamarin::android; -TemporaryPeerMap::TemporaryPeerMap (JNIEnv *jni_env, MarkCrossReferencesArgs *args) noexcept - : env{ jni_env }, - cross_refs{ args } -{ - size_t map_capacity = 0; - for (size_t i = 0; i < cross_refs->ComponentCount; i++) { - const StronglyConnectedComponent &scc = cross_refs->Components [i]; - abort_unless (!is_temporary_peer_index (scc.Count), "SCC count must not use the temporary peer marker bit"); - if (scc.Count == 0) { - map_capacity = Helpers::add_with_overflow_check (map_capacity, 1); - } - } - - if (map_capacity == 0) { - return; - } - - constexpr size_t local_ref_slack = 16; - constexpr size_t max_jint = static_cast (0x7fffffff); - size_t desired_capacity = Helpers::add_with_overflow_check (map_capacity, local_ref_slack); - jint requested_capacity = static_cast (desired_capacity > max_jint ? max_jint : desired_capacity); - - if (env->EnsureLocalCapacity (requested_capacity) != JNI_OK) [[unlikely]] { - env->ExceptionClear (); - log_warnf (LOG_GC, "Failed to reserve JNI local reference capacity for %zu temporary peers", map_capacity); - } - - capacity = map_capacity; - peers = static_cast (std::calloc (capacity, sizeof (jobject))); - abort_unless (peers != nullptr, "Failed to allocate GC bridge temporary peer map"); -} - -TemporaryPeerMap::~TemporaryPeerMap () noexcept -{ - if (peers == nullptr) { - return; - } - - for (size_t i = 0; i < count; i++) { - jobject temporary_peer = peers [i]; - if (temporary_peer != nullptr) { - env->DeleteLocalRef (temporary_peer); - peers [i] = nullptr; - } - } - - for (size_t i = 0; i < cross_refs->ComponentCount; i++) { - StronglyConnectedComponent &scc = cross_refs->Components [i]; - if (is_temporary_peer_index (scc.Count)) { - scc.Count = 0; - } - } - - count = 0; - std::free (peers); - peers = nullptr; - capacity = 0; -} - -void TemporaryPeerMap::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"); - peer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true); - abort_unless (peer_class != nullptr, "Failed to load mono.android.GCUserPeer!"); - - peer_ctor = env->GetMethodID (peer_class, "", "()V"); - abort_unless (peer_ctor != nullptr, "Failed to load mono.android.GCUserPeer constructor!"); -} - -void TemporaryPeerMap::add (StronglyConnectedComponent &scc) noexcept -{ - abort_unless (peers != nullptr, "Temporary peer map must not be null"); - abort_unless (count < capacity, "Temporary peer map must not be full"); - - jobject temporary_peer = env->NewObject (peer_class, peer_ctor); - abort_unless (temporary_peer != nullptr, "Failed to create GC bridge temporary peer"); - - size_t temporary_peer_index = count++; - peers [temporary_peer_index] = temporary_peer; - scc.Count = encode_temporary_peer_index (temporary_peer_index); -} - -bool TemporaryPeerMap::has_temporary_peer (const StronglyConnectedComponent &scc) const noexcept -{ - return is_temporary_peer_index (scc.Count); -} + GCUserPeer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true); + GCUserPeer_ctor = env->GetMethodID (GCUserPeer_class, "", "()V"); -jobject TemporaryPeerMap::get (const StronglyConnectedComponent &scc) const noexcept -{ - size_t temporary_peer_index = decode_temporary_peer_index (scc.Count); - abort_unless (temporary_peer_index < count, "Temporary peer index must be in range"); - - return peers [temporary_peer_index]; -} - -bool TemporaryPeerMap::is_temporary_peer_index (size_t count) noexcept -{ - return (count & temporary_peer_index_sign_bit) != 0; -} - -size_t TemporaryPeerMap::encode_temporary_peer_index (size_t index) noexcept -{ - abort_unless (!is_temporary_peer_index (index), "Temporary peer index is too large"); - return ~index; -} - -size_t TemporaryPeerMap::decode_temporary_peer_index (size_t count) noexcept -{ - abort_unless (is_temporary_peer_index (count), "Temporary peer index must be negative"); - return ~count; -} - -void BridgeProcessingShared::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept -{ - abort_if_invalid_pointer_argument (env, "env"); - abort_if_invalid_pointer_argument (runtimeClass, "runtimeClass"); - - TemporaryPeerMap::initialize_on_runtime_init (env, runtimeClass); + abort_unless (GCUserPeer_class != nullptr && GCUserPeer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!"); // Cache the IGCUserPeer interface method IDs once, instead of resolving them per reference edge. IGCUserPeer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_IGCUserPeer", true); @@ -142,7 +30,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 } { @@ -159,7 +47,7 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args) n } } -void BridgeProcessingShared::process () noexcept +void BridgeProcessing::process () noexcept { prepare_for_java_collection (); GCBridge::trigger_java_gc (env); @@ -167,47 +55,74 @@ void BridgeProcessingShared::process () noexcept log_gc_summary (); } -void BridgeProcessingShared::prepare_for_java_collection () noexcept +void BridgeProcessing::prepare_for_java_collection () noexcept { - prepare_sccs_and_cross_references_for_java_collection (); - - // Temporary peer indexes have been reset, so SCC counts are safe to use normally again. - // Switch global to weak references + // 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 + // local reference capacity up front so that a large number of such SCCs cannot overflow the + // JNI local reference table (which only guarantees 16 slots by default). + size_t temporary_peer_count = 0; for (size_t i = 0; i < cross_refs->ComponentCount; i++) { - const StronglyConnectedComponent &scc = cross_refs->Components [i]; - for (size_t j = 0; j < scc.Count; j++) { - const HandleContext *context = scc.Contexts [j]; - abort_unless (context != nullptr, "Context must not be null"); - - take_weak_global_ref (*context); + if (cross_refs->Components [i].Count == 0) { + temporary_peer_count = Helpers::add_with_overflow_check (temporary_peer_count, 1); } } -} -void BridgeProcessingShared::prepare_sccs_and_cross_references_for_java_collection () noexcept -{ - TemporaryPeerMap temporary_peers { env, cross_refs }; + if (temporary_peer_count > 0) { + constexpr size_t local_ref_slack = 16; + constexpr size_t max_jint = static_cast (0x7fffffff); + size_t desired_capacity = Helpers::add_with_overflow_check (temporary_peer_count, local_ref_slack); + jint requested_capacity = static_cast (desired_capacity > max_jint ? max_jint : desired_capacity); + + if (env->EnsureLocalCapacity (requested_capacity) != JNI_OK) [[unlikely]] { + env->ExceptionClear (); + log_warn (LOG_GC, "Failed to reserve JNI local reference capacity for {} temporary peers", temporary_peer_count); + } + } // Before looking at xrefs, scan the SCCs. During collection, an SCC has to behave like a // single object. If the number of objects in the SCC is anything other than 1, the SCC // must be doctored to mimic that one-object nature. for (size_t i = 0; i < cross_refs->ComponentCount; i++) { const StronglyConnectedComponent &scc = cross_refs->Components [i]; - prepare_scc_for_java_collection (i, scc, temporary_peers); + prepare_scc_for_java_collection (i, scc); } // Add the cross scc refs for (size_t i = 0; i < cross_refs->CrossReferenceCount; i++) { const ComponentCrossReference &xref = cross_refs->CrossReferences [i]; - add_cross_reference (xref.SourceGroupIndex, xref.DestinationGroupIndex, temporary_peers); + add_cross_reference (xref.SourceGroupIndex, xref.DestinationGroupIndex); + } + + // With cross references processed, the temporary peer list can be released + for (const auto& [scc, temporary_peer] : temporary_peers) { + env->DeleteLocalRef (temporary_peer); + } + + // Switch global to weak references + for (size_t i = 0; i < cross_refs->ComponentCount; i++) { + const StronglyConnectedComponent &scc = cross_refs->Components [i]; + for (size_t j = 0; j < scc.Count; j++) { + const HandleContext *context = scc.Contexts [j]; + abort_unless (context != nullptr, "Context must not be null"); + + take_weak_global_ref (*context); + } } } -void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc, TemporaryPeerMap &temporary_peers) 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) { - temporary_peers.add (cross_refs->Components [scc_index]); + jobject temporary_peer = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); + if (temporary_peer == nullptr) [[unlikely]] { + constexpr std::string_view failure = "Failed to create a temporary peer during GC bridge processing"sv; + abort_on_pending_java_exception (failure); + Helpers::abort_application (LOG_GC, failure); + } + + temporary_peers [scc_index] = temporary_peer; return; } @@ -221,14 +136,14 @@ 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, TemporaryPeerMap &temporary_peers) noexcept +CrossReferenceTarget BridgeProcessing::select_cross_reference_target (size_t scc_index) noexcept { const StronglyConnectedComponent &scc = cross_refs->Components [scc_index]; - if (temporary_peers.has_temporary_peer (scc)) { - jobject temporary_peer = temporary_peers.get (scc); - abort_unless (temporary_peer != nullptr, "Temporary peer must not be null"); - return { .is_temporary_peer = true, .temporary_peer = temporary_peer }; + if (scc.Count == 0) { + const auto temporary_peer = temporary_peers.find (scc_index); + abort_unless (temporary_peer != temporary_peers.end(), "Temporary peer must be found in the map"); + return { .is_temporary_peer = true, .temporary_peer = temporary_peer->second }; } abort_unless (scc.Contexts [0] != nullptr, "SCC must have at least one context"); @@ -236,7 +151,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"); @@ -270,25 +185,21 @@ void BridgeProcessingShared::add_circular_references (const StronglyConnectedCom } } -void BridgeProcessingShared::add_cross_reference (size_t source_index, size_t dest_index, TemporaryPeerMap &temporary_peers) noexcept +void BridgeProcessing::add_cross_reference (size_t source_index, size_t dest_index) noexcept { - CrossReferenceTarget from = select_cross_reference_target (source_index, temporary_peers); - CrossReferenceTarget to = select_cross_reference_target (dest_index, temporary_peers); + CrossReferenceTarget from = select_cross_reference_target (source_index); + CrossReferenceTarget to = select_cross_reference_target (dest_index); if (add_reference (from.get_handle(), to.get_handle())) { from.mark_refs_added_if_needed (); } } -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); @@ -301,7 +212,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; @@ -321,14 +232,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); @@ -340,7 +247,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; @@ -351,7 +258,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"); @@ -377,7 +284,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"); @@ -404,7 +311,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]; @@ -422,7 +329,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; @@ -461,7 +368,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_errorf (LOG_DEFAULT, "Failed to find monodroidAddReferences method"); #if DEBUG @@ -477,7 +384,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_errorf (LOG_DEFAULT, "Failed to find monodroidClearReferences method"); #if DEBUG @@ -493,7 +400,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]] { @@ -513,12 +420,11 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe OSBridge::_monodroid_gref_logf ( "take_global_ref wref=0x%" PRIxPTR " -> handle=0x%" PRIxPTR "\n", reinterpret_cast (weak), - reinterpret_cast (handle) - ); + reinterpret_cast (handle)); } [[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; @@ -526,12 +432,11 @@ void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept OSBridge::_monodroid_gref_logf ( "handle 0x%" PRIxPTR "/W; was collected by a Java GC", - reinterpret_cast (weak) - ); + reinterpret_cast (weak)); } [[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; @@ -541,7 +446,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), @@ -553,7 +458,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), @@ -564,7 +469,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), @@ -575,7 +480,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; @@ -598,5 +503,5 @@ void BridgeProcessingShared::log_gc_summary () noexcept } } - log_infof (LOG_GC, "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive); + log_info (LOG_GC, "GC cleanup summary: {} objects tested - resurrecting {}.", total, alive); } 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 9de2c8d020f..00000000000 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ /dev/null @@ -1,115 +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 TemporaryPeerMap -{ -public: - explicit TemporaryPeerMap (JNIEnv *env, MarkCrossReferencesArgs *cross_refs) noexcept; - ~TemporaryPeerMap () noexcept; - - TemporaryPeerMap (const TemporaryPeerMap&) = delete; - TemporaryPeerMap& operator= (const TemporaryPeerMap&) = delete; - TemporaryPeerMap (TemporaryPeerMap&&) = delete; - TemporaryPeerMap& operator= (TemporaryPeerMap&&) = delete; - - static void initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept; - - void add (StronglyConnectedComponent &scc) noexcept; - bool has_temporary_peer (const StronglyConnectedComponent &scc) const noexcept; - jobject get (const StronglyConnectedComponent &scc) const noexcept; - -private: - // Count is unsigned, so encode the temporary peer index as ~index. This stores the same bit - // pattern as -(index + 1), giving us a sign bit marker while preserving index 0. - // The destructor resets every marker before returning cross_refs to the runtime. - static constexpr size_t temporary_peer_index_sign_bit = ~(~size_t { 0 } >> 1); - - static bool is_temporary_peer_index (size_t count) noexcept; - static size_t encode_temporary_peer_index (size_t index) noexcept; - static size_t decode_temporary_peer_index (size_t count) noexcept; - - static inline jclass peer_class = nullptr; - static inline jmethodID peer_ctor = nullptr; - - JNIEnv *env; - MarkCrossReferencesArgs *cross_refs; - jobject *peers {}; - size_t count {}; - size_t capacity {}; -}; - -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; - - // 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_sccs_and_cross_references_for_java_collection () noexcept; - void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc, TemporaryPeerMap &temporary_peers) 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, TemporaryPeerMap &temporary_peers) noexcept; - CrossReferenceTarget select_cross_reference_target (size_t scc_index, TemporaryPeerMap &temporary_peers) 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 b3625794791..00000000000 --- a/src/native/nativeaot/host/bridge-processing.cc +++ /dev/null @@ -1,53 +0,0 @@ -#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"); - - constexpr char ABSENT[] = "absent"; - constexpr char PRESENT[] = "present"; - - if (GCUserPeerable_jiAddManagedReference == nullptr || GCUserPeerable_jiClearManagedReferences == nullptr) [[unlikely]] { - Helpers::abort_applicationf ( - LOG_DEFAULT, - std::source_location::current (), - "Failed to find GCUserPeerable method(s): jiAddManagedReference (%s); jiClearManagedReferences (%s)", - 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 7258f963785..28830d7cef5 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; -};