Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/AddressBook/ABRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion src/AudioToolbox/MusicSequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ protected override void Dispose (bool disposing)
static readonly Dictionary<IntPtr, WeakReference> sequenceMap = new Dictionary<IntPtr, WeakReference> (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)) {
Expand All @@ -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;
}
Expand Down
12 changes: 12 additions & 0 deletions src/Foundation/NSArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,18 @@ static public T [] ArrayFromHandleFunc<T> (NativeHandle handle, Func<NativeHandl
return ret;
}

/// <summary>Create a managed array from a pointer to a native NSArray instance.</summary>
/// <param name="handle">The pointer to the native NSArray instance.</param>
/// <param name="createObject">A callback that returns an instance of the type T for a given pointer (for an element in the NSArray).</param>
/// <param name="releaseHandle">Whether the native NSArray instance should be released before returning or not.</param>
public static T [] ArrayFromHandleFunc<T> (NativeHandle handle, Func<NativeHandle, T> createObject, bool releaseHandle)
{
var rv = ArrayFromHandleFunc<T> (handle, createObject);
if (releaseHandle && handle != NativeHandle.Zero)
NSObject.DangerousRelease (handle);
return rv;
}

static public T [] ArrayFromHandle<T> (NativeHandle handle, Converter<NativeHandle, T> creator)
{
if (handle == NativeHandle.Zero)
Expand Down
2 changes: 1 addition & 1 deletion src/MediaToolbox/MTAudioProcessingTap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
27 changes: 25 additions & 2 deletions src/ObjCRuntime/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1830,6 +1830,18 @@ static IntPtr CreateNSObject (IntPtr type_gchandle, IntPtr handle, NSObject.Flag
{
return GetNSObject ((IntPtr) ptr, MissingCtorResolution.ThrowConstructor1NotFound);
}

/// <summary>Wraps an unmanaged <see cref="NativeHandle" /> into a fully typed <see cref="NSObject" />, or returns an existing wrapper object if one already exists.</summary>
/// <param name="ptr">A pointer to an unmanaged <see cref="NSObject" /> or any class that derives from the Objective-C NSObject class.</param>
/// <param name="owns">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).</param>
/// <returns>An instance of a class that derives <see cref="NSObject" />.</returns>
/// <remarks>
/// <para>The runtime create an instance of the most derived managed class.</para>
/// </remarks>
public static NSObject? GetNSObject (NativeHandle ptr, bool owns)
{
return GetNSObject ((IntPtr) ptr, owns, MissingCtorResolution.ThrowConstructor1NotFound);
}
#endif

public static NSObject? GetNSObject (IntPtr ptr)
Expand All @@ -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<T> (IntPtr ptr) where T : NSObject
Expand Down
9 changes: 9 additions & 0 deletions src/ObjCRuntime/Selector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ internal static string GetName (IntPtr handle)
return new Selector (sel, false);
}

/// <summary>Creates a managed Selector instance from a native selector.</summary>
/// <param name="selector">The native selector handle.</param>
/// <param name="owns">Whether the caller owns the native selector handle or not.</param>
/// <remarks>It's not possible to free a selector, so the <paramref name="owns" /> parameter is ignored.</remarks>
public static Selector? FromHandle (NativeHandle selector, bool owns)
{
return FromHandle (selector);
}

public static Selector Register (NativeHandle handle)
{
return new Selector (handle);
Expand Down
1 change: 1 addition & 0 deletions src/bgen/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public RetainAttribute (string wrap)
public string WrapName { get; set; }
}

[AttributeUsage (AttributeTargets.ReturnValue, AllowMultiple = false)]
public class ReleaseAttribute : Attribute {
}

Expand Down
2 changes: 2 additions & 0 deletions src/bgen/Caches/TypeCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -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");
Expand Down
45 changes: 16 additions & 29 deletions src/bgen/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2886,22 +2888,23 @@ 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 == 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) {
cast_a = "(" + TypeManager.FormatType (minfo?.type ?? mi.DeclaringType, mi.ReturnType) + ") ";
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);
Expand All @@ -2915,27 +2918,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 = "";
Expand All @@ -2951,19 +2954,19 @@ 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 {
if (NamespaceCache.NamespacesThatConflictWithTypes.Contains (etype.Namespace))
cast_a = "CFArray.ArrayFromHandle<global::" + etype + ">(";
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) ";
Expand Down Expand Up @@ -3650,22 +3653,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<PostSnippetAttribute> (mi);

if (disposes.Length > 0)
Expand Down
4 changes: 2 additions & 2 deletions src/bgen/Models/MarshalType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<global::{t.FullName}> (";
ClosingCreate = ", false)!";
ClosingCreate = ", %OWNS%)!";
} else {
CreateFromRet = create;
ClosingCreate = closingCreate;
Expand Down
16 changes: 8 additions & 8 deletions src/bgen/Models/MarshalTypeList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public class MarshalTypeList : List<MarshalType> {

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 ("));
Expand Down Expand Up @@ -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<CVPixelBuffer> (", closingCreate: ", false)!"));
Add (new MarshalType (typeCache.CVPixelBuffer!, create: "Runtime.GetINativeObject<CVPixelBuffer> (", closingCreate: ", %OWNS%)!"));
}
Add (typeCache.CGLayer);
if (frameworks.HaveCoreMedia)
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
38 changes: 38 additions & 0 deletions tests/generator/BGenTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MethodDefinition, bool> ((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);
});
}
}
}
Loading