Skip to content

Commit

Permalink
Add support for function pointers to BlockLiteral. (#17672)
Browse files Browse the repository at this point in the history
Add support for function pointers to BlockLiteral, and use it to update
almost all manually bound block code to use function pointers (in .NET).

Also add support to the linker for optimizing the new block API.

Contributes towards #15783.
  • Loading branch information
rolfbjarne committed Mar 6, 2023
1 parent f15a903 commit e013c10
Show file tree
Hide file tree
Showing 46 changed files with 1,321 additions and 34 deletions.
9 changes: 9 additions & 0 deletions src/AddressBook/ABAddressBook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,24 @@ public void RequestAccess (Action<bool, NSError?> onCompleted)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (onCompleted));

unsafe {
#if NET
delegate* unmanaged<IntPtr, byte, IntPtr, void> trampoline = &TrampolineCompletionHandler;
using var block = new BlockLiteral (trampoline, onCompleted, typeof (ABAddressBook), nameof (TrampolineCompletionHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (static_completionHandler, onCompleted);
#endif
ABAddressBookRequestAccessWithCompletion (Handle, &block);
}
}

#if !NET
internal delegate void InnerCompleted (IntPtr block, byte success, IntPtr error);
static readonly InnerCompleted static_completionHandler = TrampolineCompletionHandler;
[MonoPInvokeCallback (typeof (InnerCompleted))]
#else
[UnmanagedCallersOnly]
#endif
static unsafe void TrampolineCompletionHandler (IntPtr block, byte success, IntPtr error)
{
var descriptor = (BlockLiteral*) block;
Expand Down
9 changes: 9 additions & 0 deletions src/AudioUnit/AudioComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -584,11 +584,15 @@ public AudioUnit CreateAudioUnit ()
#endif
public AudioComponentValidationResult Validate (NSDictionary? validationParameters = null) => Validate (validationParameters, out var _);

#if !NET
delegate void TrampolineCallback (IntPtr blockPtr, AudioComponentValidationResult result, IntPtr dictionary);

static unsafe readonly TrampolineCallback static_action = TrampolineAction;

[MonoPInvokeCallback (typeof (TrampolineCallback))]
#else
[UnmanagedCallersOnly]
#endif
static void TrampolineAction (IntPtr blockPtr, AudioComponentValidationResult result, IntPtr dictionary)
{
var del = BlockLiteral.GetTarget<Action<AudioComponentValidationResult, NSDictionary?>> (blockPtr);
Expand Down Expand Up @@ -628,8 +632,13 @@ static void TrampolineAction (IntPtr blockPtr, AudioComponentValidationResult re
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (onCompletion));

unsafe {
#if NET
delegate* unmanaged<IntPtr, AudioComponentValidationResult, IntPtr, void> trampoline = &TrampolineAction;
using var block = new BlockLiteral (trampoline, onCompletion, typeof (AudioComponent), nameof (TrampolineAction));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (static_action, onCompletion);
#endif
resultCode = AudioComponentValidateWithResults (GetCheckedHandle (), validationParameters.GetHandle (), &block);
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/CoreFoundation/DispatchIO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,14 @@ internal DispatchIO (NativeHandle handle) : this (handle, false)
}
#endif

#if !NET
delegate void DispatchReadWrite (IntPtr block, IntPtr dispatchData, int error);
static DispatchReadWrite static_DispatchReadWriteHandler = Trampoline_DispatchReadWriteHandler;

[MonoPInvokeCallback (typeof (DispatchReadWrite))]
#else
[UnmanagedCallersOnly]
#endif
static void Trampoline_DispatchReadWriteHandler (IntPtr block, IntPtr dispatchData, int error)
{
var del = BlockLiteral.GetTarget<DispatchIOHandler> (block);
Expand All @@ -95,8 +99,13 @@ public static void Read (int fd, nuint size, DispatchQueue dispatchQueue, Dispat
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (dispatchQueue));

unsafe {
#if NET
delegate* unmanaged<IntPtr, IntPtr, int, void> trampoline = &Trampoline_DispatchReadWriteHandler;
using var block = new BlockLiteral (trampoline, handler, typeof (DispatchIO), nameof (Trampoline_DispatchReadWriteHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (static_DispatchReadWriteHandler, handler);
#endif
dispatch_read (fd, size, dispatchQueue.Handle, &block);
}
}
Expand All @@ -115,8 +124,13 @@ public static void Write (int fd, DispatchData dispatchData, DispatchQueue dispa
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (dispatchQueue));

unsafe {
#if NET
delegate* unmanaged<IntPtr, IntPtr, int, void> trampoline = &Trampoline_DispatchReadWriteHandler;
using var block = new BlockLiteral (trampoline, handler, typeof (DispatchIO), nameof (Trampoline_DispatchReadWriteHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (static_DispatchReadWriteHandler, handler);
#endif
dispatch_write (fd, dispatchData.Handle, dispatchQueue.Handle, &block);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/CoreGraphics/CGPDFArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,15 @@ public bool GetString (int idx, out string? result)
}
#endif

#if !NET
delegate byte ApplyBlockHandlerDelegate (IntPtr block, nint index, IntPtr value, IntPtr info);
static readonly ApplyBlockHandlerDelegate applyblock_handler = ApplyBlockHandler;

#if !MONOMAC
[MonoPInvokeCallback (typeof (ApplyBlockHandlerDelegate))]
#endif
#else
[UnmanagedCallersOnly]
#endif
static byte ApplyBlockHandler (IntPtr block, nint index, IntPtr value, IntPtr info)
{
Expand Down Expand Up @@ -270,8 +274,13 @@ public bool Apply (ApplyCallback callback, object? info = null)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (callback));

unsafe {
#if NET
delegate* unmanaged<IntPtr, nint, IntPtr, IntPtr, byte> trampoline = &ApplyBlockHandler;
using var block = new BlockLiteral (trampoline, callback, typeof (CGPDFArray), nameof (ApplyBlockHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (applyblock_handler, callback);
#endif
var gc_handle = info is null ? default (GCHandle) : GCHandle.Alloc (info);
try {
return CGPDFArrayApplyBlock (Handle, &block, info is null ? IntPtr.Zero : GCHandle.ToIntPtr (gc_handle));
Expand Down
40 changes: 40 additions & 0 deletions src/CoreText/CTFontManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,14 @@ static NSArray EnsureNonNullArray (object [] items, string name)
#endif
public delegate bool CTFontRegistrationHandler (NSError [] errors, bool done);

#if !NET
internal delegate byte InnerRegistrationHandler (IntPtr block, IntPtr errors, byte done);
static readonly InnerRegistrationHandler callback = TrampolineRegistrationHandler;

[MonoPInvokeCallback (typeof (InnerRegistrationHandler))]
#else
[UnmanagedCallersOnly]
#endif
static unsafe byte TrampolineRegistrationHandler (IntPtr block, /* NSArray */ IntPtr errors, byte done)
{
var del = BlockLiteral.GetTarget<CTFontRegistrationHandler> (block);
Expand Down Expand Up @@ -281,8 +285,13 @@ public static void RegisterFonts (NSUrl [] fontUrls, CTFontManagerScope scope, b
}
} else {
unsafe {
#if NET
delegate* unmanaged<IntPtr, IntPtr, byte, byte> trampoline = &TrampolineRegistrationHandler;
using var block = new BlockLiteral (trampoline, registrationHandler, typeof (CTFontManager), nameof (TrampolineRegistrationHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (callback, registrationHandler);
#endif
CTFontManagerRegisterFontURLs (arr.Handle, scope, enabled, &block);
}
}
Expand Down Expand Up @@ -385,8 +394,13 @@ public unsafe static void UnregisterFonts (NSUrl [] fontUrls, CTFontManagerScope
if (registrationHandler is null) {
CTFontManagerUnregisterFontURLs (arr.Handle, scope, null);
} else {
#if NET
delegate* unmanaged<IntPtr, IntPtr, byte, byte> trampoline = &TrampolineRegistrationHandler;
using var block = new BlockLiteral (trampoline, registrationHandler, typeof (CTFontManager), nameof (TrampolineRegistrationHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (callback, registrationHandler);
#endif
CTFontManagerUnregisterFontURLs (arr.Handle, scope, &block);
}
}
Expand Down Expand Up @@ -548,8 +562,13 @@ public unsafe static void RegisterFontDescriptors (CTFontDescriptor [] fontDescr
if (registrationHandler is null) {
CTFontManagerRegisterFontDescriptors (arr.Handle, scope, enabled, null);
} else {
#if NET
delegate* unmanaged<IntPtr, IntPtr, byte, byte> trampoline = &TrampolineRegistrationHandler;
using var block = new BlockLiteral (trampoline, registrationHandler, typeof (CTFontManager), nameof (TrampolineRegistrationHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (callback, registrationHandler);
#endif
CTFontManagerRegisterFontDescriptors (arr.Handle, scope, enabled, &block);
}
}
Expand Down Expand Up @@ -587,8 +606,13 @@ public unsafe static void UnregisterFontDescriptors (CTFontDescriptor [] fontDes
if (registrationHandler is null) {
CTFontManagerUnregisterFontDescriptors (arr.Handle, scope, null);
} else {
#if NET
delegate* unmanaged<IntPtr, IntPtr, byte, byte> trampoline = &TrampolineRegistrationHandler;
using var block = new BlockLiteral (trampoline, registrationHandler, typeof (CTFontManager), nameof (TrampolineRegistrationHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (callback, registrationHandler);
#endif
CTFontManagerUnregisterFontDescriptors (arr.Handle, scope, &block);
}
}
Expand Down Expand Up @@ -709,8 +733,13 @@ public unsafe static void RegisterFonts (string[] assetNames, CFBundle bundle, C
if (registrationHandler is null) {
CTFontManagerRegisterFontsWithAssetNames (arr.Handle, bundle.GetHandle (), scope, enabled, null);
} else {
#if NET
delegate* unmanaged<IntPtr, IntPtr, byte, byte> trampoline = &TrampolineRegistrationHandler;
using var block = new BlockLiteral (trampoline, registrationHandler, typeof (CTFontManager), nameof (TrampolineRegistrationHandler));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (callback, registrationHandler);
#endif
CTFontManagerRegisterFontsWithAssetNames (arr.Handle, bundle.GetHandle (), scope, enabled, &block);
}
}
Expand Down Expand Up @@ -743,10 +772,16 @@ public unsafe static void RegisterFonts (string[] assetNames, CFBundle bundle, C
[DllImport (Constants.CoreTextLibrary)]
static extern unsafe void CTFontManagerRequestFonts (/* CFArrayRef */ IntPtr fontDescriptors, BlockLiteral* completionHandler);

#if !NET
internal delegate void InnerRequestFontsHandler (IntPtr block, IntPtr fontDescriptors);
static readonly InnerRequestFontsHandler requestCallback = TrampolineRequestFonts;
#endif

#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (InnerRequestFontsHandler))]
#endif
static unsafe void TrampolineRequestFonts (IntPtr block, /* CFArray */ IntPtr fontDescriptors)
{
var del = BlockLiteral.GetTarget<CTFontManagerRequestFontsHandler> (block);
Expand All @@ -773,8 +808,13 @@ public static void RequestFonts (CTFontDescriptor[] fontDescriptors, CTFontManag

using (var arr = EnsureNonNullArray (fontDescriptors, nameof (fontDescriptors))) {
unsafe {
#if NET
delegate* unmanaged<IntPtr, IntPtr, void> trampoline = &TrampolineRequestFonts;
using var block = new BlockLiteral (trampoline, completionHandler, typeof (CTFontManager), nameof (TrampolineRequestFonts));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (requestCallback, completionHandler);
#endif
CTFontManagerRequestFonts (arr.Handle, &block);
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/CoreText/CTLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ public nfloat GetOffsetForStringIndex (nint charIndex)
}

public delegate void CaretEdgeEnumerator (double offset, nint charIndex, bool leadingEdge, ref bool stop);
#if !NET
unsafe delegate void CaretEdgeEnumeratorProxy (IntPtr block, double offset, nint charIndex, byte leadingEdge, byte* stop);
#endif

#if NET
[SupportedOSPlatform ("ios9.0")]
Expand All @@ -222,9 +224,13 @@ public nfloat GetOffsetForStringIndex (nint charIndex)
[DllImport (Constants.CoreTextLibrary)]
unsafe static extern void CTLineEnumerateCaretOffsets (IntPtr line, BlockLiteral* blockEnumerator);

#if !NET
static unsafe readonly CaretEdgeEnumeratorProxy static_enumerate = TrampolineEnumerate;

[MonoPInvokeCallback (typeof (CaretEdgeEnumeratorProxy))]
#else
[UnmanagedCallersOnly]
#endif
unsafe static void TrampolineEnumerate (IntPtr blockPtr, double offset, nint charIndex, byte leadingEdge, byte* stopPointer)
{
var del = BlockLiteral.GetTarget<CaretEdgeEnumerator> (blockPtr);
Expand All @@ -251,8 +257,13 @@ public void EnumerateCaretOffsets (CaretEdgeEnumerator enumerator)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (enumerator));

unsafe {
#if NET
delegate* unmanaged<IntPtr, double, nint, byte, byte*, void> trampoline = &TrampolineEnumerate;
using var block = new BlockLiteral (trampoline, enumerator, typeof (CTLine), nameof (TrampolineEnumerate));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (static_enumerate, enumerator);
#endif
CTLineEnumerateCaretOffsets (Handle, &block);
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/ImageIO/CGImageAnimation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,13 @@ public static CGImageAnimationStatus AnimateImage (NSUrl url, CGImageAnimationOp
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (handler));

unsafe {
#if NET
delegate* unmanaged<IntPtr, nint, IntPtr, byte*, void> trampoline = &SDCGImageSourceAnimationBlock.Invoke;
using var block = new BlockLiteral (trampoline, handler, typeof (SDCGImageSourceAnimationBlock), nameof (SDCGImageSourceAnimationBlock.Invoke));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (SDCGImageSourceAnimationBlock.Handler, handler);
#endif
return CGAnimateImageAtURLWithBlock (url.Handle, options.GetHandle (), &block);
}
#endif
Expand Down Expand Up @@ -109,8 +114,13 @@ public static CGImageAnimationStatus AnimateImage (NSData data, CGImageAnimation
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (handler));

unsafe {
#if NET
delegate* unmanaged<IntPtr, nint, IntPtr, byte*, void> trampoline = &SDCGImageSourceAnimationBlock.Invoke;
using var block = new BlockLiteral (trampoline, handler, typeof (SDCGImageSourceAnimationBlock), nameof (SDCGImageSourceAnimationBlock.Invoke));
#else
using var block = new BlockLiteral ();
block.SetupBlockUnsafe (SDCGImageSourceAnimationBlock.Handler, handler);
#endif
return CGAnimateImageDataWithBlock (data.Handle, options.GetHandle (), &block);
}
#endif
Expand All @@ -120,9 +130,13 @@ public static CGImageAnimationStatus AnimateImage (NSData data, CGImageAnimation
// This class bridges native block invocations that call into C#
//
static internal class SDCGImageSourceAnimationBlock {
#if !NET
unsafe static internal readonly DCGImageSourceAnimationBlock Handler = Invoke;

[MonoPInvokeCallback (typeof (DCGImageSourceAnimationBlock))]
#else
[UnmanagedCallersOnly]
#endif
internal unsafe static void Invoke (IntPtr block, nint index, IntPtr image, byte* stop)
{
var del = BlockLiteral.GetTarget<CGImageSourceAnimationHandler> (block);
Expand All @@ -134,9 +148,11 @@ internal unsafe static void Invoke (IntPtr block, nint index, IntPtr image, byte
}
} /* class SDCGImageSourceAnimationBlock */

#if !NET
[UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)]
[UserDelegateType (typeof (CGImageSourceAnimationHandler))]
unsafe internal delegate void DCGImageSourceAnimationBlock (IntPtr block, nint index, IntPtr imageHandle, byte* stop);
#endif
}

}
11 changes: 11 additions & 0 deletions src/ImageIO/CGImageMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,13 @@ public CGImageMetadata (NSData data)
extern unsafe static void CGImageMetadataEnumerateTagsUsingBlock (/* CGImageMetadataRef __nonnull */ IntPtr metadata,
/* CFStringRef __nullable */ IntPtr rootPath, /* CFDictionaryRef __nullable */ IntPtr options, BlockLiteral* block);

#if !NET
delegate byte TrampolineCallback (IntPtr blockPtr, NativeHandle key, NativeHandle value);

[MonoPInvokeCallback (typeof (TrampolineCallback))]
#else
[UnmanagedCallersOnly]
#endif
static byte TagEnumerator (IntPtr block, NativeHandle key, NativeHandle value)
{
var nsKey = Runtime.GetNSObject<NSString> (key, false)!;
Expand All @@ -133,15 +137,22 @@ static byte TagEnumerator (IntPtr block, NativeHandle key, NativeHandle value)
return del (nsKey, nsValue) ? (byte) 1 : (byte) 0;
}

#if !NET
static unsafe readonly TrampolineCallback static_action = TagEnumerator;
#endif

[BindingImpl (BindingImplOptions.Optimizable)]
public void EnumerateTags (NSString? rootPath, CGImageMetadataEnumerateOptions? options, CGImageMetadataTagBlock block)
{
using var o = options?.ToDictionary ();
unsafe {
#if NET
delegate* unmanaged<IntPtr, NativeHandle, NativeHandle, byte> trampoline = &TagEnumerator;
using var block_handler = new BlockLiteral (trampoline, block, typeof (CGImageMetadata), nameof (TagEnumerator));
#else
using var block_handler = new BlockLiteral ();
block_handler.SetupBlockUnsafe (static_action, block);
#endif
CGImageMetadataEnumerateTagsUsingBlock (Handle, rootPath.GetHandle (), o.GetHandle (), &block_handler);
}
}
Expand Down

5 comments on commit e013c10

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ API diff for current PR / commit

Legacy Xamarin (No breaking changes)
.NET (No breaking changes)

✅ API diff vs stable

Legacy Xamarin (No breaking changes)
.NET (No breaking changes)
Legacy Xamarin (stable) vs .NET

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: e013c10a30b400a9ef690293d1f40ca1b3a623e2 [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ [CI Build] Tests on macOS M1 - Mac Ventura (13.0) failed ❌

Failed tests are:

  • xammac_tests

Pipeline on Agent
Hash: e013c10a30b400a9ef690293d1f40ca1b3a623e2 [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💻 [CI Build] Tests on macOS M1 - Mac Big Sur (11.5) passed 💻

All tests on macOS M1 - Mac Big Sur (11.5) passed.

Pipeline on Agent
Hash: e013c10a30b400a9ef690293d1f40ca1b3a623e2 [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📚 [CI Build] Artifacts 📚

Packages generated

View packages

Pipeline on Agent XAMMINI-059.Monterey'
Hash: e013c10a30b400a9ef690293d1f40ca1b3a623e2 [CI build]

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.