From e15f298edd3c566bf307494bd11829be95c7ba75 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Sun, 26 Jan 2025 22:01:18 -0500 Subject: [PATCH 1/8] [RGen] Update the transformer generator to access the attr data. (#22052) --- .../AttributeType.cs | 10 + .../Attributes/BindingAttributeData.cs | 3 +- .../Attributes/BindingFlagData.cs | 2 +- .../XamarinBindingAPIGenerator.cs | 183 +++++++++++++----- 4 files changed, 143 insertions(+), 55 deletions(-) create mode 100644 src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/AttributeType.cs diff --git a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/AttributeType.cs b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/AttributeType.cs new file mode 100644 index 000000000000..9548f874b006 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/AttributeType.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Macios.Transformer.Generator; + +public enum AttributeType { + None, + Flag, + Data, +} diff --git a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/Attributes/BindingAttributeData.cs b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/Attributes/BindingAttributeData.cs index f89f5a63d28f..c2c5ea00819a 100644 --- a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/Attributes/BindingAttributeData.cs +++ b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/Attributes/BindingAttributeData.cs @@ -9,6 +9,7 @@ namespace Microsoft.Macios.Transformer.Generator.Attributes; readonly struct BindingAttributeData { + public static string Name = "BindingAttributeAttribute"; public static string Source = @"// using System; @@ -54,7 +55,7 @@ public static bool TryParse (AttributeData attributeData, break; case 2: baseType = ((INamedTypeSymbol) attributeData.ConstructorArguments [0].Value!).ToDisplayString (); - target = (AttributeTargets) attributeData.ConstructorArguments [0].Value!; + target = (AttributeTargets) attributeData.ConstructorArguments [1].Value!; break; default: // 0 should not be an option.. diff --git a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/Attributes/BindingFlagData.cs b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/Attributes/BindingFlagData.cs index 061f3d7d8bfc..8419ac643398 100644 --- a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/Attributes/BindingFlagData.cs +++ b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/Attributes/BindingFlagData.cs @@ -11,7 +11,7 @@ namespace Microsoft.Macios.Transformer.Generator.Attributes; /// Struct representing the data of a Binding attribute. /// readonly struct BindingFlagData { - + public static string Name = "BindingFlagAttribute"; public static string Source = @"// using System; diff --git a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs index 0ee7cdc7a87b..9fd3dc99af0d 100644 --- a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs +++ b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs @@ -20,19 +20,6 @@ namespace Microsoft.Macios.Transformer.Generator; [Generator] public class XamarinBindingAPIGenerator : IIncrementalGenerator { const string Namespace = "Microsoft.Macios.Transformer.Generator"; - const string BindingFlagAttributeName = "BindingFlagAttribute"; - - const string BindingAttributeAttributeSourceCode = @"// -using System; - -namespace Microsoft.Macios.Transformer.Generator; - -[AttributeUsage(AttributeTargets.Field)] -public class BindingAttributeAttribute(Type dataModelType, AttributeTargets target = AttributeTargets.All) : System.Attribute { - public AttributeTargets Target { get; } = target; - public Type DataModelType { get; } = dataModelType; -} -"; public void Initialize (IncrementalGeneratorInitializationContext context) { @@ -50,35 +37,37 @@ public void Initialize (IncrementalGeneratorInitializationContext context) .CreateSyntaxProvider ( (s, _) => s is FieldDeclarationSyntax, (ctx, _) => GetClassDeclarationForSourceGen (ctx)) - .Where (t => t.reportAttributeFound) - .Select ((t, _) => t.Item1); + .Where (t => t.attrType != AttributeType.None); // Generate the source code. context.RegisterSourceOutput (context.CompilationProvider.Combine (provider.Collect ()), ((ctx, t) => GenerateCode (ctx, t.Left, t.Right))); } - static (FieldDeclarationSyntax, bool reportAttributeFound) GetClassDeclarationForSourceGen ( + static (FieldDeclarationSyntax, AttributeType attrType) GetClassDeclarationForSourceGen ( GeneratorSyntaxContext context) { var declarationSyntax = Unsafe.As (context.Node); // Go through all attributes of the field - foreach (AttributeListSyntax attributeListSyntax in declarationSyntax.AttributeLists) + foreach (AttributeListSyntax attributeListSyntax in declarationSyntax.AttributeLists) { foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes) { if (context.SemanticModel.GetSymbolInfo (attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) continue; // if we can't get the symbol, ignore it string attributeName = attributeSymbol.ContainingType.ToDisplayString (); - if (attributeName == $"{Namespace}.{BindingFlagAttributeName}") - return (declarationSyntax, true); + if (attributeName == $"{Namespace}.{BindingFlagData.Name}") + return (declarationSyntax, AttributeType.Flag); + if (attributeName == $"{Namespace}.{BindingAttributeData.Name}") + return (declarationSyntax, AttributeType.Data); } + } - return (declarationSyntax, false); + return (declarationSyntax, AttributeType.None); } - static string [] GetFlagsForTarget (Dictionary flags, + static string [] GetFlagsForTarget (Dictionary flags, AttributeTargets targets) => flags.Where (kv => kv.Value.Targets.HasFlag (targets)) .Select (kv => kv.Key) @@ -117,7 +106,9 @@ static void WriteDataModelExtension (TabbedStringBuilder sb, string dataModel, s // property to store the dictionary modelBlock.AppendLine (); modelBlock.AppendLine ("readonly Dictionary>? _attributesDictionary = null;"); - using (var dictionaryPropertyBlock = modelBlock.CreateBlock ("public Dictionary>? AttributesDictionary", block: true)) { + using (var dictionaryPropertyBlock = + modelBlock.CreateBlock ("public Dictionary>? AttributesDictionary", + block: true)) { dictionaryPropertyBlock.AppendLine ("get => _attributesDictionary;"); using (var initBlock = dictionaryPropertyBlock.CreateBlock ("private init", block: true)) { initBlock.AppendLine ("_attributesDictionary = value;"); @@ -132,7 +123,7 @@ static void WriteDataModelExtension (TabbedStringBuilder sb, string dataModel, s } static void GenerateModelExtension (TabbedStringBuilder sb, string dataModel, - Dictionary flags, AttributeTargets targets, + Dictionary flags, AttributeTargets targets, SourceProductionContext context) { var methodFlags = GetFlagsForTarget (flags, targets); @@ -146,37 +137,81 @@ static AttributeTargets GetTarget (ISymbol symbol) var attrData = symbol.GetAttributes (); // loop over attrs, if we find the BindingFlagAttribute, return the target foreach (var attr in attrData) { - if (attr.AttributeClass?.Name == BindingFlagAttributeName + if (attr.AttributeClass?.Name == BindingFlagData.Name && BindingFlagData.TryParse (attr, out var data)) { return data.Value.Target; } } + return AttributeTargets.All; } + static BindingAttributeData? GetAttributeData (ISymbol symbol) + { + var attrData = symbol.GetAttributes (); + // loop over attrs, if we find the BindingFlagAttribute, return the target + foreach (var attr in attrData) { + if (attr.AttributeClass?.Name == BindingAttributeData.Name + && BindingAttributeData.TryParse (attr, out var data)) { + return data; + } + } + + return null; + } + void GenerateCode (SourceProductionContext context, Compilation compilation, - ImmutableArray declarations) + ImmutableArray<(FieldDeclarationSyntax Field, AttributeType Type)> declarations) { - // Go through all the fields that have the [BindingFlagAttribute] attribute, get the class name - // and field name. - var flags = new Dictionary (); - foreach (var fieldDeclarationSyntax in declarations) { + // loop over the fields and create two dictionaries, one for flags and one for data + var flags = new Dictionary (); + var dataAttributes = + new Dictionary (); + + foreach (var (fieldDeclarationSyntax, attrType) in declarations) { // We need to get semantic model of the class to retrieve metadata. var semanticModel = compilation.GetSemanticModel (fieldDeclarationSyntax.SyntaxTree); - foreach (var variableSyntax in fieldDeclarationSyntax.Declaration.Variables) { - // get the symbol to retrieve the data - if (semanticModel.GetDeclaredSymbol (variableSyntax) is not IFieldSymbol symbol) - continue; - var flagName = $"Has{symbol.Name.Replace ("Attribute", "Flag")}"; - var attrName = symbol.ToDisplayString ().Trim (); - var target = GetTarget (symbol); - flags [flagName] = (attrName, target); + switch (attrType) { + // the attr type will let use know what data can be retrieved from the field + case AttributeType.Flag: + GetFlagsFromField (fieldDeclarationSyntax, semanticModel, flags); + break; + case AttributeType.Data: + GetAttributesFromField (fieldDeclarationSyntax, semanticModel, dataAttributes); + break; } } // all flags are collected, generate the code var sb = new TabbedStringBuilder (new ()); + GenerateDictionaryExtension (sb, flags, dataAttributes); + + // Add the source code to the compilation. + context.AddSource ("AttributeDataDictionaryExtensions.g.cs", + SourceText.From (sb.ToString (), Encoding.UTF8)); + +#pragma warning disable format + // generate the extra methods for the data model, group the fields by the model type based on the target + var models = new (string Model, AttributeTargets Target) [] { + ("EnumMember", AttributeTargets.Field), + ("Parameter", AttributeTargets.Parameter), + ("Property", AttributeTargets.Property), + ("Method", AttributeTargets.Method), + ("Binding", AttributeTargets.Interface), + ("TypeInfo", AttributeTargets.Parameter) + }; +#pragma warning restore format + + foreach (var (model, target) in models) { + GenerateModelExtension (sb, model, flags, target, context); + } + } + + static void GenerateDictionaryExtension (TabbedStringBuilder sb, + Dictionary flags, + Dictionary dataAttributes) + { sb.AppendLine ("// "); sb.AppendLine ("#nullable enable"); sb.AppendLine ("using System;"); @@ -191,10 +226,39 @@ void GenerateCode (SourceProductionContext context, Compilation compilation, using (var methodBlock = classBlock.CreateBlock ( $"public static bool {methodName} (this Dictionary> self)", block: true)) { - methodBlock.AppendLine ($"return self.ContainsKey ({attributeName.AttributeName});"); + methodBlock.AppendLine ($"return self.ContainsKey ({attributeName.AttributeFullName});"); } + classBlock.AppendLine (); } + + // same operation over the data attributes + foreach (var (methodName, attributeInfo) in dataAttributes) { + // property to check if the attribute is present + using (var methodBlock = classBlock.CreateBlock ( + $"public static bool {methodName} (this Dictionary> self)", + block: true)) { + methodBlock.AppendLine ($"return self.ContainsKey ({attributeInfo.AttributeFullName});"); + } + + classBlock.AppendLine (); + // property to access the attribute + using (var methodBlock = classBlock.CreateBlock ( + $"public static {attributeInfo.Data.DataModelType}? Get{attributeInfo.AttributeName} (this Dictionary> self)", + block: true)) { + methodBlock.AppendRaw ( +$@"if (self.{methodName} ()) {{ + var data = self.GetAttribute<{attributeInfo.Data.DataModelType}> ({attributeInfo.AttributeFullName}, {attributeInfo.Data.DataModelType}.TryParse); + return data; +}} else {{ + return null; +}} +"); + } + + classBlock.AppendLine (); + } + // add a generic method that will allow use to retrieve an attribute type classBlock.AppendRaw ( @"public delegate bool TryParseDelegate (AttributeData attributeData, [NotNullWhen (true)] out T? data) where T : struct; @@ -206,29 +270,42 @@ void GenerateCode (SourceProductionContext context, Compilation compilation, foreach (var attr in attrs) { if (tryParse (attr, out var data)) - return data; + return data.Value; } return null; } "); } + } - // Add the source code to the compilation. - context.AddSource ("AttributeDataDictionaryExtensions.g.cs", - SourceText.From (sb.ToString (), Encoding.UTF8)); + static void GetAttributesFromField (FieldDeclarationSyntax fieldDeclarationSyntax, SemanticModel semanticModel, + Dictionary dataAttributes) + { + foreach (var variableSyntax in fieldDeclarationSyntax.Declaration.Variables) { + // get the symbol to retrieve the data + if (semanticModel.GetDeclaredSymbol (variableSyntax) is not IFieldSymbol symbol) + continue; + var flagName = $"Has{symbol.Name}"; + var attributeFullName = symbol.ToDisplayString ().Trim (); + var attrData = GetAttributeData (symbol); + if (attrData is not null) { + dataAttributes [flagName] = (attributeFullName, symbol.Name, attrData.Value); + } + } + } - // generate the extra methods for the data model, group the fields by the model type based on the target - var models = new (string Model, AttributeTargets Target) [] { - ("EnumMember", AttributeTargets.Field), - ("Parameter", AttributeTargets.Parameter), - ("Property", AttributeTargets.Property), - ("Method", AttributeTargets.Method), - ("Binding", AttributeTargets.Interface), - ("TypeInfo", AttributeTargets.Parameter) - }; - foreach (var (model, target) in models) { - GenerateModelExtension (sb, model, flags, target, context); + static void GetFlagsFromField (FieldDeclarationSyntax fieldDeclarationSyntax, SemanticModel semanticModel, + Dictionary flags) + { + foreach (var variableSyntax in fieldDeclarationSyntax.Declaration.Variables) { + // get the symbol to retrieve the data + if (semanticModel.GetDeclaredSymbol (variableSyntax) is not IFieldSymbol symbol) + continue; + var flagName = $"Has{symbol.Name.Replace ("Attribute", "Flag")}"; + var attrName = symbol.ToDisplayString ().Trim (); + var target = GetTarget (symbol); + flags [flagName] = (attrName, target); } } } From b56304edcac235aad7da01e99205f2e8bd864a39 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Mon, 27 Jan 2025 11:26:41 -0500 Subject: [PATCH 2/8] [RGen] Add the code generation for the data attributes. (#22053) This change adds two more properties to the data models when they are compile. One to check if the data attr is present, and a second one with the attribute details. The boolean property is decorated acordingly to help with the nullability analisys. An example of the generated code can be found here: https://gist.github.com/mandel-macaque/04185ea481ca30ef9ff2ecf6dbf51ea0 --------- Co-authored-by: GitHub Actions Autoformatter --- .../XamarinBindingAPIGenerator.cs | 55 +++++++++++++++++-- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs index 9fd3dc99af0d..df6dedc052a3 100644 --- a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs +++ b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs @@ -73,6 +73,14 @@ static string [] GetFlagsForTarget (Dictionary kv.Key) .ToArray (); + static (string AttributeFullName, string AttributeName, BindingAttributeData Data) [] GetAttributesForTarget ( + Dictionary dataAttribute, + AttributeTargets targets) + => dataAttribute.Where (kv => kv.Value.Data.Target.HasFlag (targets)) + .Select (kv => kv.Value) + .ToArray (); + + static void WriteFlagProperty (TabbedStringBuilder sb, string flagName) { // write the backing field @@ -83,13 +91,34 @@ static void WriteFlagProperty (TabbedStringBuilder sb, string flagName) } } - static void WriteDataModelExtension (TabbedStringBuilder sb, string dataModel, string [] flags) + static void WriteAttributeProperty (TabbedStringBuilder sb, + (string AttributeFullName, string AttributeName, BindingAttributeData Data) attrData) + { + // add a property that will state if we have the attr, this will help with nullability + sb.AppendLine ($"readonly bool _has{attrData.AttributeName} = false;"); + sb.AppendLine ($"[MemberNotNullWhen (true, nameof ({attrData.AttributeName}))]"); + using (var flagPropertyBlock = sb.CreateBlock ($"public bool Has{attrData.AttributeName}", block: true)) { + flagPropertyBlock.AppendLine ($"get => _has{attrData.AttributeName};"); + flagPropertyBlock.AppendLine ($"private init => _has{attrData.AttributeName} = value;"); + } + sb.AppendLine (); + sb.AppendLine ($"readonly {attrData.Data.DataModelType}? _{attrData.AttributeName} = null;"); + // decorate to help with nullability + using (var attributePropertyBlock = sb.CreateBlock ($"public {attrData.Data.DataModelType}? {attrData.AttributeName}", block: true)) { + attributePropertyBlock.AppendLine ($"get => _{attrData.AttributeName};"); + attributePropertyBlock.AppendLine ($"private init => _{attrData.AttributeName} = value;"); + } + } + + static void WriteDataModelExtension (TabbedStringBuilder sb, string dataModel, string [] flags, + (string AttributeFullName, string AttributeName, BindingAttributeData Data) [] attributes) { sb.Clear (); sb.AppendLine ("// "); sb.AppendLine ("#nullable enable"); sb.AppendLine (""); sb.AppendLine ("using System;"); + sb.AppendLine ("using System.Diagnostics.CodeAnalysis;"); sb.AppendLine ("using Microsoft.CodeAnalysis;"); sb.AppendLine ("using Microsoft.Macios.Transformer.Extensions;"); sb.AppendLine (); @@ -103,6 +132,11 @@ static void WriteDataModelExtension (TabbedStringBuilder sb, string dataModel, s WriteFlagProperty (modelBlock, flag); } + foreach (var attrData in attributes) { + modelBlock.AppendLine (); + WriteAttributeProperty (modelBlock, attrData); + } + // property to store the dictionary modelBlock.AppendLine (); modelBlock.AppendLine ("readonly Dictionary>? _attributesDictionary = null;"); @@ -114,7 +148,15 @@ static void WriteDataModelExtension (TabbedStringBuilder sb, string dataModel, s initBlock.AppendLine ("_attributesDictionary = value;"); using (var ifBlock = initBlock.CreateBlock ("if (_attributesDictionary is not null)", block: true)) { foreach (var flag in flags) { - ifBlock.AppendLine ($"_{flag} = _attributesDictionary.{flag} ();"); + ifBlock.AppendLine ($"{flag} = _attributesDictionary.{flag} ();"); + } + + foreach (var attributeData in attributes) { + // check if the attribute is present, if it is, set the value + ifBlock.AppendLine ($"Has{attributeData.AttributeName} = _attributesDictionary.Has{attributeData.AttributeName} ();"); + using (var attrIfBlock = ifBlock.CreateBlock ($"if (Has{attributeData.AttributeName})", block: true)) { + attrIfBlock.AppendLine ($"{attributeData.AttributeName} = _attributesDictionary.Get{attributeData.AttributeName} ();"); + } } } } @@ -123,11 +165,14 @@ static void WriteDataModelExtension (TabbedStringBuilder sb, string dataModel, s } static void GenerateModelExtension (TabbedStringBuilder sb, string dataModel, - Dictionary flags, AttributeTargets targets, + Dictionary flags, + Dictionary attributes, + AttributeTargets targets, SourceProductionContext context) { var methodFlags = GetFlagsForTarget (flags, targets); - WriteDataModelExtension (sb, dataModel, methodFlags); + var methodAttributes = GetAttributesForTarget (attributes, targets); + WriteDataModelExtension (sb, dataModel, methodFlags, methodAttributes); context.AddSource ($"{dataModel}.Transformer.g.cs", SourceText.From (sb.ToString (), Encoding.UTF8)); } @@ -204,7 +249,7 @@ void GenerateCode (SourceProductionContext context, Compilation compilation, #pragma warning restore format foreach (var (model, target) in models) { - GenerateModelExtension (sb, model, flags, target, context); + GenerateModelExtension (sb, model, flags, dataAttributes, target, context); } } From e7419d9840c3053d78b49997bde10835b3d592e6 Mon Sep 17 00:00:00 2001 From: Mauro Agnoletti Date: Mon, 27 Jan 2025 16:26:12 -0500 Subject: [PATCH 3/8] Update Hot Restart Client to 17.14.83-g77e8f56103 (#22033) --- msbuild/Directory.Build.props | 2 +- msbuild/Makefile | 2 +- .../Xamarin.iOS.Tasks.Windows/Xamarin.iOS.Tasks.Windows.csproj | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/msbuild/Directory.Build.props b/msbuild/Directory.Build.props index 63c5da1fbd75..1e02d1a83c81 100644 --- a/msbuild/Directory.Build.props +++ b/msbuild/Directory.Build.props @@ -16,7 +16,7 @@ --> [3.0.13] - [17.14.51-gd03e25086a] + [17.14.83-g77e8f56103] diff --git a/msbuild/Makefile b/msbuild/Makefile index 8ce64128f360..1dbee190e1b0 100644 --- a/msbuild/Makefile +++ b/msbuild/Makefile @@ -53,7 +53,7 @@ IOS_WINDOWS_TARGETS = \ TASK_ASSEMBLIES = Xamarin.MacDev.Tasks $(LOCALIZATION_ASSEMBLIES) IOS_WINDOWS_TASK_ASSEMBLIES = Xamarin.iOS.Tasks.Windows -IOS_WINDOWS_DEPENDENCIES = Xamarin.iOS.Windows.Client iSign.Core ws2_32 System.Diagnostics.Tracer System.Formats.Asn1 System.Buffers System.Memory System.Numerics.Vectors System.Runtime.CompilerServices.Unsafe System.Security.Cryptography.ProtectedData System.Security.Cryptography.Pkcs Microsoft.Win32.Registry System.Security.AccessControl System.Security.Principal.Windows +IOS_WINDOWS_DEPENDENCIES = Xamarin.iOS.Windows.Client iSign.Core ws2_32 System.Diagnostics.Tracer System.Formats.Asn1 System.Buffers System.Memory System.Numerics.Vectors System.Runtime.CompilerServices.Unsafe System.Security.Cryptography.ProtectedData System.Security.Cryptography.Pkcs Microsoft.Win32.Registry IOS_WINDOWS_MOBILEDEVICE_TOOLS = iMobileDevice-net bz2 getopt ideviceactivation idevicebackup idevicebackup2 idevicecrashreport idevicedate idevicedebug idevicedebugserverproxy idevicediagnostics ideviceenterrecovery ideviceimagemounter ideviceinfo ideviceinstaller idevicename idevicenotificationproxy idevicepair ideviceprovision idevicerestore idevicescreenshot idevicesyslog idevice_id imobiledevice-net-lighthouse imobiledevice ios_webkit_debug_proxy iproxy irecovery irecovery libcharset libcurl LIBEAY32 libiconv libusb-1.0 libusb0 libxml2 lzma pcre pcreposix plist plistutil plist_cmp plist_test pthreadVC3 readline SSLEAY32 usbmuxd usbmuxd vcruntime140 zip zlib1 PROPS_AND_TARGETS = \ diff --git a/msbuild/Xamarin.iOS.Tasks.Windows/Xamarin.iOS.Tasks.Windows.csproj b/msbuild/Xamarin.iOS.Tasks.Windows/Xamarin.iOS.Tasks.Windows.csproj index 1173aaeb0905..7cbc1215d9bb 100644 --- a/msbuild/Xamarin.iOS.Tasks.Windows/Xamarin.iOS.Tasks.Windows.csproj +++ b/msbuild/Xamarin.iOS.Tasks.Windows/Xamarin.iOS.Tasks.Windows.csproj @@ -22,6 +22,7 @@ + @@ -38,7 +39,7 @@ PreserveNewest - + From db76488282bba990f3afeb74c51af335d848ab80 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Tue, 28 Jan 2025 08:19:10 -0500 Subject: [PATCH 4/8] [Rgen] Generate casting expressions for native enums. (#22059) Co-authored-by: GitHub Actions Autoformatter --- .../DataModel/TypeInfo.cs | 2 +- .../BindingSyntaxFactory.ObjCRuntime.cs | 35 +++++++++- .../Extensions/SpecialTypeExtensions.cs | 2 +- .../DataModel/TypeInfoToMarshallTypeTests.cs | 2 +- .../BindingSyntaxFactoryObjCRuntimeTests.cs | 65 +++++++++++++++++++ .../TestDataFactory.cs | 5 +- 6 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 tests/rgen/Microsoft.Macios.Generator.Tests/Emitters/BindingSyntaxFactoryObjCRuntimeTests.cs diff --git a/src/rgen/Microsoft.Macios.Generator/DataModel/TypeInfo.cs b/src/rgen/Microsoft.Macios.Generator/DataModel/TypeInfo.cs index 9ec45041ef0d..b4b0b82903c6 100644 --- a/src/rgen/Microsoft.Macios.Generator/DataModel/TypeInfo.cs +++ b/src/rgen/Microsoft.Macios.Generator/DataModel/TypeInfo.cs @@ -235,7 +235,7 @@ public override int GetHashCode () const string IntPtr = "IntPtr"; const string UIntPtr = "UIntPtr"; - public string? ToMarshallType (ReferenceKind referenceKind) + public string? ToMarshallType () { #pragma warning disable format var type = this switch { diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.ObjCRuntime.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.ObjCRuntime.cs index 98b7b6c402c5..945dc1c38e6e 100644 --- a/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.ObjCRuntime.cs +++ b/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.ObjCRuntime.cs @@ -5,10 +5,13 @@ using System.Collections.Immutable; using System.Linq; using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Macios.Generator.Attributes; using Microsoft.Macios.Generator.DataModel; using Microsoft.Macios.Generator.Extensions; using TypeInfo = Microsoft.Macios.Generator.DataModel.TypeInfo; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Microsoft.Macios.Generator.Emitters; @@ -16,6 +19,34 @@ static partial class BindingSyntaxFactory { readonly static string objc_msgSend = "objc_msgSend"; readonly static string objc_msgSendSuper = "objc_msgSendSuper"; + /// + /// Returns the expression needed to cast a parameter to its native type. + /// + /// The parameter whose castin we need to generate. The type info has to be + /// and enum and be marked as native. If it is not the method returns null + /// + internal static CastExpressionSyntax? CastToNative (in Parameter parameter) + { + // not an enum and not a native value. we cannot calculate the casting expression. + if (!parameter.Type.IsEnum || !parameter.Type.IsNativeEnum) + return null; + + // build a casting expression based on the marshall type of the typeinfo + var marshalType = parameter.Type.ToMarshallType (); + if (marshalType is null) + // cannot calculate the marshal, return null + return null; + + var enumBackingValue = parameter.Type.EnumUnderlyingType.Value.GetKeyword (); + var castExpression = CastExpression (IdentifierName (marshalType), // (IntPtr/UIntPtr) cast + CastExpression ( + IdentifierName (enumBackingValue), + IdentifierName (parameter.Name) + .WithLeadingTrivia (Space)) + .WithLeadingTrivia (Space)); // (backingfield) (variable) cast + return castExpression; + } + static string? GetObjCMessageSendMethodName (ExportData exportData, TypeInfo returnType, ImmutableArray parameters, bool isSuper = false, bool isStret = false) where T : Enum { @@ -42,7 +73,7 @@ static partial class BindingSyntaxFactory { } // return types do not have a reference kind - sb.Append (returnType.ToMarshallType (ReferenceKind.None)); + sb.Append (returnType.ToMarshallType ()); sb.Append ('_'); // append the msg method based if it is for super or not, do not append '_' intimidatingly, since if we do // not have parameters, we are done @@ -53,7 +84,7 @@ static partial class BindingSyntaxFactory { // loop over params and get their native handler name if (parameters.Length > 0) { sb.Append ('_'); - sb.AppendJoin ('_', parameters.Select (p => p.Type.ToMarshallType (p.ReferenceKind))); + sb.AppendJoin ('_', parameters.Select (p => p.Type.ToMarshallType ())); } // check if we do have a custom marshall exception set for the export diff --git a/src/rgen/Microsoft.Macios.Generator/Extensions/SpecialTypeExtensions.cs b/src/rgen/Microsoft.Macios.Generator/Extensions/SpecialTypeExtensions.cs index 6e14d5ca519a..21049b621965 100644 --- a/src/rgen/Microsoft.Macios.Generator/Extensions/SpecialTypeExtensions.cs +++ b/src/rgen/Microsoft.Macios.Generator/Extensions/SpecialTypeExtensions.cs @@ -17,7 +17,7 @@ static class SpecialTypeExtensions { /// /// The special type to convert. /// The string representation of the keyword. - public static string? GetKeyword (this SpecialType self) + public static string GetKeyword (this SpecialType self) { var kind = self switch { SpecialType.System_Void => SyntaxKind.VoidKeyword, diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/DataModel/TypeInfoToMarshallTypeTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/DataModel/TypeInfoToMarshallTypeTests.cs index 4ac0910c8408..6f50de75e446 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/DataModel/TypeInfoToMarshallTypeTests.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/DataModel/TypeInfoToMarshallTypeTests.cs @@ -205,7 +205,7 @@ void ToMarshallType (ApplePlatform platform, string inputText, string expectedTy Assert.NotNull (declaration); Assert.True (Property.TryCreate (declaration, semanticModel, out var changes)); Assert.NotNull (changes); - var marshall = changes.Value.ReturnType.ToMarshallType (ReferenceKind.None); + var marshall = changes.Value.ReturnType.ToMarshallType (); Assert.NotNull (marshall); Assert.Equal (expectedTypeName, marshall); } diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Emitters/BindingSyntaxFactoryObjCRuntimeTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Emitters/BindingSyntaxFactoryObjCRuntimeTests.cs new file mode 100644 index 000000000000..847c6cbc1e6f --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Emitters/BindingSyntaxFactoryObjCRuntimeTests.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.Macios.Generator.DataModel; +using Xunit; +using static Microsoft.Macios.Generator.Emitters.BindingSyntaxFactory; +using static Microsoft.Macios.Generator.Tests.TestDataFactory; + +namespace Microsoft.Macios.Generator.Tests.Emitters; + +public class BindingSyntaxFactoryObjCRuntimeTests { + + class TestDataCodeChangesFromClassDeclaration : IEnumerable { + public IEnumerator GetEnumerator () + { + + // not enum parameter + var boolParam = new Parameter ( + position: 0, + type: ReturnTypeForBool (), + name: "myParam"); + yield return [boolParam, null!]; + + // not smart enum parameter + var enumParam = new Parameter ( + position: 0, + type: ReturnTypeForEnum ("MyEnum", isNativeEnum: false), + name: "myParam"); + + yield return [enumParam, null!]; + + // int64 + var byteEnum = new Parameter ( + position: 0, + type: ReturnTypeForEnum ("MyEnum", isNativeEnum: true, underlyingType: SpecialType.System_Int64), + name: "myParam"); + yield return [byteEnum, "(IntPtr) (long) myParam"]; + + // uint64 + var int64Enum = new Parameter ( + position: 0, + type: ReturnTypeForEnum ("MyEnum", isNativeEnum: true, underlyingType: SpecialType.System_UInt64), + name: "myParam"); + yield return [int64Enum, "(UIntPtr) (ulong) myParam"]; + } + + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + } + + [Theory] + [ClassData (typeof (TestDataCodeChangesFromClassDeclaration))] + void CastToNativeTests (Parameter parameter, string? expectedCast) + { + var expression = CastToNative (parameter); + if (expectedCast is null) { + Assert.Null (expression); + } else { + Assert.NotNull (expression); + Assert.Equal (expectedCast, expression?.ToString ()); + } + } +} diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/TestDataFactory.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/TestDataFactory.cs index 5c28ac2e00d2..6caa973ca695 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/TestDataFactory.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/TestDataFactory.cs @@ -181,7 +181,8 @@ public static TypeInfo ReturnTypeForStruct (string structName) isStruct: true ) { Parents = ["System.ValueType", "object"] }; - public static TypeInfo ReturnTypeForEnum (string enumName, bool isSmartEnum = false, bool isNativeEnum = false) + public static TypeInfo ReturnTypeForEnum (string enumName, bool isSmartEnum = false, bool isNativeEnum = false, + SpecialType underlyingType = SpecialType.System_Int32) => new ( name: enumName, isBlittable: true, @@ -199,7 +200,7 @@ public static TypeInfo ReturnTypeForEnum (string enumName, bool isSmartEnum = fa "System.ISpanFormattable" ], IsNativeEnum = isNativeEnum, - EnumUnderlyingType = SpecialType.System_Int32, + EnumUnderlyingType = underlyingType, }; public static TypeInfo ReturnTypeForArray (string type, bool isNullable = false, bool isBlittable = false) From 695ef24a93af1e96ea1930beaa4a041c1343fbab Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Tue, 28 Jan 2025 11:28:04 -0500 Subject: [PATCH 5/8] [Rgen] Fix a bug in how the target flags are managed. (#22054) We cannot use an or a & we need to provide a list, that leater is tested against a flag that has been either | or &. --------- Co-authored-by: GitHub Actions Autoformatter --- .../XamarinBindingAPIGenerator.cs | 25 ++++++++++--------- .../AttributesNames.cs | 2 ++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs index df6dedc052a3..72cff4800382 100644 --- a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs +++ b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs @@ -68,15 +68,16 @@ public void Initialize (IncrementalGeneratorInitializationContext context) } static string [] GetFlagsForTarget (Dictionary flags, - AttributeTargets targets) - => flags.Where (kv => kv.Value.Targets.HasFlag (targets)) + AttributeTargets [] targets) + => flags.Where (kv => targets.Any (t => kv.Value.Targets.HasFlag (t))) .Select (kv => kv.Key) .ToArray (); static (string AttributeFullName, string AttributeName, BindingAttributeData Data) [] GetAttributesForTarget ( Dictionary dataAttribute, - AttributeTargets targets) - => dataAttribute.Where (kv => kv.Value.Data.Target.HasFlag (targets)) + AttributeTargets [] targets) + // return all the attributes that have at least one of the targets + => dataAttribute.Where (kv => targets.Any (t => kv.Value.Data.Target.HasFlag (t))) .Select (kv => kv.Value) .ToArray (); @@ -167,7 +168,7 @@ static void WriteDataModelExtension (TabbedStringBuilder sb, string dataModel, s static void GenerateModelExtension (TabbedStringBuilder sb, string dataModel, Dictionary flags, Dictionary attributes, - AttributeTargets targets, + AttributeTargets [] targets, SourceProductionContext context) { var methodFlags = GetFlagsForTarget (flags, targets); @@ -238,13 +239,13 @@ void GenerateCode (SourceProductionContext context, Compilation compilation, #pragma warning disable format // generate the extra methods for the data model, group the fields by the model type based on the target - var models = new (string Model, AttributeTargets Target) [] { - ("EnumMember", AttributeTargets.Field), - ("Parameter", AttributeTargets.Parameter), - ("Property", AttributeTargets.Property), - ("Method", AttributeTargets.Method), - ("Binding", AttributeTargets.Interface), - ("TypeInfo", AttributeTargets.Parameter) + var models = new (string Model, AttributeTargets[] Targets) [] { + ("EnumMember", [AttributeTargets.Field]), + ("Parameter", [AttributeTargets.Parameter]), + ("Property", [AttributeTargets.Property]), + ("Method", [AttributeTargets.Method]), + ("Binding", [AttributeTargets.Interface, AttributeTargets.Class, AttributeTargets.Enum, AttributeTargets.Struct]), + ("TypeInfo", [AttributeTargets.Parameter]) }; #pragma warning restore format diff --git a/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs b/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs index a3ca39d41f3c..1d8a5186630b 100644 --- a/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs +++ b/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs @@ -145,7 +145,9 @@ static class AttributesNames { /// [BindingFlag (AttributeTargets.Class | AttributeTargets.Interface)] public const string ModelAttribute = "Foundation.ModelAttribute"; + [BindingAttribute(typeof(NativeData), AttributeTargets.Enum)] public const string NativeAttribute = "ObjCRuntime.NativeAttribute"; + [BindingAttribute(typeof(NativeData), AttributeTargets.Enum | AttributeTargets.Struct)] public const string NativeNameAttribute = "ObjCRuntime.NativeNameAttribute"; /// From 208f61a97fe3c8eedd1c2e898737652fff63f3b0 Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Tue, 28 Jan 2025 13:58:58 -0500 Subject: [PATCH 6/8] [CI] Use macOS Sequoia for building and testing (#22028) --- tools/devops/automation/build-pipeline.yml | 2 +- tools/devops/automation/build-pull-request.yml | 2 +- .../devops/automation/templates/pipelines/api-diff-pipeline.yml | 2 +- tools/devops/automation/templates/pipelines/build-pipeline.yml | 2 +- .../automation/templates/pipelines/run-tests-pipeline.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/devops/automation/build-pipeline.yml b/tools/devops/automation/build-pipeline.yml index de5ac8964149..ee5ba61c090e 100644 --- a/tools/devops/automation/build-pipeline.yml +++ b/tools/devops/automation/build-pipeline.yml @@ -14,7 +14,7 @@ parameters: - name: macOSName # comes from the build agent demand named macOS.Name displayName: Name of the version of macOS to use type: string - default: 'Sonoma' + default: 'Sequoia' - name: runGovernanceTests displayName: Run Governance Checks diff --git a/tools/devops/automation/build-pull-request.yml b/tools/devops/automation/build-pull-request.yml index 0733fbabcb11..c1ec04a9f8ac 100644 --- a/tools/devops/automation/build-pull-request.yml +++ b/tools/devops/automation/build-pull-request.yml @@ -15,7 +15,7 @@ parameters: - name: macOSName # comes from the build agent demand named macOS.Name displayName: Name of the version of macOS to use type: string - default: 'Sonoma' + default: 'Sequoia' - name: runGovernanceTests displayName: Run Governance Checks diff --git a/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml b/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml index 991def1ca58b..3a22e978aa6e 100644 --- a/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml +++ b/tools/devops/automation/templates/pipelines/api-diff-pipeline.yml @@ -11,7 +11,7 @@ parameters: - name: macOSName # comes from the build agent demand named macOS.Name displayName: Name of the version of macOS to use type: string - default: 'Sonoma' + default: 'Sequoia' - name: pool type: string diff --git a/tools/devops/automation/templates/pipelines/build-pipeline.yml b/tools/devops/automation/templates/pipelines/build-pipeline.yml index 7271c276caff..3133a1b5ae2a 100644 --- a/tools/devops/automation/templates/pipelines/build-pipeline.yml +++ b/tools/devops/automation/templates/pipelines/build-pipeline.yml @@ -11,7 +11,7 @@ parameters: - name: macOSName # comes from the build agent demand named macOS.Name displayName: Name of the version of macOS to use type: string - default: 'Sonoma' + default: 'Sequoia' - name: pool type: string diff --git a/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml b/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml index b87e0def11af..210d8a997969 100644 --- a/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml +++ b/tools/devops/automation/templates/pipelines/run-tests-pipeline.yml @@ -14,7 +14,7 @@ parameters: - name: macOSName # comes from the build agent demand named macOS.Name displayName: Name of the version of macOS to use type: string - default: 'Sonoma' + default: 'Sequoia' - name: runTests displayName: Run Simulator Tests From a70bd3e2073b3df8d13fb6c0e99a5129c6d9698d Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Wed, 29 Jan 2025 06:20:18 -0500 Subject: [PATCH 7/8] [RGen] Add attributes to the accessor struct in the generator. (#22055) --- .../XamarinBindingAPIGenerator.cs | 1 + .../DataModel/Accessor.Transformer.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs index 72cff4800382..7d6bfee038f4 100644 --- a/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs +++ b/src/rgen/Microsoft.Macios.Transformer.Generator/Microsoft.Macios.Transformer.Generator/XamarinBindingAPIGenerator.cs @@ -242,6 +242,7 @@ void GenerateCode (SourceProductionContext context, Compilation compilation, var models = new (string Model, AttributeTargets[] Targets) [] { ("EnumMember", [AttributeTargets.Field]), ("Parameter", [AttributeTargets.Parameter]), + ("Accessor", [AttributeTargets.Property]), ("Property", [AttributeTargets.Property]), ("Method", [AttributeTargets.Method]), ("Binding", [AttributeTargets.Interface, AttributeTargets.Class, AttributeTargets.Enum, AttributeTargets.Struct]), diff --git a/src/rgen/Microsoft.Macios.Transformer/DataModel/Accessor.Transformer.cs b/src/rgen/Microsoft.Macios.Transformer/DataModel/Accessor.Transformer.cs index 3220114a142d..372467045775 100644 --- a/src/rgen/Microsoft.Macios.Transformer/DataModel/Accessor.Transformer.cs +++ b/src/rgen/Microsoft.Macios.Transformer/DataModel/Accessor.Transformer.cs @@ -8,4 +8,6 @@ namespace Microsoft.Macios.Generator.DataModel; readonly partial struct Accessor { public ExportData? ExportPropertyData { get; init; } + + public Accessor () { } } From 3b647ca29d46440ec38057dbdf291be370e67416 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Wed, 29 Jan 2025 06:21:16 -0500 Subject: [PATCH 8/8] [Rgen] Add method in the syntax factory to cast a bool to a byte. (#22061) This is needed because the pinvokes do not use bools but bytes. --- .../BindingSyntaxFactory.ObjCRuntime.cs | 29 +++++++++++++++++++ .../BindingSyntaxFactoryObjCRuntimeTests.cs | 17 +++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.ObjCRuntime.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.ObjCRuntime.cs index 945dc1c38e6e..ec0622d4dbc0 100644 --- a/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.ObjCRuntime.cs +++ b/src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.ObjCRuntime.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Macios.Generator.Attributes; using Microsoft.Macios.Generator.DataModel; @@ -47,6 +48,34 @@ static partial class BindingSyntaxFactory { return castExpression; } + /// + /// Returns the expression needed to cast a bool to a byte to be used in a native call. + /// + /// The parameter to cast. + /// A conditional expression that casts a bool to a byte. + internal static ConditionalExpressionSyntax? CastToByte (in Parameter parameter) + { + if (parameter.Type.SpecialType != SpecialType.System_Boolean) + return null; + // (byte) 1 + var castOne = CastExpression ( + PredefinedType (Token (SyntaxKind.ByteKeyword)), + LiteralExpression (SyntaxKind.NumericLiteralExpression, Literal (1)).WithLeadingTrivia (Space).WithTrailingTrivia (Space) + ); + // (byte) 0 + var castZero = CastExpression ( + PredefinedType (Token (SyntaxKind.ByteKeyword)), + LiteralExpression (SyntaxKind.NumericLiteralExpression, Literal (0)).WithLeadingTrivia (Space) + ).WithLeadingTrivia (Space); + + // with this exact space count + // foo ? (byte) 1 : (byte) 0 + return ConditionalExpression ( + condition: IdentifierName (parameter.Name).WithTrailingTrivia (Space), + whenTrue: castOne.WithLeadingTrivia (Space), + whenFalse: castZero); + } + static string? GetObjCMessageSendMethodName (ExportData exportData, TypeInfo returnType, ImmutableArray parameters, bool isSuper = false, bool isStret = false) where T : Enum { diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Emitters/BindingSyntaxFactoryObjCRuntimeTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/Emitters/BindingSyntaxFactoryObjCRuntimeTests.cs index 847c6cbc1e6f..4a34d717499e 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/Emitters/BindingSyntaxFactoryObjCRuntimeTests.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Emitters/BindingSyntaxFactoryObjCRuntimeTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.Macios.Generator.Tests.Emitters; public class BindingSyntaxFactoryObjCRuntimeTests { - class TestDataCodeChangesFromClassDeclaration : IEnumerable { + class TestDataCastToNativeTests : IEnumerable { public IEnumerator GetEnumerator () { @@ -51,7 +51,7 @@ public IEnumerator GetEnumerator () } [Theory] - [ClassData (typeof (TestDataCodeChangesFromClassDeclaration))] + [ClassData (typeof (TestDataCastToNativeTests))] void CastToNativeTests (Parameter parameter, string? expectedCast) { var expression = CastToNative (parameter); @@ -62,4 +62,17 @@ void CastToNativeTests (Parameter parameter, string? expectedCast) Assert.Equal (expectedCast, expression?.ToString ()); } } + + [Fact] + void CastToByteTests () + { + var boolParameter = new Parameter (0, ReturnTypeForBool (), "myParameter"); + var conditionalExpr = CastToByte (boolParameter); + Assert.NotNull (conditionalExpr); + Assert.Equal ("myParameter ? (byte) 1 : (byte) 0", conditionalExpr.ToString ()); + + var intParameter = new Parameter (1, ReturnTypeForInt (), "myParameter"); + conditionalExpr = CastToByte (intParameter); + Assert.Null (conditionalExpr); + } }