From 62d72c7a155f73956aa715716fefd8be0e584a05 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Saenz Date: Wed, 28 May 2025 11:38:29 -0400 Subject: [PATCH 1/5] [RGen] Update the emitters to generate the trampoline static class. This is the initial implementation of the trampoline emitter. It just focuses on the delegate and the static class. A generic class has been added but more tests will come per framework, we simply want to make the diff manageable for the team, so we have not added all the tests in a single PR. --- .../BindingSyntaxFactory.KnownTypes.cs | 7 + .../Emitters/Documentation.cs | 3 + .../Emitters/TrampolineEmitter.cs | 171 ++++++++++- .../BaseGeneratorTestClass.cs | 2 +- .../Data/ExpectedTrampolinePropertyTests.cs | 276 +++++++++++++++++ ...ectedTrampolinePropertyTestsTrampolines.cs | 279 ++++++++++++++++++ .../Classes/Data/TrampolinePropertyTests.cs | 42 +++ .../Microsoft.Macios.Generator.Tests.csproj | 2 + 8 files changed, 775 insertions(+), 7 deletions(-) diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.KnownTypes.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.KnownTypes.cs index 47c7b440f530..091a7fad7de3 100644 --- a/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.KnownTypes.cs +++ b/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.KnownTypes.cs @@ -68,6 +68,13 @@ static partial class BindingSyntaxFactory { @namespace: ["ObjCRuntime"], @class: "Runtime"); + /// + /// TypeSyntax for ObjCRuntime.BlockLiteral. + /// + public static readonly TypeSyntax BlockLiteral = StringExtensions.GetIdentifierName ( + @namespace: ["ObjCRuntime"], + @class: "BlockLiteral"); + // Foundation types /// diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/Documentation.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/Documentation.cs index 9025697643e4..d2a1c6028bb7 100644 --- a/src/rgen/Microsoft.Macios.Generator/Emitters/Documentation.cs +++ b/src/rgen/Microsoft.Macios.Generator/Emitters/Documentation.cs @@ -135,5 +135,8 @@ public static string DefaultInitWithHandle (string name) => /// Developers should not invoke this method directly, instead they should call as it will prevent two instances of a managed object pointing to the same native object. /// /// "; + + public static string TrampolineStaticClass (string name) => +"/// This class bridges native block invocations that call into C#"; } } diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs index 1f06e97b9be2..3bb693a8d9af 100644 --- a/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs +++ b/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs @@ -3,21 +3,164 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.Macios.Generator.Context; -using Microsoft.Macios.Generator.DataModel; +using Microsoft.Macios.Generator.Formatters; using Microsoft.Macios.Generator.IO; using TypeInfo = Microsoft.Macios.Generator.DataModel.TypeInfo; +using static Microsoft.Macios.Generator.Emitters.BindingSyntaxFactory; namespace Microsoft.Macios.Generator.Emitters; class TrampolineEmitter ( RootContext context, TabbedStringBuilder builder) { + /// + /// The nomenclator is used to generate the name of the static class that will contain the trampoline, needs to + /// be an instance class since we want to keep track of the already generated names. + /// + Nomenclator nomenclator = new (); public string SymbolNamespace => "ObjCRuntime"; public string SymbolName => "Trampolines"; + /// + /// Generate the static helper class for the trampoline uses to bridge the native block invocation. + /// + /// The type info of the trampoline to generate. + /// The trampoline name. + /// The tabbed string builder to use. + /// Ture if the code was generated, fals otherwise. + public bool TryEmitStaticClass (in TypeInfo typeInfo, string trampolineName, TabbedWriter classBuilder) + { + // create a new static class using the name from the nomenclator + var argumentSyntax = GetTrampolineInvokeArguments (trampolineName, typeInfo.Delegate!); + var delegateIdentifier = typeInfo.GetIdentifierSyntax (); + var className = Nomenclator.GetTrampolineClassName (trampolineName, Nomenclator.TrampolineClassType.StaticBridgeClass); + var invokeMethodName = Nomenclator.GetTrampolineInvokeMethodName (); + var trampolineVariableName = Nomenclator.GetTrampolineDelegatePointerVariableName (); + var delegateVariableName = Nomenclator.GetTrampolineDelegateVariableName (); + + classBuilder.WriteDocumentation (Documentation.Class.TrampolineStaticClass (className)); + using (var classBlock = classBuilder.CreateBlock ($"static internal class {className}", true)) { + // Invoke method + classBlock.WriteLine ("[Preserve (Conditional = true)]"); + classBlock.WriteLine ("[UnmanagedCallersOnly]"); + classBlock.WriteLine ($"[UserDelegateType (typeof ({delegateIdentifier}))]"); + using (var invokeMethod = classBlock.CreateBlock (GetTrampolineInvokeSignature (typeInfo).ToString (), true)) { + // initialized the parameters, this might be needed for the parameters that are out or ref + foreach (var argument in argumentSyntax) { + invokeMethod.Write (argument.Initializers); + } + + // get the delegate from the block literal to execute with the trampoline + invokeMethod.WriteLine ($"var {delegateVariableName} = {BlockLiteral}.GetTarget<{delegateIdentifier}> ({Nomenclator.GetTrampolineBlockParameterName (typeInfo.Delegate!.Parameters)});"); + + // if the deletate is null, we return default, otherwise we call the delegate + using (var ifBlock = invokeMethod.CreateBlock ($"if ({delegateVariableName} is not null)", true)) { + + // build any needed pre conversion operations before calling the delegate + foreach (var argument in argumentSyntax) { + ifBlock.Write (argument.PreDelegateCallConversion); + } + + ifBlock.WriteLine ($"{CallTrampolineDelegate (typeInfo.Delegate!, argumentSyntax)}"); + + // build any needed post conversion operations after calling the delegate + foreach (var argument in argumentSyntax) { + ifBlock.Write (argument.PostDelegateCallConversion); + } + + // perform any needed + if (typeInfo.Delegate.ReturnType.SpecialType != SpecialType.System_Void) + ifBlock.WriteLine($"return {GetTrampolineInvokeReturnType (typeInfo, Nomenclator.GetReturnVariableName ())};"); + } + if (typeInfo.Delegate.ReturnType.SpecialType != SpecialType.System_Void) + invokeMethod.WriteLine ("return default;"); + } + + // CreateNullableBlock + classBlock.WriteLine (); // empty line for readability + using (var createNullableBlock = classBlock.CreateBlock ( + $"internal static unsafe {BlockLiteral} CreateNullableBlock ({delegateIdentifier}? callback)", true)) { + createNullableBlock.WriteRaw ( +$@"if (callback is null) + return default ({BlockLiteral}); +return CreateBlock (callback); +" + ); + } + + // CreateBlock + classBlock.WriteLine (); // empty line for readability + classBlock.WriteLine ("[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]"); + using (var createBlock = classBlock.CreateBlock ( + $"internal static unsafe {BlockLiteral} CreateBlock ({delegateIdentifier} callback)", true)) { + createBlock.WriteLine (GetTrampolineDelegatePointer (typeInfo).ToFullString ()); + createBlock.WriteLine ( + $"return new {BlockLiteral} ({trampolineVariableName}, callback, typeof ({className}), nameof ({invokeMethodName}));"); + } + } + return true; + } + + /// + /// Emits the delegate declaration for the trampoline. + /// + /// The type of the trampoline to generate. + /// The current tabbed string builder to use. + /// A hash set with the delegates already added. + /// The delegate name. + /// True if the code was generated, false otherwise. + public bool TryEmitInternalDelegate (in TypeInfo typeInfo, TabbedWriter classBuilder, HashSet addedDelegates, + [NotNullWhen (true)] out string? delegateName) + { + delegateName = null; + if (typeInfo.Delegate is null) + return false; + + // generate the delegate and get its name, if we already emitted it, skip it + var delegateDeclaration = GetTrampolineDelegateDeclaration (typeInfo, out delegateName); + if (addedDelegates.Add (delegateName)) { + // print the attributes needed for the delegate and the delegate itself + classBuilder.WriteLine ("[UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)]"); + classBuilder.WriteLine ($"[UserDelegateType (typeof ({typeInfo.GetIdentifierSyntax ()}))]"); + classBuilder.WriteLine (delegateDeclaration.ToString ()); + } + return true; + } + + /// + /// Write the using statements for the namespaces used by the trampolines. + /// + /// The trampolines to generate. + public void WriteUsedNamespaces (IReadOnlySet trampolines) + { + // create set with the default namespaces + var namespaces = new HashSet () { + "Foundation", + "ObjCBindings", + "ObjCRuntime", + "System", + }; + // loop through the trampolines and add the namespaces used by the delegates + foreach (var info in trampolines) + namespaces.Add (string.Join ('.', info.Namespace)); + + // sort the namespaces so that we can write them in a deterministic way + foreach (var ns in namespaces.OrderBy (x => x)) { + builder.WriteLine ($"using {ns};"); + } + } + + /// + /// Generate the trampolines for the given set of types. + /// + /// The trampolines type info to use for the code generation. + /// Possible diagnostic errors. + /// True if the code was generated, false otherwise. public bool TryEmit (IReadOnlySet trampolines, [NotNullWhen (false)] out ImmutableArray? diagnostics) { @@ -25,19 +168,35 @@ public bool TryEmit (IReadOnlySet trampolines, // and some comments with the trampolines to emit diagnostics = null; - builder.WriteLine ("using Foundation;"); - builder.WriteLine ("using ObjCBindings;"); - builder.WriteLine ("using ObjCRuntime;"); - builder.WriteLine ("using System;"); + // write the using statements + WriteUsedNamespaces (trampolines); builder.WriteLine (); - builder.WriteLine ($"namespace ObjCRuntime;"); + builder.WriteLine ("namespace ObjCRuntime;"); builder.WriteLine (); + // keep track of the already emitted delegates + var addedDelegates = new HashSet (); + using (var classBlock = builder.CreateBlock ($"static partial class {SymbolName}", true)) { classBlock.WriteLine ($"// Generate trampolines for compilation"); _ = context.CurrentPlatform; foreach (var info in trampolines) { + var trampolineName = nomenclator.GetTrampolineName (info); + // write the delegate declaration + if (!TryEmitInternalDelegate (info, classBlock, addedDelegates, out var delegateDeclaration)) { + diagnostics = []; + return false; + } + + classBlock.WriteLine (); // empty line for readability + + // generate the static class + if (!TryEmitStaticClass (info, trampolineName, classBlock)) { + diagnostics = []; + return false; + } + classBlock.WriteLine ($"// TODO: generate trampoline for {info.FullyQualifiedName}"); classBlock.WriteLine (); } diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs index 959b1f4fdfec..ddb73fcbd323 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs @@ -122,7 +122,7 @@ protected void CompareGeneratedCode (GenerationTestData testData) if (testData.ExpectedTrampolineText is not null) { // validate that Library.g.cs was created by the LibraryEmitter and matches the expectation - var generatedLibSyntax = runResult.GeneratedTrees.Single (t => t.FilePath.EndsWith ("Trampolines.g.cs")); + var generatedLibSyntax = runResult.GeneratedTrees.Single (t => t.FilePath.EndsWith ("ObjCRuntime/Trampolines.g.cs")); Assert.Equal (testData.ExpectedTrampolineText, generatedLibSyntax.GetText ().ToString ()); } } diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/ExpectedTrampolinePropertyTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/ExpectedTrampolinePropertyTests.cs index 34e68a976805..525223609d4b 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/ExpectedTrampolinePropertyTests.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/ExpectedTrampolinePropertyTests.cs @@ -2,7 +2,9 @@ #nullable enable +using AVFoundation; using CoreGraphics; +using CoreImage; using Foundation; using ObjCBindings; using ObjCRuntime; @@ -20,6 +22,14 @@ namespace Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace; [Register ("TrampolinePropertyTests", true)] public partial class TrampolinePropertyTests { + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selCreateObjectHandlerX = "createObjectHandler"; + static readonly global::ObjCRuntime.NativeHandle selCreateObjectHandlerXHandle = global::ObjCRuntime.Selector.GetHandle ("createObjectHandler"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetCreateObjectHandler_X = "setCreateObjectHandler:"; + static readonly global::ObjCRuntime.NativeHandle selSetCreateObjectHandler_XHandle = global::ObjCRuntime.Selector.GetHandle ("setCreateObjectHandler:"); + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] const string selCompletionHandlerX = "completionHandler"; static readonly global::ObjCRuntime.NativeHandle selCompletionHandlerXHandle = global::ObjCRuntime.Selector.GetHandle ("completionHandler"); @@ -28,6 +38,62 @@ public partial class TrampolinePropertyTests const string selSetCompletionHandler_X = "setCompletionHandler:"; static readonly global::ObjCRuntime.NativeHandle selSetCompletionHandler_XHandle = global::ObjCRuntime.Selector.GetHandle ("setCompletionHandler:"); + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selDuplicateCompletionHandlerX = "duplicateCompletionHandler"; + static readonly global::ObjCRuntime.NativeHandle selDuplicateCompletionHandlerXHandle = global::ObjCRuntime.Selector.GetHandle ("duplicateCompletionHandler"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetDuplicateCompletionHandler_X = "setDuplicateCompletionHandler:"; + static readonly global::ObjCRuntime.NativeHandle selSetDuplicateCompletionHandler_XHandle = global::ObjCRuntime.Selector.GetHandle ("setDuplicateCompletionHandler:"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selImageGeneratorCompletionHandlerX = "imageGeneratorCompletionHandler"; + static readonly global::ObjCRuntime.NativeHandle selImageGeneratorCompletionHandlerXHandle = global::ObjCRuntime.Selector.GetHandle ("imageGeneratorCompletionHandler"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetImageGeneratorCompletionHandler_X = "setImageGeneratorCompletionHandler:"; + static readonly global::ObjCRuntime.NativeHandle selSetImageGeneratorCompletionHandler_XHandle = global::ObjCRuntime.Selector.GetHandle ("setImageGeneratorCompletionHandler:"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selKernelRoiCallbackX = "kernelRoiCallback"; + static readonly global::ObjCRuntime.NativeHandle selKernelRoiCallbackXHandle = global::ObjCRuntime.Selector.GetHandle ("kernelRoiCallback"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetKernelRoiCallback_X = "setKernelRoiCallback:"; + static readonly global::ObjCRuntime.NativeHandle selSetKernelRoiCallback_XHandle = global::ObjCRuntime.Selector.GetHandle ("setKernelRoiCallback:"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selStringActionHandlerX = "stringActionHandler"; + static readonly global::ObjCRuntime.NativeHandle selStringActionHandlerXHandle = global::ObjCRuntime.Selector.GetHandle ("stringActionHandler"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetStringActionHandler_X = "setStringActionHandler:"; + static readonly global::ObjCRuntime.NativeHandle selSetStringActionHandler_XHandle = global::ObjCRuntime.Selector.GetHandle ("setStringActionHandler:"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selIntActionHandlerX = "intActionHandler"; + static readonly global::ObjCRuntime.NativeHandle selIntActionHandlerXHandle = global::ObjCRuntime.Selector.GetHandle ("intActionHandler"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetIntActionHandler_X = "setIntActionHandler:"; + static readonly global::ObjCRuntime.NativeHandle selSetIntActionHandler_XHandle = global::ObjCRuntime.Selector.GetHandle ("setIntActionHandler:"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selBoolActionHandlerX = "boolActionHandler"; + static readonly global::ObjCRuntime.NativeHandle selBoolActionHandlerXHandle = global::ObjCRuntime.Selector.GetHandle ("boolActionHandler"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetBoolActionHandler_X = "setBoolActionHandler:"; + static readonly global::ObjCRuntime.NativeHandle selSetBoolActionHandler_XHandle = global::ObjCRuntime.Selector.GetHandle ("setBoolActionHandler:"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selManualRenderingCallbackX = "manualRenderingCallback"; + static readonly global::ObjCRuntime.NativeHandle selManualRenderingCallbackXHandle = global::ObjCRuntime.Selector.GetHandle ("manualRenderingCallback"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetManualRenderingCallback_X = "setManualRenderingCallback:"; + static readonly global::ObjCRuntime.NativeHandle selSetManualRenderingCallback_XHandle = global::ObjCRuntime.Selector.GetHandle ("setManualRenderingCallback:"); + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] static readonly global::ObjCRuntime.NativeHandle class_ptr = global::ObjCRuntime.Class.GetHandle ("TrampolinePropertyTests"); @@ -113,6 +179,27 @@ protected TrampolinePropertyTests (global::Foundation.NSObjectFlag t) : base (t) [EditorBrowsable (EditorBrowsableState.Advanced)] protected internal TrampolinePropertyTests (global::ObjCRuntime.NativeHandle handle) : base (handle) {} + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::System.Action BoolActionHandler + { + get + { + global::System.Action ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("boolActionHandler")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("boolActionHandler")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] public partial global::System.Action CompletionHandler { @@ -133,5 +220,194 @@ protected internal TrampolinePropertyTests (global::ObjCRuntime.NativeHandle han throw new NotImplementedException(); } } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace.TrampolinePropertyTests.CreateObject CreateObjectHandler + { + get + { + global::Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace.TrampolinePropertyTests.CreateObject ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("createObjectHandler")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("createObjectHandler")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::System.Action DuplicateCompletionHandler + { + get + { + global::System.Action ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("duplicateCompletionHandler")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("duplicateCompletionHandler")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::AVFoundation.AVAssetImageGenerator.AsynchronouslyForTimeCompletionHandler ImageGeneratorCompletionHandler + { + get + { + global::AVFoundation.AVAssetImageGenerator.AsynchronouslyForTimeCompletionHandler ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging._objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("imageGeneratorCompletionHandler")); + } else { + ret = global::ObjCRuntime.Messaging._objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("imageGeneratorCompletionHandler")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::AVFoundation.AVAssetImageGenerateAsynchronouslyForTimeCompletionHandler ImageGeneratorCompletionHandler + { + get + { + global::AVFoundation.AVAssetImageGenerateAsynchronouslyForTimeCompletionHandler ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("imageGeneratorCompletionHandler")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("imageGeneratorCompletionHandler")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::System.Action IntActionHandler + { + get + { + global::System.Action ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("intActionHandler")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("intActionHandler")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::CoreImage.CIKernelRoiCallback KernelRoiCallback + { + get + { + global::CoreImage.CIKernelRoiCallback ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("kernelRoiCallback")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("kernelRoiCallback")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::CoreImage.CIKernelRoiCallback KernelRoiCallback + { + get + { + global::CoreImage.CIKernelRoiCallback ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("kernelRoiCallback")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("kernelRoiCallback")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::AVFoundation.AVAudioEngineManualRenderingBlock ManualRendering + { + get + { + global::AVFoundation.AVAudioEngineManualRenderingBlock ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("manualRenderingCallback")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("manualRenderingCallback")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::System.Action StringActionHandler + { + get + { + global::System.Action ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("stringActionHandler")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("stringActionHandler")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } // TODO: add binding code here } diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/ExpectedTrampolinePropertyTestsTrampolines.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/ExpectedTrampolinePropertyTestsTrampolines.cs index e6dbd4fde887..fa4919703a99 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/ExpectedTrampolinePropertyTestsTrampolines.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/ExpectedTrampolinePropertyTestsTrampolines.cs @@ -2,7 +2,10 @@ #nullable enable +using AVFoundation; +using CoreImage; using Foundation; +using Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace; using ObjCBindings; using ObjCRuntime; using System; @@ -12,6 +15,282 @@ namespace ObjCRuntime; static partial class Trampolines { // Generate trampolines for compilation + [UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)] + [UserDelegateType (typeof (global::Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace.TrampolinePropertyTests.CreateObject))] + unsafe internal delegate global::ObjCRuntime.NativeHandle DTrampolinePropertyTests.CreateObject (global::System.IntPtr block_ptr, global::ObjCRuntime.NativeHandle obj); + + /// This class bridges native block invocations that call into C# + static internal class SDTrampolinePropertyTests.CreateObject + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace.TrampolinePropertyTests.CreateObject))] + internal static unsafe global::ObjCRuntime.NativeHandle Invoke (global::System.IntPtr block_ptr, global::ObjCRuntime.NativeHandle obj) + { + var del = global::ObjCRuntime.BlockLiteral.GetTarget (block_ptr); + if (del is not null) + { + var ret = del (global::ObjCRuntime.Runtime.GetNSObject (obj)!); + return global::ObjCRuntime.Runtime.RetainAndAutoreleaseNSObject (ret); + } + return default; + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace.TrampolinePropertyTests.CreateObject? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace.TrampolinePropertyTests.CreateObject callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDTrampolinePropertyTests.CreateObject), nameof (Invoke)); + } + } + // TODO: generate trampoline for Microsoft.Macios.Generator.Tests.Classes.Data.TestNamespace.TrampolinePropertyTests.CreateObject + + [UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)] + [UserDelegateType (typeof (global::System.Action))] + unsafe internal delegate void DAction (global::System.IntPtr block_ptr); + + /// This class bridges native block invocations that call into C# + static internal class SDAction + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::System.Action))] + internal static unsafe void Invoke (global::System.IntPtr block_ptr) + { + var del = global::ObjCRuntime.BlockLiteral.GetTarget (block_ptr); + if (del is not null) + { + del (); + } + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::System.Action? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::System.Action callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDAction), nameof (Invoke)); + } + } // TODO: generate trampoline for System.Action + [UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)] + [UserDelegateType (typeof (global::CoreImage.CIKernelRoiCallback))] + unsafe internal delegate global::CoreGraphics.CGRect DCIKernelRoiCallback (global::System.IntPtr block_ptr, int index, global::CoreGraphics.CGRect rect); + + /// This class bridges native block invocations that call into C# + static internal class SDCIKernelRoiCallback + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::CoreImage.CIKernelRoiCallback))] + internal static unsafe global::CoreGraphics.CGRect Invoke (global::System.IntPtr block_ptr, int index, global::CoreGraphics.CGRect rect) + { + var del = global::ObjCRuntime.BlockLiteral.GetTarget (block_ptr); + if (del is not null) + { + var ret = del (index, rect); + return ret; + } + return default; + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::CoreImage.CIKernelRoiCallback? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::CoreImage.CIKernelRoiCallback callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDCIKernelRoiCallback), nameof (Invoke)); + } + } + // TODO: generate trampoline for CoreImage.CIKernelRoiCallback + + + /// This class bridges native block invocations that call into C# + static internal class SDActionArity1V0 + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::System.Action))] + internal static unsafe void Invoke (global::System.IntPtr block_ptr, global::ObjCRuntime.NativeHandle obj) + { + var del = global::ObjCRuntime.BlockLiteral.GetTarget> (block_ptr); + if (del is not null) + { + del (global::CoreFoundation.CFString.FromHandle (obj)!); + } + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::System.Action? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::System.Action callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDActionArity1V0), nameof (Invoke)); + } + } + // TODO: generate trampoline for System.Action + + + /// This class bridges native block invocations that call into C# + static internal class SDActionArity1V1 + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::System.Action))] + internal static unsafe void Invoke (global::System.IntPtr block_ptr, int obj) + { + var del = global::ObjCRuntime.BlockLiteral.GetTarget> (block_ptr); + if (del is not null) + { + del (obj); + } + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::System.Action? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::System.Action callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDActionArity1V1), nameof (Invoke)); + } + } + // TODO: generate trampoline for System.Action + + + /// This class bridges native block invocations that call into C# + static internal class SDActionArity1V2 + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::System.Action))] + internal static unsafe void Invoke (global::System.IntPtr block_ptr, byte obj) + { + var del = global::ObjCRuntime.BlockLiteral.GetTarget> (block_ptr); + if (del is not null) + { + del (obj != 0); + } + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::System.Action? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::System.Action callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDActionArity1V2), nameof (Invoke)); + } + } + // TODO: generate trampoline for System.Action + + [UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)] + [UserDelegateType (typeof (global::AVFoundation.AVAssetImageGenerateAsynchronouslyForTimeCompletionHandler))] + unsafe internal delegate void DAVAssetImageGenerateAsynchronouslyForTimeCompletionHandler (global::System.IntPtr block_ptr, global::ObjCRuntime.NativeHandle imageRef, global::CoreMedia.CMTime actualTime, global::ObjCRuntime.NativeHandle error); + + /// This class bridges native block invocations that call into C# + static internal class SDAVAssetImageGenerateAsynchronouslyForTimeCompletionHandler + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::AVFoundation.AVAssetImageGenerateAsynchronouslyForTimeCompletionHandler))] + internal static unsafe void Invoke (global::System.IntPtr block_ptr, global::ObjCRuntime.NativeHandle imageRef, global::CoreMedia.CMTime actualTime, global::ObjCRuntime.NativeHandle error) + { + var del = global::ObjCRuntime.BlockLiteral.GetTarget (block_ptr); + if (del is not null) + { + del (global::ObjCRuntime.Runtime.GetINativeObject (imageRef, false)!, actualTime, global::ObjCRuntime.Runtime.GetNSObject (error)!); + } + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::AVFoundation.AVAssetImageGenerateAsynchronouslyForTimeCompletionHandler? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::AVFoundation.AVAssetImageGenerateAsynchronouslyForTimeCompletionHandler callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDAVAssetImageGenerateAsynchronouslyForTimeCompletionHandler), nameof (Invoke)); + } + } + // TODO: generate trampoline for AVFoundation.AVAssetImageGenerateAsynchronouslyForTimeCompletionHandler + + [UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)] + [UserDelegateType (typeof (global::AVFoundation.AVAudioEngineManualRenderingBlock))] + unsafe internal delegate global::System.IntPtr DAVAudioEngineManualRenderingBlock (global::System.IntPtr block_ptr, uint numberOfFrames, global::ObjCRuntime.NativeHandle outBuffer, int* outError); + + /// This class bridges native block invocations that call into C# + static internal class SDAVAudioEngineManualRenderingBlock + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::AVFoundation.AVAudioEngineManualRenderingBlock))] + internal static unsafe global::System.IntPtr Invoke (global::System.IntPtr block_ptr, uint numberOfFrames, global::ObjCRuntime.NativeHandle outBuffer, int* outError) + { + *outError = default; + var del = global::ObjCRuntime.BlockLiteral.GetTarget (block_ptr); + if (del is not null) + { + var ret = del (numberOfFrames, new global::AudioToolbox.AudioBuffers (outBuffer), ref global::System.Runtime.CompilerServices.Unsafe.AsRef (outError)); + return (IntPtr) (long) ret; + } + return default; + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::AVFoundation.AVAudioEngineManualRenderingBlock? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::AVFoundation.AVAudioEngineManualRenderingBlock callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDAVAudioEngineManualRenderingBlock), nameof (Invoke)); + } + } + // TODO: generate trampoline for AVFoundation.AVAudioEngineManualRenderingBlock + } diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/TrampolinePropertyTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/TrampolinePropertyTests.cs index 38701c21642c..fdeaf1f596c9 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/TrampolinePropertyTests.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/TrampolinePropertyTests.cs @@ -8,6 +8,8 @@ namespace Microsoft.Macios.Generator.Tests.Classes.Data; using System; using System.Runtime.Versioning; +using AVFoundation; +using CoreImage; using CoreGraphics; using Foundation; using ObjCBindings; @@ -17,7 +19,47 @@ namespace TestNamespace; [BindingType] public partial class TrampolinePropertyTests { + + public delegate NSObject CreateObject (NSObject obj); + + [Export ("createObjectHandler", ArgumentSemantic.Copy)] + public partial CreateObject CreateObjectHandler { get; set; } [Export ("completionHandler", ArgumentSemantic.Copy)] public partial Action CompletionHandler { get; set; } + + // Duplicate property using Action + [Export ("duplicateCompletionHandler", ArgumentSemantic.Copy)] + public partial Action DuplicateCompletionHandler { get; set; } + + [Export ("imageGeneratorCompletionHandler", ArgumentSemantic.Copy)] + public partial AVAssetImageGenerator.AsynchronouslyForTimeCompletionHandler ImageGeneratorCompletionHandler { get; set; } + + // Property using CIKernelRoiCallback + [Export ("kernelRoiCallback", ArgumentSemantic.Copy)] + public partial CIKernelRoiCallback KernelRoiCallback { get; set; }] + + // Property using Action + [Export ("stringActionHandler", ArgumentSemantic.Copy)] + public partial Action StringActionHandler { get; set; } + + // Property using Action + [Export ("intActionHandler", ArgumentSemantic.Copy)] + public partial Action IntActionHandler { get; set; } + + // Property using Action + [Export("boolActionHandler", ArgumentSemantic.Copy)] + public partial Action BoolActionHandler { get; set; } + + // Property using AVAssetImageGenerator.AsynchronouslyForTimeCompletionHandler + [Export ("imageGeneratorCompletionHandler", ArgumentSemantic.Copy)] + public partial AVAssetImageGenerateAsynchronouslyForTimeCompletionHandler ImageGeneratorCompletionHandler { get; set; } + + // Property using CIKernelRoiCallback + [Export ("kernelRoiCallback", ArgumentSemantic.Copy)] + public partial CIKernelRoiCallback KernelRoiCallback { get; set; } + + // Property using AVAssetImageGenerator.AsynchronouslyForTimeCompletionHandler + [Export("manualRenderingCallback", ArgumentSemantic.Copy)] + public partial AVAudioEngineManualRenderingBlock ManualRendering { get; set; } } diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj b/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj index 974bac298d84..706ed2664fce 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj @@ -61,9 +61,11 @@ + + From f2c89ab7f2ead32d45de32e1c4a0bd9285400ddb Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Wed, 28 May 2025 11:43:52 -0400 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs index 3bb693a8d9af..670bfe5168cc 100644 --- a/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs +++ b/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs @@ -32,7 +32,7 @@ class TrampolineEmitter ( /// The type info of the trampoline to generate. /// The trampoline name. /// The tabbed string builder to use. - /// Ture if the code was generated, fals otherwise. + /// True if the code was generated, false otherwise. public bool TryEmitStaticClass (in TypeInfo typeInfo, string trampolineName, TabbedWriter classBuilder) { // create a new static class using the name from the nomenclator From 587a8063e3f95ec8179a5f299aa8c075babc8077 Mon Sep 17 00:00:00 2001 From: GitHub Actions Autoformatter Date: Wed, 28 May 2025 15:43:50 +0000 Subject: [PATCH 3/5] Auto-format source code --- .../Emitters/TrampolineEmitter.cs | 18 +++++++++--------- .../Classes/Data/TrampolinePropertyTests.cs | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs index 670bfe5168cc..9479f5d08111 100644 --- a/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs +++ b/src/rgen/Microsoft.Macios.Generator/Emitters/TrampolineEmitter.cs @@ -54,30 +54,30 @@ public bool TryEmitStaticClass (in TypeInfo typeInfo, string trampolineName, Tab foreach (var argument in argumentSyntax) { invokeMethod.Write (argument.Initializers); } - + // get the delegate from the block literal to execute with the trampoline invokeMethod.WriteLine ($"var {delegateVariableName} = {BlockLiteral}.GetTarget<{delegateIdentifier}> ({Nomenclator.GetTrampolineBlockParameterName (typeInfo.Delegate!.Parameters)});"); - + // if the deletate is null, we return default, otherwise we call the delegate using (var ifBlock = invokeMethod.CreateBlock ($"if ({delegateVariableName} is not null)", true)) { - + // build any needed pre conversion operations before calling the delegate foreach (var argument in argumentSyntax) { ifBlock.Write (argument.PreDelegateCallConversion); } - + ifBlock.WriteLine ($"{CallTrampolineDelegate (typeInfo.Delegate!, argumentSyntax)}"); - + // build any needed post conversion operations after calling the delegate foreach (var argument in argumentSyntax) { ifBlock.Write (argument.PostDelegateCallConversion); } - + // perform any needed - if (typeInfo.Delegate.ReturnType.SpecialType != SpecialType.System_Void) - ifBlock.WriteLine($"return {GetTrampolineInvokeReturnType (typeInfo, Nomenclator.GetReturnVariableName ())};"); + if (typeInfo.Delegate.ReturnType.SpecialType != SpecialType.System_Void) + ifBlock.WriteLine ($"return {GetTrampolineInvokeReturnType (typeInfo, Nomenclator.GetReturnVariableName ())};"); } - if (typeInfo.Delegate.ReturnType.SpecialType != SpecialType.System_Void) + if (typeInfo.Delegate.ReturnType.SpecialType != SpecialType.System_Void) invokeMethod.WriteLine ("return default;"); } diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/TrampolinePropertyTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/TrampolinePropertyTests.cs index fdeaf1f596c9..1cfb989abb26 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/TrampolinePropertyTests.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/TrampolinePropertyTests.cs @@ -19,9 +19,9 @@ namespace TestNamespace; [BindingType] public partial class TrampolinePropertyTests { - + public delegate NSObject CreateObject (NSObject obj); - + [Export ("createObjectHandler", ArgumentSemantic.Copy)] public partial CreateObject CreateObjectHandler { get; set; } @@ -46,9 +46,9 @@ public partial class TrampolinePropertyTests { // Property using Action [Export ("intActionHandler", ArgumentSemantic.Copy)] public partial Action IntActionHandler { get; set; } - + // Property using Action - [Export("boolActionHandler", ArgumentSemantic.Copy)] + [Export ("boolActionHandler", ArgumentSemantic.Copy)] public partial Action BoolActionHandler { get; set; } // Property using AVAssetImageGenerator.AsynchronouslyForTimeCompletionHandler @@ -58,8 +58,8 @@ public partial class TrampolinePropertyTests { // Property using CIKernelRoiCallback [Export ("kernelRoiCallback", ArgumentSemantic.Copy)] public partial CIKernelRoiCallback KernelRoiCallback { get; set; } - + // Property using AVAssetImageGenerator.AsynchronouslyForTimeCompletionHandler - [Export("manualRenderingCallback", ArgumentSemantic.Copy)] + [Export ("manualRenderingCallback", ArgumentSemantic.Copy)] public partial AVAudioEngineManualRenderingBlock ManualRendering { get; set; } } From a95ae94e3e50ee98b1df29ca97331e98421a1937 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Saenz Date: Wed, 28 May 2025 11:52:22 -0400 Subject: [PATCH 4/5] [RGen] Add ARKit trampoline tests. Add the tests that show that the ARKit trampoline static class is correctly generated. --- .../Classes/ClassGenerationTests.cs | 3 + .../Data/Trampolines/ARKitTrampolines.cs | 19 +++ .../ExpectedARKitTrampolinesProperties.cs | 137 ++++++++++++++++++ .../ExpectedARKitTrampolinesTrampolines.cs | 51 +++++++ 4 files changed, 210 insertions(+) create mode 100644 tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ARKitTrampolines.cs create mode 100644 tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ExpectedARKitTrampolinesProperties.cs create mode 100644 tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ExpectedARKitTrampolinesTrampolines.cs diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/ClassGenerationTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/ClassGenerationTests.cs index f56062e4641c..ea365e9dfb79 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/ClassGenerationTests.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/ClassGenerationTests.cs @@ -47,6 +47,9 @@ public class TestDataGenerator : BaseTestDataGenerator, IEnumerable { (ApplePlatform.TVOS, "TrampolinePropertyTests", "TrampolinePropertyTests.cs", "ExpectedTrampolinePropertyTests.cs", null, "ExpectedTrampolinePropertyTestsTrampolines.cs"), (ApplePlatform.MacCatalyst, "TrampolinePropertyTests", "TrampolinePropertyTests.cs", "ExpectedTrampolinePropertyTests.cs", null, "ExpectedTrampolinePropertyTestsTrampolines.cs"), (ApplePlatform.MacOSX, "TrampolinePropertyTests", "TrampolinePropertyTests.cs", "ExpectedTrampolinePropertyTests.cs", null, "ExpectedTrampolinePropertyTestsTrampolines.cs"), + + // ARKit trampoline tests, only present on iOS + (ApplePlatform.iOS, "ARKitTrampolines", "Trampolines/ARKitTrampolines.cs", "Trampolines/ExpectedARKitTrampolinesProperties.cs", null, "Trampolines/ExpectedARKitTrampolinesTrampolines.cs"), }; public IEnumerator GetEnumerator () diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ARKitTrampolines.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ARKitTrampolines.cs new file mode 100644 index 000000000000..5ac1e9582ebe --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ARKitTrampolines.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.Versioning; +using ARKit; +using Foundation; +using ObjCBindings; +using ObjCRuntime; + +namespace TestNamespace; + +[BindingType] +public partial class ARKitTrampolines { + + [Export ("geolocationCallbackHandler", ArgumentSemantic.Copy)] + public partial ARKit.GetGeolocationCallback GeolocationCallbackHandler { get; set; } + +} diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ExpectedARKitTrampolinesProperties.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ExpectedARKitTrampolinesProperties.cs new file mode 100644 index 000000000000..f3fc9f4ce8de --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ExpectedARKitTrampolinesProperties.cs @@ -0,0 +1,137 @@ +// + +#nullable enable + +using ARKit; +using Foundation; +using ObjCBindings; +using ObjCRuntime; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading.Tasks; + +namespace TestNamespace; + +[Register ("ARKitTrampolines", true)] +public partial class ARKitTrampolines +{ + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selGeolocationCallbackHandlerX = "geolocationCallbackHandler"; + static readonly global::ObjCRuntime.NativeHandle selGeolocationCallbackHandlerXHandle = global::ObjCRuntime.Selector.GetHandle ("geolocationCallbackHandler"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + const string selSetGeolocationCallbackHandler_X = "setGeolocationCallbackHandler:"; + static readonly global::ObjCRuntime.NativeHandle selSetGeolocationCallbackHandler_XHandle = global::ObjCRuntime.Selector.GetHandle ("setGeolocationCallbackHandler:"); + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + static readonly global::ObjCRuntime.NativeHandle class_ptr = global::ObjCRuntime.Class.GetHandle ("ARKitTrampolines"); + + /// The Objective-C class handle for this class. + /// The pointer to the Objective-C class. + /// + /// Each managed class mirrors an unmanaged Objective-C class. + /// This value contains the pointer to the Objective-C class. + /// It is similar to calling the managed or the native objc_getClass method with the type name. + /// + public override global::ObjCRuntime.NativeHandle ClassHandle => class_ptr; + + /// Creates a new with default values. + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + [DesignatedInitializer] + [Export ("init")] + public ARKitTrampolines () : base (global::Foundation.NSObjectFlag.Empty) + { + if (IsDirectBinding) + InitializeHandle (global::ObjCRuntime.Messaging.IntPtr_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("init")), "init"); + else + InitializeHandle (global::ObjCRuntime.Messaging.IntPtr_objc_msgSendSuper (this.SuperHandle, global::ObjCRuntime.Selector.GetHandle ("init")), "init"); + } + + /// Constructor to call on derived classes to skip initialization and merely allocate the object. + /// Unused sentinel value, pass NSObjectFlag.Empty. + /// + /// + /// This constructor should be called by derived classes when they completely construct the object in managed code and merely want the runtime to allocate and initialize the . + /// This is required to implement the two-step initialization process that Objective-C uses, the first step is to perform the object allocation, the second step is to initialize the object. + /// When developers invoke this constructor, they take advantage of a direct path that goes all the way up to to merely allocate the object's memory and bind the Objective-C and C# objects together. + /// The actual initialization of the object is up to the developer. + /// + /// + /// This constructor is typically used by the binding generator to allocate the object, but prevent the actual initialization to take place. + /// Once the allocation has taken place, the constructor has to initialize the object. + /// With constructors generated by the binding generator this means that it manually invokes one of the "init" methods to initialize the object. + /// + /// It is the developer's responsibility to completely initialize the object if they chain up using this constructor chain. + /// + /// In general, if the developer's constructor invokes the corresponding base implementation, then it should also call an Objective-C init method. + /// If this is not the case, developers should instead chain to the proper constructor in their class. + /// + /// + /// The argument value is ignored and merely ensures that the only code that is executed is the construction phase is the basic allocation and runtime type registration. + /// Typically the chaining would look like this: + /// + /// + /// + /// + /// + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + [EditorBrowsable (EditorBrowsableState.Advanced)] + protected ARKitTrampolines (global::Foundation.NSObjectFlag t) : base (t) {} + + /// A constructor used when creating managed representations of unmanaged objects. Called by the runtime. + /// Pointer (handle) to the unmanaged object. + /// + /// + /// This constructor is invoked by the runtime infrastructure () to create a new managed representation for a pointer to an unmanaged Objective-C object. + /// Developers should not invoke this method directly, instead they should call as it will prevent two instances of a managed object pointing to the same native object. + /// + /// + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + [EditorBrowsable (EditorBrowsableState.Advanced)] + protected internal ARKitTrampolines (global::ObjCRuntime.NativeHandle handle) : base (handle) {} + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + public partial global::ARKit.GetGeolocationCallback GeolocationCallbackHandler + { + get + { + global::ARKit.GetGeolocationCallback ret; + if (IsDirectBinding) { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("geolocationCallbackHandler")); + } else { + ret = global::ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper (this.Handle, global::ObjCRuntime.Selector.GetHandle ("geolocationCallbackHandler")); + } + GC.KeepAlive (this); + return ret; + } + + set + { + throw new NotImplementedException(); + } + } + // TODO: add binding code here +} diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ExpectedARKitTrampolinesTrampolines.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ExpectedARKitTrampolinesTrampolines.cs new file mode 100644 index 000000000000..ac81d401251f --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ExpectedARKitTrampolinesTrampolines.cs @@ -0,0 +1,51 @@ +// + +#nullable enable + +using ARKit; +using Foundation; +using ObjCBindings; +using ObjCRuntime; +using System; + +namespace ObjCRuntime; + +static partial class Trampolines +{ + // Generate trampolines for compilation + [UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)] + [UserDelegateType (typeof (global::ARKit.GetGeolocationCallback))] + unsafe internal delegate void DGetGeolocationCallback (global::System.IntPtr block_ptr, global::CoreLocation.CLLocationCoordinate2D coordinate, double altitude, global::ObjCRuntime.NativeHandle error); + + /// This class bridges native block invocations that call into C# + static internal class SDGetGeolocationCallback + { + [Preserve (Conditional = true)] + [UnmanagedCallersOnly] + [UserDelegateType (typeof (global::ARKit.GetGeolocationCallback))] + internal static unsafe void Invoke (global::System.IntPtr block_ptr, global::CoreLocation.CLLocationCoordinate2D coordinate, double altitude, global::ObjCRuntime.NativeHandle error) + { + var del = global::ObjCRuntime.BlockLiteral.GetTarget (block_ptr); + if (del is not null) + { + del (coordinate, altitude, global::ObjCRuntime.Runtime.GetNSObject (error)!); + } + } + + internal static unsafe global::ObjCRuntime.BlockLiteral CreateNullableBlock (global::ARKit.GetGeolocationCallback? callback) + { + if (callback is null) + return default (global::ObjCRuntime.BlockLiteral); + return CreateBlock (callback); + } + + [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)] + internal static unsafe global::ObjCRuntime.BlockLiteral CreateBlock (global::ARKit.GetGeolocationCallback callback) + { + delegate* unmanaged trampoline = &Invoke; + return new global::ObjCRuntime.BlockLiteral (trampoline, callback, typeof (SDGetGeolocationCallback), nameof (Invoke)); + } + } + // TODO: generate trampoline for ARKit.GetGeolocationCallback + +} From 4a5e4c8d146f14b9f8e0e171f463ee7530a33acf Mon Sep 17 00:00:00 2001 From: GitHub Actions Autoformatter Date: Wed, 28 May 2025 15:57:28 +0000 Subject: [PATCH 5/5] Auto-format source code --- .../Classes/Data/Trampolines/ARKitTrampolines.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ARKitTrampolines.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ARKitTrampolines.cs index 5ac1e9582ebe..31a91a839c9f 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ARKitTrampolines.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Classes/Data/Trampolines/ARKitTrampolines.cs @@ -12,7 +12,7 @@ namespace TestNamespace; [BindingType] public partial class ARKitTrampolines { - + [Export ("geolocationCallbackHandler", ArgumentSemantic.Copy)] public partial ARKit.GetGeolocationCallback GeolocationCallbackHandler { get; set; }