diff --git a/src/AddressBook/ABRecord.cs b/src/AddressBook/ABRecord.cs index 115e759fec46..578d9811c7b8 100644 --- a/src/AddressBook/ABRecord.cs +++ b/src/AddressBook/ABRecord.cs @@ -66,10 +66,15 @@ internal ABRecord (NativeHandle handle, bool owns) } public static ABRecord? FromHandle (IntPtr handle) + { + return FromHandle (handle, false); + } + + internal static ABRecord? FromHandle (IntPtr handle, bool owns) { if (handle == IntPtr.Zero) return null; - return FromHandle (handle, null, false); + return FromHandle (handle, null, owns); } internal static ABRecord FromHandle (IntPtr handle, ABAddressBook? addressbook, bool owns = true) diff --git a/src/AudioToolbox/MusicSequence.cs b/src/AudioToolbox/MusicSequence.cs index 2f2d70867d83..9aa73b7c5e3e 100644 --- a/src/AudioToolbox/MusicSequence.cs +++ b/src/AudioToolbox/MusicSequence.cs @@ -111,6 +111,11 @@ protected override void Dispose (bool disposing) static readonly Dictionary sequenceMap = new Dictionary (Runtime.IntPtrEqualityComparer); internal static MusicSequence Lookup (IntPtr handle) + { + return Lookup (handle, false); + } + + internal static MusicSequence Lookup (IntPtr handle, bool owns) { lock (sequenceMap) { if (sequenceMap.TryGetValue (handle, out var weakRef)) { @@ -120,7 +125,7 @@ internal static MusicSequence Lookup (IntPtr handle) } sequenceMap.Remove (handle); } - var ms = new MusicSequence (handle, false); + var ms = new MusicSequence (handle, owns); sequenceMap [handle] = new WeakReference (ms); return ms; } diff --git a/src/Foundation/NSArray.cs b/src/Foundation/NSArray.cs index 028be3f6c5d1..fc054c8fbff6 100644 --- a/src/Foundation/NSArray.cs +++ b/src/Foundation/NSArray.cs @@ -390,6 +390,18 @@ static public T [] ArrayFromHandleFunc (NativeHandle handle, FuncCreate a managed array from a pointer to a native NSArray instance. + /// The pointer to the native NSArray instance. + /// A callback that returns an instance of the type T for a given pointer (for an element in the NSArray). + /// Whether the native NSArray instance should be released before returning or not. + public static T [] ArrayFromHandleFunc (NativeHandle handle, Func createObject, bool releaseHandle) + { + var rv = ArrayFromHandleFunc (handle, createObject); + if (releaseHandle && handle != NativeHandle.Zero) + NSObject.DangerousRelease (handle); + return rv; + } + static public T [] ArrayFromHandle (NativeHandle handle, Converter creator) { if (handle == NativeHandle.Zero) diff --git a/src/MediaToolbox/MTAudioProcessingTap.cs b/src/MediaToolbox/MTAudioProcessingTap.cs index 1cf58dad97b6..df910cd2e4ac 100644 --- a/src/MediaToolbox/MTAudioProcessingTap.cs +++ b/src/MediaToolbox/MTAudioProcessingTap.cs @@ -87,7 +87,7 @@ delegate void MTAudioProcessingTapProcessCallbackProxy (/* MTAudioProcessingTapR MTAudioProcessingTapCallbacks callbacks; - internal static MTAudioProcessingTap? FromHandle (IntPtr handle) + internal static MTAudioProcessingTap? FromHandle (IntPtr handle, bool owns) { lock (handles){ if (handles.TryGetValue (handle, out var ret)) diff --git a/src/ObjCRuntime/Runtime.cs b/src/ObjCRuntime/Runtime.cs index 5db823dfa3e1..c5526b40c7d3 100644 --- a/src/ObjCRuntime/Runtime.cs +++ b/src/ObjCRuntime/Runtime.cs @@ -1830,6 +1830,18 @@ static IntPtr CreateNSObject (IntPtr type_gchandle, IntPtr handle, NSObject.Flag { return GetNSObject ((IntPtr) ptr, MissingCtorResolution.ThrowConstructor1NotFound); } + + /// Wraps an unmanaged into a fully typed , or returns an existing wrapper object if one already exists. + /// A pointer to an unmanaged or any class that derives from the Objective-C NSObject class. + /// Pass true if the caller has a reference to the native object, and wants to give it to the managed wrapper instance. Otherwise pass false (and the native object will be retained if needed). + /// An instance of a class that derives . + /// + /// The runtime create an instance of the most derived managed class. + /// + public static NSObject? GetNSObject (NativeHandle ptr, bool owns) + { + return GetNSObject ((IntPtr) ptr, owns, MissingCtorResolution.ThrowConstructor1NotFound); + } #endif public static NSObject? GetNSObject (IntPtr ptr) @@ -1838,16 +1850,27 @@ static IntPtr CreateNSObject (IntPtr type_gchandle, IntPtr handle, NSObject.Flag } internal static NSObject? GetNSObject (IntPtr ptr, MissingCtorResolution missingCtorResolution, bool evenInFinalizerQueue = false) + { + return GetNSObject (ptr, false, missingCtorResolution, evenInFinalizerQueue); + } + + internal static NSObject? GetNSObject (IntPtr ptr, bool owns, MissingCtorResolution missingCtorResolution, bool evenInFinalizerQueue = false) { if (ptr == IntPtr.Zero) return null; var o = TryGetNSObject (ptr, evenInFinalizerQueue); - if (o is not null) + if (o is not null) { + if (owns) + o.DangerousRelease (); return o; + } - return ConstructNSObject (ptr, Class.GetClassForObject (ptr), missingCtorResolution); + o = ConstructNSObject (ptr, Class.GetClassForObject (ptr), missingCtorResolution); + if (owns) + NSObject.DangerousRelease (ptr); + return o; } static public T? GetNSObject (IntPtr ptr) where T : NSObject diff --git a/src/ObjCRuntime/Selector.cs b/src/ObjCRuntime/Selector.cs index 7643d6b42256..7f4326ad8936 100644 --- a/src/ObjCRuntime/Selector.cs +++ b/src/ObjCRuntime/Selector.cs @@ -129,6 +129,15 @@ internal static string GetName (IntPtr handle) return new Selector (sel, false); } + /// Creates a managed Selector instance from a native selector. + /// The native selector handle. + /// Whether the caller owns the native selector handle or not. + /// It's not possible to free a selector, so the parameter is ignored. + public static Selector? FromHandle (NativeHandle selector, bool owns) + { + return FromHandle (selector); + } + public static Selector Register (NativeHandle handle) { return new Selector (handle); diff --git a/src/bgen/Attributes.cs b/src/bgen/Attributes.cs index 9c5d819f7270..89ac0ac635f6 100644 --- a/src/bgen/Attributes.cs +++ b/src/bgen/Attributes.cs @@ -111,6 +111,7 @@ public RetainAttribute (string wrap) public string WrapName { get; set; } } +[AttributeUsage (AttributeTargets.ReturnValue, AllowMultiple = false)] public class ReleaseAttribute : Attribute { } diff --git a/src/bgen/Caches/TypeCache.cs b/src/bgen/Caches/TypeCache.cs index 6da8cb5f211e..a97441c5a1cb 100644 --- a/src/bgen/Caches/TypeCache.cs +++ b/src/bgen/Caches/TypeCache.cs @@ -35,6 +35,7 @@ public class TypeCache { /* fundamental */ public Type NSObject { get; } public Type INativeObject { get; } + public Type NativeObject { get; } /* objcruntime */ public Type BlockLiteral { get; } @@ -195,6 +196,7 @@ public TypeCache (MetadataLoadContext universe, Frameworks frameworks, PlatformN /* fundamental */ NSObject = Lookup (platformAssembly, "Foundation", "NSObject"); INativeObject = Lookup (platformAssembly, "ObjCRuntime", "INativeObject"); + NativeObject = Lookup (platformAssembly, "CoreFoundation", "NativeObject"); /* objcruntime */ BlockLiteral = Lookup (platformAssembly, "ObjCRuntime", "BlockLiteral"); diff --git a/src/bgen/Generator.cs b/src/bgen/Generator.cs index 32d4ebc25e52..f07290779d3e 100644 --- a/src/bgen/Generator.cs +++ b/src/bgen/Generator.cs @@ -57,6 +57,8 @@ // Disable until we get around to enable + fix any issues. #nullable disable +// but allow annotation source code with nullability info. +#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. public partial class Generator : IMemberGatherer { internal bool IsPublicMode; @@ -536,7 +538,7 @@ string GetFromBindAsWrapper (MemberInformation minfo, out string suffix) return append; } - public bool HasForcedAttribute (ICustomAttributeProvider cu, out string owns) + public bool HasForcedAttribute (ICustomAttributeProvider cu, out bool owns) { var att = AttributeManager.GetCustomAttribute (cu); @@ -547,11 +549,11 @@ public bool HasForcedAttribute (ICustomAttributeProvider cu, out string owns) } if (att is null) { - owns = "false"; + owns = false; return false; } - owns = att.Owns ? "true" : "false"; + owns = att.Owns; return true; } @@ -606,8 +608,7 @@ public TrampolineInfo MakeTrampoline (Type t) pars.Add (new TrampolineParameterInfo ("IntPtr", "block")); var parameters = mi.GetParameters (); foreach (var pi in parameters) { - string isForcedOwns; - var isForced = HasForcedAttribute (pi, out isForcedOwns); + var isForced = HasForcedAttribute (pi, out var isForcedOwns); if (pi != parameters [0]) invoke.Append (", "); @@ -619,7 +620,7 @@ public TrampolineInfo MakeTrampoline (Type t) if (IsProtocolInterface (pi.ParameterType)) { invoke.AppendFormat (" Runtime.GetINativeObject<{1}> ({0}, false)!", safe_name, pi.ParameterType); } else if (isForced) { - invoke.AppendFormat (" Runtime.GetINativeObject<{1}> ({0}, true, {2})!", safe_name, TypeManager.RenderType (pi.ParameterType), isForcedOwns); + invoke.AppendFormat (" Runtime.GetINativeObject<{1}> ({0}, true, {2})!", safe_name, TypeManager.RenderType (pi.ParameterType), isForcedOwns ? "true" : "false"); } else if (IsNSObject (pi.ParameterType)) { invoke.AppendFormat (" Runtime.GetNSObject<{1}> ({0})!", safe_name, TypeManager.RenderType (pi.ParameterType)); } else { @@ -2886,6 +2887,7 @@ void GetReturnsWrappers (MethodInfo mi, MemberInformation minfo, Type declaringT MarshalInfo mai = new MarshalInfo (this, mi); MarshalType mt; + var owns = (minfo?.is_return_release == true) || (minfo?.is_forced_owns == true) ? "true" : "false"; if (GetNativeEnumToManagedExpression (mi.ReturnType, out cast_a, out cast_b, out var _, postproc)) { // we're done here } else if (mi.ReturnType.IsEnum) { @@ -2893,15 +2895,15 @@ void GetReturnsWrappers (MethodInfo mi, MemberInformation minfo, Type declaringT cast_b = ""; } else if (marshalTypes.TryGetMarshalType (mai.Type, out mt)) { cast_a = mt.CreateFromRet; - cast_b = mt.ClosingCreate; + cast_b = mt.ClosingCreate?.Replace ("%OWNS%", owns) ?? string.Empty; } else if (TypeManager.IsWrappedType (mi.ReturnType)) { // protocol support means we can return interfaces and, as far as .NET knows, they might not be NSObject if (IsProtocolInterface (mi.ReturnType)) { cast_a = " Runtime.GetINativeObject<" + TypeManager.FormatType (minfo?.type ?? mi.DeclaringType, mi.ReturnType) + "> ("; - cast_b = $", {(minfo?.is_return_release == true ? "true" : "false")})!"; + cast_b = $", {owns})!"; } else if (minfo is not null && minfo.is_forced) { cast_a = " Runtime.GetINativeObject<" + TypeManager.FormatType (minfo.type, mi.ReturnType) + "> ("; - cast_b = $", true, {minfo.is_forced_owns})!"; + cast_b = $", true, {owns})!"; } else if (minfo is not null && minfo.is_bindAs) { var bindAs = GetBindAsAttribute (minfo.mi); var nullableBindAsType = TypeManager.GetUnderlyingNullableType (bindAs.Type); @@ -2915,27 +2917,27 @@ void GetReturnsWrappers (MethodInfo mi, MemberInformation minfo, Type declaringT if (isNullable) { print ("{0} retvaltmp;", NativeHandleType); cast_a = "((retvaltmp = "; - cast_b = $") == IntPtr.Zero ? default ({formattedBindAsType}) : ({wrapper}Runtime.GetNSObject<{formattedReturnType}> (retvaltmp)!){suffix})"; + cast_b = $") == IntPtr.Zero ? default ({formattedBindAsType}) : ({wrapper}Runtime.GetNSObject<{formattedReturnType}> (retvaltmp, {owns})!){suffix})"; } else { cast_a = $"{wrapper}Runtime.GetNSObject<{formattedReturnType}> ("; - cast_b = $")!{suffix}"; + cast_b = $", {owns})!{suffix}"; } } else { var enumCast = (bindAsType.IsEnum && !minfo.type.IsArray) ? $"({formattedBindAsType}) " : string.Empty; print ("{0} retvaltmp;", NativeHandleType); cast_a = "((retvaltmp = "; - cast_b = $") == IntPtr.Zero ? default ({formattedBindAsType}) : ({enumCast}Runtime.GetNSObject<{formattedReturnType}> (retvaltmp)!{wrapper})){suffix}"; + cast_b = $") == IntPtr.Zero ? default ({formattedBindAsType}) : ({enumCast}Runtime.GetNSObject<{formattedReturnType}> (retvaltmp, {owns})!{wrapper})){suffix}"; } } else { cast_a = " Runtime.GetNSObject<" + TypeManager.FormatType (minfo?.type ?? declaringType, mi.ReturnType) + "> ("; - cast_b = ")!"; + cast_b = $", {owns})!"; } } else if (mi.ReturnType.IsGenericParameter) { cast_a = " Runtime.GetINativeObject<" + mi.ReturnType.Name + "> ("; - cast_b = ", false)!"; + cast_b = $", {owns})!"; } else if (mai.Type == TypeCache.System_String && !mai.PlainString) { cast_a = "CFString.FromHandle ("; - cast_b = ")!"; + cast_b = $", {owns})!"; } else if (mi.ReturnType.IsSubclassOf (TypeCache.System_Delegate)) { cast_a = ""; cast_b = ""; @@ -2951,11 +2953,11 @@ void GetReturnsWrappers (MethodInfo mi, MemberInformation minfo, Type declaringT print ("{0} retvalarrtmp;", NativeHandleType); cast_a = "((retvalarrtmp = "; cast_b = ") == IntPtr.Zero ? null! : ("; - cast_b += $"NSArray.ArrayFromHandleFunc <{TypeManager.FormatType (bindAsT.DeclaringType, bindAsT)}> (retvalarrtmp, {GetFromBindAsWrapper (minfo, out suffix)})" + suffix; - cast_b += "))"; + cast_b += $"NSArray.ArrayFromHandleFunc <{TypeManager.FormatType (bindAsT.DeclaringType, bindAsT)}> (retvalarrtmp, {GetFromBindAsWrapper (minfo, out suffix)}, {owns})" + suffix; + cast_b += $"))"; } else if (etype == TypeCache.System_String) { cast_a = "CFArray.StringArrayFromHandle ("; - cast_b = ")!"; + cast_b = $", {owns})!"; } else if (etype == TypeCache.Selector) { exceptions.Add (ErrorHelper.CreateError (1066, mai.Type.FullName, mi.DeclaringType.FullName, mi.Name)); } else { @@ -2963,7 +2965,7 @@ void GetReturnsWrappers (MethodInfo mi, MemberInformation minfo, Type declaringT cast_a = "CFArray.ArrayFromHandle("; else cast_a = "CFArray.ArrayFromHandle<" + TypeManager.FormatType (mi.DeclaringType, etype) + ">("; - cast_b = ")!"; + cast_b = $", {owns})!"; } } else if (mi.ReturnType.Namespace == "System" && mi.ReturnType.Name == "nint") { cast_a = "(nint) "; @@ -3418,7 +3420,7 @@ void GenerateTypeLowering (MethodInfo mi, bool null_allowed_override, out String } else if (isINativeObjectSubclass) { if (!pi.IsOut) by_ref_processing.AppendFormat ("if ({0}Value != ({0} is null ? NativeHandle.Zero : {0}.Handle))\n\t", pi.Name.GetSafeParamName ()); - by_ref_processing.AppendFormat ("{0} = Runtime.GetINativeObject<{1}> ({0}Value, {2}, {3})!;\n", pi.Name.GetSafeParamName (), TypeManager.RenderType (elementType), isForcedType ? "true" : "false", isForcedType ? isForcedOwns : "false"); + by_ref_processing.AppendFormat ("{0} = Runtime.GetINativeObject<{1}> ({0}Value, {2}, {3})!;\n", pi.Name.GetSafeParamName (), TypeManager.RenderType (elementType), isForcedType ? "true" : "false", (isForcedType && isForcedOwns) ? "true" : "false"); } else { throw ErrorHelper.CreateError (88, mai.Type, mi); } @@ -3650,22 +3652,6 @@ public void GenerateMethodBody (MemberInformation minfo, MethodInfo mi, string s if (shouldMarshalNativeExceptions) print ("Runtime.ThrowException (exception_gchandle);"); - if (minfo.is_return_release && !IsProtocolInterface (mi.ReturnType)) { - - // Make sure we generate the required signature in Messaging only if needed - // bool_objc_msgSendSuper_IntPtr: for respondsToSelector: - if (!send_methods.ContainsKey ("void_objc_msgSend")) { - print (m, "[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSendSuper\")]"); - print (m, "public extern static void void_objc_msgSend (IntPtr receiever, IntPtr selector);"); - RegisterMethodName ("void_objc_msgSend"); - } - - print ("if (ret is not null)"); - indent++; - print ("global::{0}.void_objc_msgSend (ret.Handle, Selector.GetHandle (\"release\"));", NamespaceCache.Messaging); - indent--; - } - Inject (mi); if (disposes.Length > 0) diff --git a/src/bgen/Models/MarshalType.cs b/src/bgen/Models/MarshalType.cs index 20c6178c0f52..e6835861f956 100644 --- a/src/bgen/Models/MarshalType.cs +++ b/src/bgen/Models/MarshalType.cs @@ -9,14 +9,14 @@ public class MarshalType { public string CreateFromRet { get; } public string? ClosingCreate { get; } - public MarshalType (Type t, string? encode = null, string? fetch = null, string? create = null, string? closingCreate = ")") + public MarshalType (Type t, string? encode = null, string? fetch = null, string? create = null, string? closingCreate = ", %OWNS%)") { Type = t; Encoding = encode ?? Generator.NativeHandleType; ParameterMarshal = fetch ?? "{0}.Handle"; if (create is null) { CreateFromRet = $"Runtime.GetINativeObject ("; - ClosingCreate = ", false)!"; + ClosingCreate = ", %OWNS%)!"; } else { CreateFromRet = create; ClosingCreate = closingCreate; diff --git a/src/bgen/Models/MarshalTypeList.cs b/src/bgen/Models/MarshalTypeList.cs index b83110fe8263..ea7a3250cada 100644 --- a/src/bgen/Models/MarshalTypeList.cs +++ b/src/bgen/Models/MarshalTypeList.cs @@ -10,8 +10,8 @@ public class MarshalTypeList : List { public void Load (TypeCache typeCache, Frameworks frameworks) { - Add (new MarshalType (typeCache.NSObject, create: "Runtime.GetNSObject (", closingCreate: ")!")); - Add (new MarshalType (typeCache.Selector, create: "Selector.FromHandle (", closingCreate: ")!")); + Add (new MarshalType (typeCache.NSObject, create: "Runtime.GetNSObject (", closingCreate: ", %OWNS%)!")); + Add (new MarshalType (typeCache.Selector, create: "Selector.FromHandle (", closingCreate: ", %OWNS%)!")); Add (new MarshalType (typeCache.BlockLiteral, "BlockLiteral", "{0}", "THIS_IS_BROKEN")); if (typeCache.MusicSequence is not null) Add (new MarshalType (typeCache.MusicSequence, create: "global::AudioToolbox.MusicSequence.Lookup (")); @@ -42,15 +42,15 @@ public void Load (TypeCache typeCache, Frameworks frameworks) Add (typeCache.CVImageBuffer); } if (frameworks.HaveMediaToolbox) - Add (new MarshalType (typeCache.MTAudioProcessingTap!, create: "MediaToolbox.MTAudioProcessingTap.FromHandle(")); + Add (new MarshalType (typeCache.MTAudioProcessingTap!, create: "MediaToolbox.MTAudioProcessingTap.FromHandle (", closingCreate: ", %OWNS%)!")); if (frameworks.HaveAddressBook) { Add (typeCache.ABAddressBook); - Add (new MarshalType (typeCache.ABPerson!, create: "(ABPerson) ABRecord.FromHandle (", closingCreate: ")!")); - Add (new MarshalType (typeCache.ABRecord!, create: "ABRecord.FromHandle (", closingCreate: ")!")); + Add (new MarshalType (typeCache.ABPerson!, create: "(ABPerson) ABRecord.FromHandle (", closingCreate: ", %OWNS%)!")); + Add (new MarshalType (typeCache.ABRecord!, create: "ABRecord.FromHandle (", closingCreate: ", %OWNS%)!")); } if (frameworks.HaveCoreVideo) { // owns `false` like ptr ctor https://github.com/xamarin/xamarin-macios/blob/6f68ab6f79c5f1d96d2cbb1e697330623164e46d/src/CoreVideo/CVBuffer.cs#L74-L90 - Add (new MarshalType (typeCache.CVPixelBuffer!, create: "Runtime.GetINativeObject (", closingCreate: ", false)!")); + Add (new MarshalType (typeCache.CVPixelBuffer!, create: "Runtime.GetINativeObject (", closingCreate: ", %OWNS%)!")); } Add (typeCache.CGLayer); if (frameworks.HaveCoreMedia) @@ -63,7 +63,7 @@ public void Load (TypeCache typeCache, Frameworks frameworks) if (frameworks.HaveAudioUnit) Add (typeCache.AudioComponent); if (frameworks.HaveCoreMedia) { - Add (new MarshalType (typeCache.CMFormatDescription!, create: "CMFormatDescription.Create (", closingCreate: ")!")); + Add (new MarshalType (typeCache.CMFormatDescription!, create: "CMFormatDescription.Create (", closingCreate: ", %OWNS%)!")); Add (typeCache.CMAudioFormatDescription); Add (typeCache.CMVideoFormatDescription); } @@ -77,7 +77,7 @@ public void Load (TypeCache typeCache, Frameworks frameworks) Add (typeCache.SecProtocolOptions); Add (typeCache.SecProtocolMetadata); Add (typeCache.SecAccessControl); - Add (new MarshalType (typeCache.AudioBuffers, create: "new global::AudioToolbox.AudioBuffers (", closingCreate: ", false)")); + Add (new MarshalType (typeCache.AudioBuffers, create: "new global::AudioToolbox.AudioBuffers (", closingCreate: ", %OWNS%)")); if (frameworks.HaveAudioUnit) { Add (typeCache.AURenderEventEnumerator); } diff --git a/src/bgen/Models/MemberInformation.cs b/src/bgen/Models/MemberInformation.cs index 9ece95b28fbd..cc028bf5cbf8 100644 --- a/src/bgen/Models/MemberInformation.cs +++ b/src/bgen/Models/MemberInformation.cs @@ -54,7 +54,7 @@ public class MemberInformation { // know whether the code is to call the internal static method implementing a // protocol member (in which case this property is true). See also is_protocol_implementation_method. public bool call_protocol_implementation_method; - public string is_forced_owns; + public bool is_forced_owns; public bool is_bindAs => Generator.HasBindAsAttribute (mi); public bool generate_is_async_overload; diff --git a/tests/generator/BGenTests.cs b/tests/generator/BGenTests.cs index f13ef8f40a4c..b8616ca3c531 100644 --- a/tests/generator/BGenTests.cs +++ b/tests/generator/BGenTests.cs @@ -1761,5 +1761,43 @@ public void DelegatesWithPointerTypes (Profile profile) Assert.IsTrue (p.ParameterType.IsPointer, $"Pointer parameter type: {p.Name}"); } } + + [Test] + [TestCase (Profile.iOS)] + public void ReleaseAttribute (Profile profile) + { + Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ()); + var bgen = BuildFile (profile, "tests/release-attribute.cs"); + bgen.AssertNoWarnings (); + + var passesOwnsEqualsTrue = new Func ((method) => { + foreach (var ins in method.Body.Instructions) { + switch (ins.OpCode.Code) { + case Code.Call: + case Code.Calli: + case Code.Callvirt: + var mr = (MethodReference) ins.Operand; + switch (mr.Name) { + case "GetINativeObject": + case "GetNSObject": + case "FromHandle": + var prev = ins.Previous; + return prev.OpCode.Code == Code.Ldc_I4_1; + } + break; + } + } + return false; + }); + + // The last argument in the call to GetNSObject, GetINativeObject or FromHandle (or any other object-creating methods) must be 'true'. + var methods = bgen.ApiAssembly.MainModule.GetType ("NS", "ReleaseAttributeTest").Methods + .Where ((v) => !v.IsConstructor) + .Where (v => v.Name != "get_ClassHandle"); + Assert.Multiple (() => { + foreach (var method in methods) + Assert.True (passesOwnsEqualsTrue (method), method.Name); + }); + } } } diff --git a/tests/generator/tests/release-attribute.cs b/tests/generator/tests/release-attribute.cs new file mode 100644 index 000000000000..c2133234fbed --- /dev/null +++ b/tests/generator/tests/release-attribute.cs @@ -0,0 +1,22 @@ +using System; + +using CoreVideo; +using Foundation; +using ObjCRuntime; + +namespace NS { + [BaseType (typeof (NSObject))] + interface ReleaseAttributeTest { + [Export ("getNSObject")] + [return: Release] + NSObject GetNSObject (); + + [Export ("getNativeObject")] + [return: Release] + CVPixelBuffer GetNativeObject (); + + [Export ("getINativeObject")] + [return: Release] + Selector GetINativeObject (); + } +}