From 56e42abe503b42aad216262b55f611f384e3aede Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Saenz Date: Sat, 25 Jan 2025 18:06:26 -0500 Subject: [PATCH] [Rgen] Add code to parse the NativeAttribute. Not to be confused with the NativeNameAttribute, which is different. --- .../Attributes/NativeData.cs | 89 ++++++++++++++++ .../Attributes/NativeDataTests.cs | 100 ++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 src/rgen/Microsoft.Macios.Transformer/Attributes/NativeData.cs create mode 100644 tests/rgen/Microsoft.Macios.Transformer.Tests/Attributes/NativeDataTests.cs diff --git a/src/rgen/Microsoft.Macios.Transformer/Attributes/NativeData.cs b/src/rgen/Microsoft.Macios.Transformer/Attributes/NativeData.cs new file mode 100644 index 00000000000..759d1cb1ce3 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Transformer/Attributes/NativeData.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; + +namespace Microsoft.Macios.Transformer.Attributes; + +readonly struct NativeData : IEquatable { + + public string? NativeName { get; } + + public NativeData () {} + + public NativeData (string? nativeName) + { + NativeName = nativeName; + } + + + public static bool TryParse (AttributeData attributeData, + [NotNullWhen (true)] out NativeData? data) + { + data = null; + var count = attributeData.ConstructorArguments.Length; + string? nativeName; + + switch (count) { + case 0: + nativeName = null; + break; + case 1: + nativeName = (string) attributeData.ConstructorArguments [0].Value!; + break; + default: + // 0 should not be an option.. + return false; + } + + if (attributeData.NamedArguments.Length == 0) { + data = new (nativeName); + return true; + } + + foreach (var (argumentName, value) in attributeData.NamedArguments) { + switch (argumentName) { + case "NativeName": + nativeName = (string) value.Value!; + break; + default: + data = null; + return false; + } + } + + data = new(nativeName); + return true; + } + + public bool Equals (NativeData other) + => NativeName == other.NativeName; + + /// + public override bool Equals (object? obj) + { + return obj is NativeData other && Equals (other); + } + + /// + public override int GetHashCode () + => HashCode.Combine (NativeName); + + + public static bool operator == (NativeData x, NativeData y) + { + return x.Equals (y); + } + + public static bool operator != (NativeData x, NativeData y) + { + return !(x == y); + } + + /// + public override string ToString () + { + return $"{{ NativeName: '{NativeName}' }}"; + } +} diff --git a/tests/rgen/Microsoft.Macios.Transformer.Tests/Attributes/NativeDataTests.cs b/tests/rgen/Microsoft.Macios.Transformer.Tests/Attributes/NativeDataTests.cs new file mode 100644 index 00000000000..31fa3f1117d --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Transformer.Tests/Attributes/NativeDataTests.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Macios.Generator.Extensions; +using Microsoft.Macios.Transformer.Attributes; +using Xamarin.Tests; +using Xamarin.Utils; + +namespace Microsoft.Macios.Transformer.Tests.Attributes; + +public class NativeDataTests : BaseTransformerTestClass { + + class TestDataTryCreate : IEnumerable { + public IEnumerator GetEnumerator () + { + var path = "/some/random/path.cs"; + var simnpleNativeEnum = @" +using System; +using Foundation; +using ObjCRuntime; + +[Native] +public enum AVAudioQuality : long { + Min = 0, + Low = 0x20, + Medium = 0x40, + High = 0x60, + Max = 0x7F, +} +"; + + yield return [(Sorunce: simnpleNativeEnum, Path: path), new NativeData ()]; + + var nativeEnumWithName = @" +using System; +using Foundation; +using ObjCRuntime; + +[Native (""Test"")] +public enum AVAudioQuality : long { + Min = 0, + Low = 0x20, + Medium = 0x40, + High = 0x60, + Max = 0x7F, +} +"; + + yield return [(Sorunce: nativeEnumWithName, Path: path), new NativeData ("Test")]; + + var nativeEnumWithNameNamed = @" +using System; +using Foundation; +using ObjCRuntime; + +[Native (NativeName = ""Test"")] +public enum AVAudioQuality : long { + Min = 0, + Low = 0x20, + Medium = 0x40, + High = 0x60, + Max = 0x7F, +} +"; + + yield return [(Sorunce: nativeEnumWithNameNamed, Path: path), new NativeData ("Test")]; + } + + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + } + + [Theory] + [AllSupportedPlatformsClassData] + void TryCreateTests (ApplePlatform platform, (string Source, string Path) source, + NativeData expectedData) + { + // create a compilation used to create the transformer + var compilation = CreateCompilation (platform, sources: source); + var syntaxTree = compilation.SyntaxTrees.ForSource (source); + Assert.NotNull (syntaxTree); + + var semanticModel = compilation.GetSemanticModel (syntaxTree); + Assert.NotNull (semanticModel); + + var declaration = syntaxTree.GetRoot () + .DescendantNodes ().OfType () + .LastOrDefault (); + Assert.NotNull (declaration); + + var symbol = semanticModel.GetDeclaredSymbol (declaration); + Assert.NotNull (symbol); + var exportData = symbol.GetAttribute ( + AttributesNames.NativeAttribute, NativeData.TryParse); + Assert.Equal (expectedData, exportData); + } + +}