From 71ed12c8c22aa8047142373a84f245648ee608d6 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Saenz Date: Sun, 26 Jan 2025 19:54:32 -0500 Subject: [PATCH] [Rgen] Add parsing of the ThreadSafe attr to the transformer. --- .../Attributes/ThreadSafeData.cs | 68 ++++++++++++++ .../AttributesNames.cs | 5 + .../Attributes/ThreadSafeDataTests.cs | 92 +++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 src/rgen/Microsoft.Macios.Transformer/Attributes/ThreadSafeData.cs create mode 100644 tests/rgen/Microsoft.Macios.Transformer.Tests/Attributes/ThreadSafeDataTests.cs diff --git a/src/rgen/Microsoft.Macios.Transformer/Attributes/ThreadSafeData.cs b/src/rgen/Microsoft.Macios.Transformer/Attributes/ThreadSafeData.cs new file mode 100644 index 00000000000..3c42045a7c0 --- /dev/null +++ b/src/rgen/Microsoft.Macios.Transformer/Attributes/ThreadSafeData.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; + +namespace Microsoft.Macios.Transformer.Attributes; + +readonly struct ThreadSafeData : IEquatable { + + public bool Safe { get; } = true; + + public ThreadSafeData () : this (true) {} + + public ThreadSafeData (bool safe) + { + Safe = safe; + } + + public static bool TryParse (AttributeData attributeData, + [NotNullWhen (true)] out ThreadSafeData? data) + { + data = null; + var count = attributeData.ConstructorArguments.Length; + if (count == 0) { + data = new(); + return true; + } + bool safe = true; + switch (count) { + case 1: + safe = (bool) attributeData.ConstructorArguments [0].Value!; + break; + default: + // 0 should not be an option.. + return false; + } + + data = new ThreadSafeData (safe); + return true; + } + + public bool Equals (ThreadSafeData other) + => Safe == other.Safe; + + /// + public override bool Equals (object? obj) + { + return obj is ThreadSafeData other && Equals (other); + } + + /// + public override int GetHashCode () + => HashCode.Combine (Safe); + + public static bool operator == (ThreadSafeData x, ThreadSafeData y) + { + return x.Equals (y); + } + + public static bool operator != (ThreadSafeData x, ThreadSafeData y) + { + return !(x == y); + } + + public override string ToString () + => $"{{ ThreadSafe: {Safe} }}"; +} diff --git a/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs b/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs index 1d8a5186630..0fb39f6624b 100644 --- a/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs +++ b/src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs @@ -279,6 +279,11 @@ static class AttributesNames { /// [BindingFlag (AttributeTargets.Class | AttributeTargets.Interface)] public const string TargetAttribute = "TargetAttribute"; + + /// + /// Flags the object as being thread safe. + /// + [BindingAttribute(typeof(BackingFieldTypeData), AttributeTargets.All)] public const string ThreadSafeAttribute = "ThreadSafeAttribute"; /// diff --git a/tests/rgen/Microsoft.Macios.Transformer.Tests/Attributes/ThreadSafeDataTests.cs b/tests/rgen/Microsoft.Macios.Transformer.Tests/Attributes/ThreadSafeDataTests.cs new file mode 100644 index 00000000000..768d5d1865a --- /dev/null +++ b/tests/rgen/Microsoft.Macios.Transformer.Tests/Attributes/ThreadSafeDataTests.cs @@ -0,0 +1,92 @@ +// 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 ThreadSafeDataTests : BaseTransformerTestClass { + + class TestDataTryCreate : IEnumerable { + public IEnumerator GetEnumerator () + { + var path = "/some/random/path.cs"; + + const string threadSageMethod = @" +using System; +using Foundation; +using ObjCRuntime; +using UIKit; + +namespace Test; + +[NoTV] +[MacCatalyst (13, 1)] +[DisableDefaultCtor] +[Abstract] +[BaseType (typeof (NSObject))] +interface UIFeedbackGenerator : UIInteraction { + + [Export (""prepare""), ThreadSafe] + void Prepare (); +} +"; + + yield return [(Source: threadSageMethod, Path: path), new ThreadSafeData()]; + + const string notThreadSafeMethod = @" +using System; +using Foundation; +using ObjCRuntime; +using UIKit; + +namespace Test; + +[NoTV] +[MacCatalyst (13, 1)] +[DisableDefaultCtor] +[Abstract] +[BaseType (typeof (NSObject))] +interface UIFeedbackGenerator : UIInteraction { + + [Export (""prepare""), ThreadSafe (false)] + void Prepare (); +} +"; + + yield return [(Source: notThreadSafeMethod, Path: path), new ThreadSafeData(false)]; + + } + + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + } + + [Theory] + [AllSupportedPlatformsClassData] + void TryCreateTests (ApplePlatform platform, (string Source, string Path) source, ThreadSafeData 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 () + .FirstOrDefault (); + Assert.NotNull (declaration); + + var symbol = semanticModel.GetDeclaredSymbol (declaration); + Assert.NotNull (symbol); + var attribute = symbol.GetAttribute (AttributesNames.ThreadSafeAttribute, ThreadSafeData.TryParse); + Assert.Equal (expectedData, attribute); + } +}