diff --git a/docs/api/ObjCRuntime/BlockLiteral.xml b/docs/api/ObjCRuntime/BlockLiteral.xml
deleted file mode 100644
index 3f56d3e142bb..000000000000
--- a/docs/api/ObjCRuntime/BlockLiteral.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
- Wraps a ECMA CLI delegate (C# lambdas, anonymous methods or delegates) as an Objective-C block.
-
-
- This is a low-level class that is automatically used by the Xamarin.iOS bindings when using Objective-C block APIs.
-
-
- In the C#/ECMA CLI world delegates are automatically turned into blocks that can be consumed by Objective-C blocks-aware APIs.
-
-
- If you need to P/Invoke a native C method that takes a block
- parameter, you would need to manually setup the BlockLiteral
- object and declare a proxy method that is invoked by the block
- handler and will invoke your managed code.
-
-
-
- (block);
- if (callback != null)
- callback (offset, count);
-}
-
-[DllImport ("YourLibrary")]
-static extern void SetupHandler (ref BlockLiteral block);
-
-public void SetupHandler (SetupHandlerCallback callback)
-{
- if (callback == null)
- throw new ArgumentNullException (nameof (callback));
- BlockLiteral block = new BlockLiteral ();
- block.SetupBlock (static_handler, callback);
- try {
- SetupHandler (ref block);
- } finally {
- block.CleanupBlock ();
- }
-}
-]]>
-
-
-
-
\ No newline at end of file
diff --git a/src/ImageIO/CGImageMetadata.cs b/src/ImageIO/CGImageMetadata.cs
index ba8540daea86..da876d360032 100644
--- a/src/ImageIO/CGImageMetadata.cs
+++ b/src/ImageIO/CGImageMetadata.cs
@@ -143,6 +143,8 @@ static byte TagEnumerator (IntPtr block, NativeHandle key, NativeHandle value)
var nsKey = Runtime.GetNSObject (key, false)!;
var nsValue = Runtime.GetINativeObject (value, false)!;
var del = BlockLiteral.GetTarget (block);
+ if (del is null)
+ return default;
return del (nsKey, nsValue) ? (byte) 1 : (byte) 0;
}
diff --git a/src/Metal/MTLDevice.cs b/src/Metal/MTLDevice.cs
index 453a1f30daac..a24dbbfaa543 100644
--- a/src/Metal/MTLDevice.cs
+++ b/src/Metal/MTLDevice.cs
@@ -113,8 +113,7 @@ public static IMTLDevice [] GetAllDevices (MTLDeviceNotificationHandler handler,
[UnmanagedCallersOnly]
public static unsafe void TrampolineNotificationHandler (IntPtr block, IntPtr device, IntPtr notifyName)
{
- var descriptor = (BlockLiteral*) block;
- var del = (MTLDeviceNotificationHandler) (descriptor->Target);
+ var del = BlockLiteral.GetTarget (block);
if (del is not null)
del ((IMTLDevice) Runtime.GetNSObject (device)!, (Foundation.NSString) Runtime.GetNSObject (notifyName)!);
}
diff --git a/src/MetalPerformanceShaders/MPSImageBatch.cs b/src/MetalPerformanceShaders/MPSImageBatch.cs
index e7024247ab30..15c0b4f995eb 100644
--- a/src/MetalPerformanceShaders/MPSImageBatch.cs
+++ b/src/MetalPerformanceShaders/MPSImageBatch.cs
@@ -88,7 +88,7 @@ public static nuint GetResourceSize (NSArray imageBatch)
[UnmanagedCallersOnly]
static unsafe nint InvokeIterator (IntPtr block, IntPtr image, nuint index)
{
- var del = (MPSImageBatchIterator) BlockLiteral.GetTarget (block);
+ var del = BlockLiteral.GetTarget (block);
if (del is not null) {
using var img = Runtime.GetNSObject (image)!;
return del (img, index);
diff --git a/src/ObjCRuntime/Blocks.cs b/src/ObjCRuntime/Blocks.cs
index 445a824bcc12..e492ab7307cc 100644
--- a/src/ObjCRuntime/Blocks.cs
+++ b/src/ObjCRuntime/Blocks.cs
@@ -34,8 +34,7 @@
using Xamarin.Bundler;
#endif
-// Disable until we get around to enable + fix any issues.
-#nullable disable
+#nullable enable
// http://clang.llvm.org/docs/Block-ABI-Apple.html
@@ -60,7 +59,48 @@ struct XamarinBlockDescriptor {
// followed by variable-length string (the signature)
}
- ///
+ /// Wraps an ECMA CLI delegate (C# lambdas, anonymous methods, or delegates) as an Objective-C block.
+ ///
+ ///
+ /// This is a low-level class that is automatically used when using Objective-C block APIs.
+ ///
+ ///
+ /// In the C#/ECMA CLI world, delegates are automatically turned into blocks that can be consumed by Objective-C block-aware APIs.
+ ///
+ ///
+ /// If you need to P/Invoke a native C method that takes a block parameter, you would need to manually setup the object and declare a proxy method that is invoked by the block handler and will invoke your managed code.
+ ///
+ ///
+ /// (block);
+ /// if (callback is not null)
+ /// callback (offset, count);
+ /// }
+ ///
+ /// [DllImport ("YourLibrary")]
+ /// unsafe static extern void SetupHandler (BlockLiteral* block);
+ ///
+ /// public void SetupHandler (SetupHandlerCallback callback)
+ /// {
+ /// if (callback is null)
+ /// throw new ArgumentNullException (nameof (callback));
+ /// delegate* unmanaged trampoline = &TrampolineHandler;
+ /// using var block = new BlockLiteral (trampoline, callback, GetType (), nameof (TrampolineHandler));
+ /// SetupHandler (&block);
+ /// }
+ /// ]]>
+ ///
+ ///
[StructLayout (LayoutKind.Sequential)]
#if XAMCORE_5_0
// Let's try to make this a ref struct in XAMCORE_5_0, that will mean blocks can't be boxed (which is good, because it would most likely result in broken code).
@@ -98,13 +138,13 @@ static IntPtr NSConcreteStackBlock {
///
/// Creates a block literal.
///
- /// A function pointer that will be called when the block is called. This function must have an [UnmanagedCallersOnly] attribute.
+ /// A function pointer that will be called when the block is called. This method must have an attribute.
/// A context object that can be retrieved from the trampoline. This is typically a delegate to the managed function to call.
/// The type where the trampoline is located.
/// The name of the trampoline method.
///
- /// The 'trampolineType' and 'trampolineMethod' must uniquely define the trampoline method (it will be looked up using reflection).
- /// If there are multiple methods with the same name, use the overload that takes a MethodInfo instead.
+ /// and must uniquely define the trampoline method (it is looked up using reflection).
+ /// If there are multiple methods with the same name, call instead.
///
public BlockLiteral (void* trampoline, object context, Type trampolineType, string trampolineMethod)
: this (trampoline, context, FindTrampoline (trampolineType, trampolineMethod))
@@ -114,9 +154,9 @@ public BlockLiteral (void* trampoline, object context, Type trampolineType, stri
///
/// Creates a block literal.
///
- /// A function pointer that will be called when the block is called. This function must have an [UnmanagedCallersOnly] attribute.
+ /// A function pointer that will be called when the block is called. This method must have an attribute.
/// A context object that can be retrieved from the trampoline. This is typically a delegate to the managed function to call.
- /// The MethodInfo instance corresponding with the trampoline method.
+ /// The instance corresponding to the trampoline method.
public BlockLiteral (void* trampoline, object context, MethodInfo trampolineMethod)
: this (trampoline, context, GetBlockSignature (trampoline, trampolineMethod))
{
@@ -125,7 +165,7 @@ public BlockLiteral (void* trampoline, object context, MethodInfo trampolineMeth
///
/// Creates a block literal.
///
- /// A function pointer that will be called when the block is called. This function must have an [UnmanagedCallersOnly] attribute.
+ /// A function pointer that will be called when the block is called. This method must have an attribute.
/// A context object that can be retrieved from the trampoline. This is typically a delegate to the managed function to call.
/// The Objective-C signature of the trampoline method.
public BlockLiteral (void* trampoline, object context, string trampolineSignature)
@@ -157,6 +197,16 @@ static MethodInfo FindTrampoline (Type trampolineType, string trampolineMethod)
return rv;
}
+ static string GetTypeName (Type? type)
+ {
+ return type?.FullName ?? type?.Name ?? "";
+ }
+
+ static string GetMethodName (MethodInfo method)
+ {
+ return GetTypeName (method.DeclaringType) + "." + method.Name;
+ }
+
[BindingImpl (BindingImplOptions.Optimizable)]
static string GetBlockSignature (void* trampoline, MethodInfo trampolineMethod)
{
@@ -166,17 +216,17 @@ static string GetBlockSignature (void* trampoline, MethodInfo trampolineMethod)
// Verify that there's at least one parameter, and it must be System.IntPtr, void* or ObjCRuntime.BlockLiteral*.
var parameters = trampolineMethod.GetParameters ();
if (parameters.Length < 1)
- throw ErrorHelper.CreateError (8048, Errors.MX8048 /* The trampoline method {0} must have at least one parameter. */, trampolineMethod.DeclaringType.FullName + "." + trampolineMethod.Name);
+ throw ErrorHelper.CreateError (8048, Errors.MX8048 /* The trampoline method {0} must have at least one parameter. */, GetMethodName (trampolineMethod));
var firstParameterType = parameters [0].ParameterType;
if (firstParameterType != typeof (IntPtr) &&
firstParameterType != typeof (void*) &&
firstParameterType != typeof (BlockLiteral*)) {
- throw ErrorHelper.CreateError (8049, Errors.MX8049 /* The first parameter in the trampoline method {0} must be either 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*'. */, trampolineMethod.DeclaringType.FullName + "." + trampolineMethod.Name);
+ throw ErrorHelper.CreateError (8049, Errors.MX8049 /* The first parameter in the trampoline method {0} must be either 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*'. */, GetMethodName (trampolineMethod));
}
// Verify that the method as an [UnmanagedCallersOnly] attribute
if (!trampolineMethod.IsDefined (typeof (UnmanagedCallersOnlyAttribute), false))
- throw ErrorHelper.CreateError (8051, Errors.MX8051 /* The trampoline method {0} must have an [UnmanagedCallersOnly] attribute. */, trampolineMethod.DeclaringType.FullName + "." + trampolineMethod.Name);
+ throw ErrorHelper.CreateError (8051, Errors.MX8051 /* The trampoline method {0} must have an [UnmanagedCallersOnly] attribute. */, GetMethodName (trampolineMethod));
// We need to get the signature of the target method, so that we can compute
// the ObjC signature correctly (the generated method that's actually
@@ -223,7 +273,10 @@ static bool TryGetUserDelegateType (MemberInfo provider, MethodInfo noUserDelega
{
var userDelegateType = provider.GetCustomAttribute ()?.UserDelegateType;
if (userDelegateType is not null) {
- userMethod = userDelegateType.GetMethod ("Invoke");
+ var invokeMethod = userDelegateType.GetMethod ("Invoke");
+ if (invokeMethod is null)
+ throw new InvalidOperationException ($"The user delegate type {GetTypeName (userDelegateType)} does not have an Invoke method.");
+ userMethod = invokeMethod;
return true;
} else {
userMethod = noUserDelegateTypeMethod;
@@ -288,11 +341,9 @@ void SetupFunctionPointerBlock (IntPtr invokeMethod, object context, byte [] utf
}
// trampoline must be static, and someone else needs to keep a ref to it
- /// The trampoline must be a static delegate. The developer's code must keep a reference to it.
- /// The user code to invoke.
- /// Sets up a block using a trampoline and a user delegate.
- ///
- ///
+ /// Sets up a block using a trampoline and a user delegate.
+ /// The trampoline must be a static delegate. The developer's code must keep a reference to this delegate.
+ /// The user code to invoke.
[EditorBrowsable (EditorBrowsableState.Never)]
public void SetupBlockUnsafe (Delegate trampoline, Delegate userDelegate)
{
@@ -300,11 +351,9 @@ public void SetupBlockUnsafe (Delegate trampoline, Delegate userDelegate)
}
// trampoline must be static, but it's not necessary to keep a ref to it
+ /// Sets up a block using a trampoline and a user delegate.
/// The trampoline must be a static delegate. Xamarin.iOS will automatically keep a reference to this delegate.
- /// The user code to invoke.
- /// Sets up a block using a trampoline and a user delegate.
- ///
- ///
+ /// The user code to invoke.
[EditorBrowsable (EditorBrowsableState.Never)]
public void SetupBlock (Delegate trampoline, Delegate userDelegate)
{
@@ -333,24 +382,28 @@ void VerifyBlockDelegates (Delegate trampoline, Delegate userDelegate)
// It should be enough to run this check in the simulator
var method = trampoline.Method;
if (!method.IsStatic)
- ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name} is not static.");
+ ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {GetMethodName (method)} is not static.");
var attrib = method.GetCustomAttribute (false);
if (attrib is null)
- ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name} does not have a [MonoPInvokeCallback] attribute.");
+ ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {GetMethodName (method)} does not have a [MonoPInvokeCallback] attribute.");
- Type delegateType = attrib.DelegateType;
+ var delegateType = attrib.DelegateType;
+ if (delegateType is null)
+ ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {GetMethodName (method)} has a [MonoPInvokeCallback] attribute with an invalid delegate type.");
var signatureMethod = delegateType.GetMethod ("Invoke");
+ if (signatureMethod is null)
+ ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {GetMethodName (method)} has a [MonoPInvokeCallback] attribute with an invalid delegate type ({GetTypeName (delegateType)}).");
if (method.ReturnType != signatureMethod.ReturnType)
- ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name}'s return type ({method.ReturnType.FullName}) does not match the return type of the delegate in its [MonoPInvokeCallback] attribute ({signatureMethod.ReturnType.FullName}).");
+ ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {GetMethodName (method)}'s return type ({method.ReturnType.FullName}) does not match the return type of the delegate in its [MonoPInvokeCallback] attribute ({signatureMethod.ReturnType.FullName}).");
var parameters = method.GetParameters ();
var signatureParameters = signatureMethod.GetParameters ();
if (parameters.Length != signatureParameters.Length)
- ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name}'s parameter count ({parameters.Length}) does not match the parameter count of the delegate in its [MonoPInvokeCallback] attribute ({signatureParameters.Length}).");
+ ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {GetMethodName (method)}'s parameter count ({parameters.Length}) does not match the parameter count of the delegate in its [MonoPInvokeCallback] attribute ({signatureParameters.Length}).");
for (int i = 0; i < parameters.Length; i++) {
if (parameters [i].ParameterType != signatureParameters [i].ParameterType)
- ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name}'s parameter #{i + 1}'s type ({parameters [i].ParameterType.FullName}) does not match the corresponding parameter type of the delegate in its [MonoPInvokeCallback] attribute ({signatureParameters [i].ParameterType.FullName}).");
+ ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {GetMethodName (method)}'s parameter #{i + 1}'s type ({parameters [i].ParameterType.FullName}) does not match the corresponding parameter type of the delegate in its [MonoPInvokeCallback] attribute ({signatureParameters [i].ParameterType.FullName}).");
}
}
#endif
@@ -358,14 +411,15 @@ void VerifyBlockDelegates (Delegate trampoline, Delegate userDelegate)
}
/// Releases the resources associated with this block.
- ///
- /// This releases the GCHandle that points to the user delegate.
- ///
+ ///
+ /// This releases the that points to the user delegate.
+ ///
public void CleanupBlock ()
{
Dispose ();
}
+ /// Releases the resources associated with this block.
public void Dispose ()
{
if (local_handle != IntPtr.Zero) {
@@ -389,22 +443,18 @@ public void Dispose ()
}
}
- ///
- /// This is the 'context' value that was specified when creating the BlockLiteral.
- ///
- public object Context {
+ /// Gets the context value that was specified when creating the .
+ /// The context value.
+ public object? Context {
get {
var handle = global_handle != IntPtr.Zero ? global_handle : local_handle;
return GCHandle.FromIntPtr (handle).Target;
}
}
- /// Returns the target object for the block.
- ///
- ///
- ///
- ///
- public object Target {
+ /// Gets the target object for the block.
+ /// The target object if this block wraps a managed delegate; otherwise the context value.
+ public object? Target {
get {
var target = Context;
var tuple = target as Tuple;
@@ -414,33 +464,33 @@ public object Target {
}
}
- /// Desired type to get, the delegate must be compatible with this type.
- /// This method supports the Xamarin.iOS runtime and is not intended for use by application developers.
- /// Returns a delegate of the given type that can be used to invoke the Objective-C block in the provided handle.
- ///
- ///
+ /// Returns a managed delegate of type that can invoke the native block.
+ /// The delegate type to create.
+ /// The delegate of type for this block.
+ /// This method supports the Xamarin.iOS runtime and is not intended for use by application developers.
public T GetDelegateForBlock () where T : System.MulticastDelegate
{
return Runtime.GetDelegateForBlock (invoke);
}
+ /// Returns the managed delegate represented by the specified block.
/// The type of the managed delegate to return.
- /// The pointer to the native block.
- /// If this block represents a managed delegate, this method will return that managed delegate.
- /// The managed delegate for this block.
- ///
- /// Behavior is undefined if this block does not represent a managed delegate.
- ///
- public unsafe static T GetTarget (IntPtr block) where T : System.MulticastDelegate
+ /// The pointer to the native block.
+ /// The managed delegate for this block, or if no managed delegate is available.
+ ///
+ /// Behavior is undefined if this block does not represent a managed delegate.
+ ///
+ public unsafe static T? GetTarget (IntPtr block) where T : System.MulticastDelegate
{
- return (T) ((BlockLiteral*) block)->Target;
+ var target = ((BlockLiteral*) block)->Target;
+ if (target is null)
+ return null;
+ return (T) target;
}
+ /// Determines whether the specified block wraps a managed delegate.
/// The pointer to the native block.
- /// This method determines whether a block is wrapping a managed delegate or if it's an Objective-C block.
- /// Returns true if the specified block contains a managed delegate.
- ///
- ///
+ /// if the block contains a managed delegate; otherwise, .
[EditorBrowsable (EditorBrowsableState.Never)]
public static bool IsManagedBlock (IntPtr block)
{
@@ -459,7 +509,7 @@ public static bool IsManagedBlock (IntPtr block)
[UnconditionalSuppressMessage ("", "IL2075", Justification = "The APIs this method tries to access are marked by other means, so this is linker-safe.")]
// IL2072: 'interfaceType' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Type.GetInterfaceMap(Type)'. The return value of method 'System.Type.GetInterfaces()' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
[UnconditionalSuppressMessage ("", "IL2072", Justification = "The APIs this method tries to access are marked by other means, so this is linker-safe.")]
- static Type GetDelegateProxyType (MethodInfo minfo, uint token_ref, out MethodInfo baseMethod)
+ static Type? GetDelegateProxyType (MethodInfo minfo, uint token_ref, out MethodInfo? baseMethod)
{
// Note that the code in this method doesn't necessarily work with NativeAOT, so assert that never happens by throwing an exception if using the managed static registrar (which is required for NativeAOT)
if (Runtime.IsManagedStaticRegistrar)
@@ -478,12 +528,15 @@ static Type GetDelegateProxyType (MethodInfo minfo, uint token_ref, out MethodIn
return ((DelegateProxyAttribute) delegateProxies [0]).DelegateType;
// We might be implementing a protocol, find any DelegateProxy attributes on the corresponding interface as well.
- string selector = null;
- foreach (var iface in minfo.DeclaringType.GetInterfaces ()) {
+ string? selector = null;
+ var declaringType = minfo.DeclaringType;
+ if (declaringType is null)
+ throw ErrorHelper.CreateError (8011, $"Unable to locate the delegate to block conversion attribute ([DelegateProxy]) for the return value for the method {GetMethodName (minfo)}. {Constants.PleaseFileBugReport}");
+ foreach (var iface in declaringType.GetInterfaces ()) {
if (!iface.IsDefined (typeof (ProtocolAttribute), false))
continue;
- var map = minfo.DeclaringType.GetInterfaceMap (iface);
+ var map = declaringType.GetInterfaceMap (iface);
for (int i = 0; i < map.TargetMethods.Length; i++) {
if (map.TargetMethods [i] == minfo) {
delegateProxies = map.InterfaceMethods [i].ReturnTypeCustomAttributes.GetCustomAttributes (typeof (DelegateProxyAttribute), false);
@@ -502,11 +555,11 @@ static Type GetDelegateProxyType (MethodInfo minfo, uint token_ref, out MethodIn
}
}
- throw ErrorHelper.CreateError (8011, $"Unable to locate the delegate to block conversion attribute ([DelegateProxy]) for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}. {Constants.PleaseFileBugReport}");
+ throw ErrorHelper.CreateError (8011, $"Unable to locate the delegate to block conversion attribute ([DelegateProxy]) for the return value for the method {GetMethodName (baseMethod ?? minfo)}. {Constants.PleaseFileBugReport}");
}
[BindingImpl (BindingImplOptions.Optimizable)]
- unsafe static IntPtr GetBlockForFunctionPointer (MethodInfo delegateInvokeMethod, object @delegate, string signature)
+ unsafe static IntPtr GetBlockForFunctionPointer (MethodInfo delegateInvokeMethod, object @delegate, string? signature)
{
void* invokeFunctionPointer = (void*) delegateInvokeMethod.MethodHandle.GetFunctionPointer ();
if (signature is null) {
@@ -523,7 +576,7 @@ unsafe static IntPtr GetBlockForFunctionPointer (MethodInfo delegateInvokeMethod
[EditorBrowsable (EditorBrowsableState.Never)]
[BindingImpl (BindingImplOptions.Optimizable)]
- static IntPtr CreateBlockForDelegate (Delegate @delegate, Delegate delegateProxyFieldValue, string /*?*/ signature)
+ static IntPtr CreateBlockForDelegate (Delegate @delegate, Delegate delegateProxyFieldValue, string? signature)
{
if (@delegate is null)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (@delegate));
@@ -555,7 +608,7 @@ static IntPtr CreateBlockForDelegate (Delegate @delegate, Delegate delegateProxy
// IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.NonPublicFields' in call to 'System.Type.GetField(String, BindingFlags)'. The return value of method 'ObjCRuntime.BlockLiteral.GetDelegateProxyType(MethodInfo, UInt32, MethodInfo&)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
// IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Type.GetMethod(String, BindingFlags)'. The return value of method 'ObjCRuntime.BlockLiteral.GetDelegateProxyType(MethodInfo, UInt32, MethodInfo&)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."
[UnconditionalSuppressMessage ("", "IL2075", Justification = "The APIs this method tries to access are marked by other means, so this is linker-safe.")]
- internal static IntPtr GetBlockForDelegate (MethodInfo minfo, object @delegate, uint token_ref, string signature)
+ internal static IntPtr GetBlockForDelegate (MethodInfo minfo, object? @delegate, uint token_ref, string? signature)
{
// Note that the code in this method doesn't necessarily work with NativeAOT, so assert that never happens by throwing an exception if using the managed static registrar (which is required for NativeAOT)
if (Runtime.IsManagedStaticRegistrar)
@@ -564,35 +617,35 @@ internal static IntPtr GetBlockForDelegate (MethodInfo minfo, object @delegate,
if (@delegate is null)
return IntPtr.Zero;
- if (!(@delegate is Delegate))
- throw ErrorHelper.CreateError (8016, $"Unable to convert delegate to block for the return value for the method {minfo.DeclaringType.FullName}.{minfo.Name}, because the input isn't a delegate, it's a {@delegate.GetType ().FullName}. {Constants.PleaseFileBugReport}");
+ if (@delegate is not Delegate managedDelegate)
+ throw ErrorHelper.CreateError (8016, $"Unable to convert delegate to block for the return value for the method {GetMethodName (minfo)}, because the input isn't a delegate, it's a {@delegate.GetType ().FullName}. {Constants.PleaseFileBugReport}");
if (Runtime.IsNativeAOT)
throw Runtime.CreateNativeAOTNotSupportedException ();
- Type delegateProxyType = GetDelegateProxyType (minfo, token_ref, out var baseMethod);
+ var delegateProxyType = GetDelegateProxyType (minfo, token_ref, out var baseMethod);
if (baseMethod is null)
baseMethod = minfo; // 'baseMethod' is only used in error messages, and if it's null, we just use the closest alternative we have (minfo).
if (delegateProxyType is null)
- throw ErrorHelper.CreateError (8012, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: DelegateType is null. {Constants.PleaseFileBugReport}");
+ throw ErrorHelper.CreateError (8012, $"Invalid DelegateProxyAttribute for the return value for the method {GetMethodName (baseMethod)}: DelegateType is null. {Constants.PleaseFileBugReport}");
var delegateInvokeMethod = delegateProxyType.GetMethod ("Invoke", BindingFlags.NonPublic | BindingFlags.Static);
if (delegateInvokeMethod is null)
- throw ErrorHelper.CreateError (8060, Errors.MX8060 /* Invalid DelegateProxyAttribute for the return value for the method {0}.{1}: No 'Invoke' method found. {Constants.PleaseFileBugReport} */, baseMethod.DeclaringType.FullName, baseMethod.Name);
+ throw ErrorHelper.CreateError (8060, Errors.MX8060 /* Invalid DelegateProxyAttribute for the return value for the method {0}.{1}: No 'Invoke' method found. {Constants.PleaseFileBugReport} */, GetTypeName (baseMethod.DeclaringType), baseMethod.Name);
if (delegateInvokeMethod.IsDefined (typeof (UnmanagedCallersOnlyAttribute), false))
- return GetBlockForFunctionPointer (delegateInvokeMethod, @delegate, signature);
+ return GetBlockForFunctionPointer (delegateInvokeMethod, managedDelegate, signature);
var delegateProxyField = delegateProxyType.GetField ("Handler", BindingFlags.NonPublic | BindingFlags.Static);
if (delegateProxyField is null)
- throw ErrorHelper.CreateError (8013, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: DelegateType ({delegateProxyType.FullName}) specifies a type without a 'Handler' field. {Constants.PleaseFileBugReport}");
+ throw ErrorHelper.CreateError (8013, $"Invalid DelegateProxyAttribute for the return value for the method {GetMethodName (baseMethod)}: DelegateType ({GetTypeName (delegateProxyType)}) specifies a type without a 'Handler' field. {Constants.PleaseFileBugReport}");
var handlerDelegate = delegateProxyField.GetValue (null);
if (handlerDelegate is null)
- throw ErrorHelper.CreateError (8014, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: The DelegateType's ({delegateProxyType.FullName}) 'Handler' field is null. {Constants.PleaseFileBugReport}");
+ throw ErrorHelper.CreateError (8014, $"Invalid DelegateProxyAttribute for the return value for the method {GetMethodName (baseMethod)}: The DelegateType's ({GetTypeName (delegateProxyType)}) 'Handler' field is null. {Constants.PleaseFileBugReport}");
- if (!(handlerDelegate is Delegate))
- throw ErrorHelper.CreateError (8015, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: The DelegateType's ({delegateProxyType.FullName}) 'Handler' field is not a delegate, it's a {handlerDelegate.GetType ().FullName}. {Constants.PleaseFileBugReport}");
+ if (handlerDelegate is not Delegate handler)
+ throw ErrorHelper.CreateError (8015, $"Invalid DelegateProxyAttribute for the return value for the method {GetMethodName (baseMethod)}: The DelegateType's ({GetTypeName (delegateProxyType)}) 'Handler' field is not a delegate, it's a {handlerDelegate.GetType ().FullName}. {Constants.PleaseFileBugReport}");
// We now have the information we need to create the block.
// Note that we must create a heap-allocated block, so we
@@ -602,12 +655,12 @@ internal static IntPtr GetBlockForDelegate (MethodInfo minfo, object @delegate,
using var block = new BlockLiteral ();
if (signature is null) {
if (Runtime.DynamicRegistrationSupported) {
- block.SetupBlock ((Delegate) handlerDelegate, (Delegate) @delegate);
+ block.SetupBlock (handler, managedDelegate);
} else {
- throw ErrorHelper.CreateError (8026, $"BlockLiteral.GetBlockForDelegate with a null signature is not supported when the dynamic registrar has been linked away (delegate type: {@delegate.GetType ().FullName}).");
+ throw ErrorHelper.CreateError (8026, $"BlockLiteral.GetBlockForDelegate with a null signature is not supported when the dynamic registrar has been linked away (delegate type: {managedDelegate.GetType ().FullName}).");
}
} else {
- block.SetupBlockImpl ((Delegate) handlerDelegate, (Delegate) @delegate, true, signature);
+ block.SetupBlockImpl (handler, managedDelegate, true, signature);
}
unsafe {
@@ -680,10 +733,10 @@ public void Add (IntPtr block)
#endif
/// Flags for the BlockLiteral enum.
- ///
- /// Xamarin.iOS as of version 12.0 only uses the flags BlockFlags.BLOCK_HAS_COPY_DISPOSE | BlockFlags.BLOCK_HAS_SIGNATURE for its blocks.
- /// See Block ABI for more detailed information about the Block ABI.
- ///
+ ///
+ /// Only the flags and are used for the blocks we create.
+ /// See Block ABI for more detailed information about the Block ABI.
+ ///
[Flags]
internal enum BlockFlags : int {
/// Objective-C Block ABI Flags.
diff --git a/src/Security/SecSharedCredential.cs b/src/Security/SecSharedCredential.cs
index ab96123e95fe..b26510622551 100644
--- a/src/Security/SecSharedCredential.cs
+++ b/src/Security/SecSharedCredential.cs
@@ -26,8 +26,7 @@ static internal class ActionTrampoline {
[UnmanagedCallersOnly]
internal static unsafe void Invoke (IntPtr block, IntPtr obj)
{
- var descriptor = (BlockLiteral*) block;
- var del = (global::System.Action) (descriptor->Target);
+ var del = BlockLiteral.GetTarget> (block);
if (del is not null) {
del (Runtime.GetNSObject (obj));
}
@@ -89,8 +88,7 @@ static internal class ArrayErrorActionTrampoline {
[UnmanagedCallersOnly]
internal static unsafe void Invoke (IntPtr block, IntPtr array, IntPtr err)
{
- var descriptor = (BlockLiteral*) block;
- var del = (global::System.Action) (descriptor->Target);
+ var del = BlockLiteral.GetTarget> (block);
if (del is not null)
del (Runtime.GetNSObject (array), Runtime.GetNSObject (err));
}
diff --git a/src/UIKit/UIAccessibility.cs b/src/UIKit/UIAccessibility.cs
index e18ac4545c21..5bed32f51ffc 100644
--- a/src/UIKit/UIAccessibility.cs
+++ b/src/UIKit/UIAccessibility.cs
@@ -261,8 +261,7 @@ public static Task RequestGuidedAccessSessionAsync (bool enable)
[UnmanagedCallersOnly]
static unsafe void TrampolineRequestGuidedAccessSession (IntPtr block, byte enable)
{
- var descriptor = (BlockLiteral*) block;
- var del = (Action) (descriptor->Target);
+ var del = BlockLiteral.GetTarget> (block);
if (del is not null)
del (enable != 0);
}
diff --git a/src/UIKit/UICellAccessory.cs b/src/UIKit/UICellAccessory.cs
index 9073917fada5..2b185775d881 100644
--- a/src/UIKit/UICellAccessory.cs
+++ b/src/UIKit/UICellAccessory.cs
@@ -64,8 +64,9 @@ static internal class SDUICellAccessoryPosition {
[UnmanagedCallersOnly]
static unsafe nuint Invoke (IntPtr block, IntPtr accessories)
{
- var descriptor = (BlockLiteral*) block;
- var del = (UICellAccessoryPosition) (descriptor->Target);
+ var del = BlockLiteral.GetTarget (block);
+ if (del is null)
+ return default;
nuint retval = del (NSArray.ArrayFromHandle (accessories));
return retval;
}
diff --git a/src/UIKit/UIConfigurationColorTransformer.cs b/src/UIKit/UIConfigurationColorTransformer.cs
index 40dd38ad64f8..5d75a8b1c7e0 100644
--- a/src/UIKit/UIConfigurationColorTransformer.cs
+++ b/src/UIKit/UIConfigurationColorTransformer.cs
@@ -43,8 +43,7 @@ static internal class SDUIConfigurationColorTransformerHandler {
[UnmanagedCallersOnly]
static unsafe IntPtr Invoke (IntPtr block, IntPtr color)
{
- var descriptor = (BlockLiteral*) block;
- var del = (UIConfigurationColorTransformerHandler) (descriptor->Target);
+ var del = BlockLiteral.GetTarget (block);
var retval = del is null ? null : del (Runtime.GetNSObject (color)!);
return Runtime.RetainAndAutoreleaseNSObject (retval);
}
diff --git a/src/UIKit/UIGuidedAccessRestriction.cs b/src/UIKit/UIGuidedAccessRestriction.cs
index 8bb93eae06b5..d641d4c22138 100644
--- a/src/UIKit/UIGuidedAccessRestriction.cs
+++ b/src/UIKit/UIGuidedAccessRestriction.cs
@@ -50,8 +50,7 @@ static internal class UIGuidedAccessConfigureAccessibilityFeaturesTrampoline {
[UnmanagedCallersOnlyAttribute]
internal static unsafe void Invoke (IntPtr block, byte success, IntPtr error)
{
- var descriptor = (BlockLiteral*) block;
- var del = (UIGuidedAccessConfigureAccessibilityFeaturesCompletionHandler) (descriptor->Target);
+ var del = BlockLiteral.GetTarget (block);
if (del is not null)
del (success != 0, Runtime.GetNSObject (error));
}
diff --git a/src/bgen/Generator.cs b/src/bgen/Generator.cs
index 9c25f6ad3521..ea5ada12ea96 100644
--- a/src/bgen/Generator.cs
+++ b/src/bgen/Generator.cs
@@ -1620,6 +1620,10 @@ void GenerateTrampolinesForQueue (TrampolineInfo [] queue)
}
print ("}");
} else {
+ print ("if (del is null)");
+ indent++;
+ print ("return default;");
+ indent--;
if (ti.Convert.Length > 0)
print (ti.Convert);
print ("var retval = del ({1});", ti.DelegateReturnType, ti.Invoke);
diff --git a/tests/cecil-tests/Documentation.KnownFailures.txt b/tests/cecil-tests/Documentation.KnownFailures.txt
index af25cae37ccc..0a3dc185681b 100644
--- a/tests/cecil-tests/Documentation.KnownFailures.txt
+++ b/tests/cecil-tests/Documentation.KnownFailures.txt
@@ -15214,7 +15214,6 @@ M:ObjCRuntime.AssemblyRegistrationEventArgs.#ctor
M:ObjCRuntime.BaseWrapper.#ctor(ObjCRuntime.NativeHandle,System.Boolean)
M:ObjCRuntime.BaseWrapper.Release
M:ObjCRuntime.BaseWrapper.Retain
-M:ObjCRuntime.BlockLiteral.Dispose
M:ObjCRuntime.Class.#ctor(ObjCRuntime.NativeHandle)
M:ObjCRuntime.Class.Equals(ObjCRuntime.Class)
M:ObjCRuntime.Class.Equals(System.Object)