diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index dc7dff2aa87..fda2bdb0b96 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -573,6 +573,9 @@ <_ValidateObjectPointers Condition="'$(_ValidateObjectPointers)' == ''">false + + <_DisposeTaggedPointers Condition="'$(_DisposeTaggedPointers)' == ''">true + <_CustomLinkerOptions> AreAnyAssembliesTrimmed=$(_AreAnyAssembliesTrimmed) AssemblyName=$(AssemblyName).dll @@ -687,6 +690,7 @@ + dispose_tagged_pointers; + set => dispose_tagged_pointers = value; + } + protected virtual void Dispose (bool disposing) { if (disposed) return; disposed = true; - if (handle != NativeHandle.Zero) { + /* Tagged pointer is limited to 64bit, which is all we support anyway. + * + * The tagged pointer bit is: + * + * Arm64: most significant bit + * Simulators (both on arm64 and x64 desktops): most significant bit + * Desktop/x64 (macOS + Mac Catalyst): least significant bit + * Ref: https://github.com/apple-oss-distributions/objc4/blob/89543e2c0f67d38ca5211cea33f42c51500287d5/runtime/objc-internal.h#L603-L672 + */ +#if __MACOS__ || __MACCATALYST__ + ulong _OBJC_TAG_MASK; + if (Runtime.IsARM64CallingConvention) { + _OBJC_TAG_MASK = 1UL << 63; + } else { + _OBJC_TAG_MASK = 1UL; + } +#else + const ulong _OBJC_TAG_MASK = 1UL << 63; +#endif + + bool isTaggedPointer; + unchecked { + var ulongHandle = (ulong) (IntPtr) handle; + isTaggedPointer = (ulongHandle & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; + } + + if (!DisposeTaggedPointers && isTaggedPointer) { + // don't dispose tagged pointers. + FreeData (); // still need to do this though. + } else if (handle != NativeHandle.Zero) { if (disposing) { ReleaseManagedRef (); } else { diff --git a/src/ILLink.Substitutions.macOS.xml b/src/ILLink.Substitutions.macOS.xml index 67e0e9963ea..6f2f5257d54 100644 --- a/src/ILLink.Substitutions.macOS.xml +++ b/src/ILLink.Substitutions.macOS.xml @@ -1,5 +1,9 @@ + + + + diff --git a/tests/monotouch-test/ObjCRuntime/TaggedPointerTest.cs b/tests/monotouch-test/ObjCRuntime/TaggedPointerTest.cs new file mode 100644 index 00000000000..988d5bf34cb --- /dev/null +++ b/tests/monotouch-test/ObjCRuntime/TaggedPointerTest.cs @@ -0,0 +1,54 @@ +using System; + +using Foundation; + +using NUnit.Framework; + +namespace MonoTouchFixtures.ObjCRuntime { + + [TestFixture] + [Preserve (AllMembers = true)] + public class TaggedPointerTest { + + [Test] + public void TaggedPointersArentDisposed () + { + var notificationData = + """ + { + action = "action"; + aps = + { + category = "ee"; + "content-available" = dd; + "mutable-content" = cc; + sound = bb; + "thread-id" = "aa"; + }; + "em-account" = "a"; + "em-account-id" = "b"; + "em-body" = "c"; + "em-date" = "d"; + "em-from" = "e"; + "em-from-address" = "f"; + "em-notification" = g; + "em-notification-id" = h; + "em-subject" = "i"; + "gcm.message_id" = j; + "google.c.a.e" = k; + "google.c.fid" = l; + "google.c.sender.id" = m; + } + """; + + var data = NSData.FromString (notificationData); + var fmt = NSPropertyListFormat.OpenStep; + var userInfo = (NSMutableDictionary) NSPropertyListSerialization.PropertyListWithData (data, NSPropertyListReadOptions.Immutable, ref fmt, out var error); + var apsKey = new NSString ("aps"); + // Iteration here should not throw ObjectDisposedExceptions + foreach (var kv in userInfo) { + apsKey.Dispose (); + } + } + } +}