Skip to content

Commit

Permalink
[Rge] Implement the code in the transformer to parse Export attrs. (#…
Browse files Browse the repository at this point in the history
…22018)

Add the code that will parse an attribute data for a
Foundation.ExportAttribute.

---------

Co-authored-by: GitHub Actions Autoformatter <[email protected]>
  • Loading branch information
mandel-macaque and GitHub Actions Autoformatter authored Jan 20, 2025
1 parent 915d7cc commit efd6ff9
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ public static bool HasAttribute (this ISymbol symbol, string attribute)
return false;
}

delegate string? GetAttributeNames ();
internal delegate string? GetAttributeNames ();

delegate bool TryParse<T> (AttributeData data, [NotNullWhen (true)] out T? value) where T : struct;
internal delegate bool TryParse<T> (AttributeData data, [NotNullWhen (true)] out T? value) where T : struct;

static T? GetAttribute<T> (this ISymbol symbol, GetAttributeNames getAttributeNames, TryParse<T> tryParse)
internal static T? GetAttribute<T> (this ISymbol symbol, GetAttributeNames getAttributeNames, TryParse<T> tryParse)
where T : struct
{
var attributes = symbol.GetAttributeData ();
Expand All @@ -114,6 +114,9 @@ public static bool HasAttribute (this ISymbol symbol, string attribute)
return null;
}

internal static T? GetAttribute<T> (this ISymbol symbol, string attributeName, TryParse<T> tryParse) where T : struct
=> GetAttribute (symbol, () => attributeName, tryParse);

/// <summary>
/// Returns if a type is blittable or not.
/// </summary>
Expand Down
92 changes: 90 additions & 2 deletions src/rgen/Microsoft.Macios.Transformer/Attributes/ExportData.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,95 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using ObjCRuntime;

namespace Microsoft.Macios.Transformer.Attributes;

public struct ExportData : IEquatable<ExportData> {

/// <summary>
/// The exported native selector.
/// </summary>
public string? Selector { get; }

/// <summary>
/// Argument semantics to use with the selector.
/// </summary>
public ArgumentSemantic ArgumentSemantic { get; } = ArgumentSemantic.None;

public ExportData (string? selector)
{
Selector = selector;
}

public ExportData (string? selector, ArgumentSemantic argumentSemantic)
{
Selector = selector;
ArgumentSemantic = argumentSemantic;
}

/// <summary>
/// Try to parse the attribute data to retrieve the information of an ExportAttribute&lt;T&gt;.
/// </summary>
/// <param name="attributeData">The attribute data to be parsed.</param>
/// <param name="data">The parsed data. Null if we could not parse the attribute data.</param>
/// <returns>True if the data was parsed.</returns>
public static bool TryParse (AttributeData attributeData,
[NotNullWhen (true)] out ExportData? data)
{
data = null;
var count = attributeData.ConstructorArguments.Length;
string? selector;
ArgumentSemantic argumentSemantic = ArgumentSemantic.None;

// custom marshal directive values

switch (count) {
case 1:
selector = (string?) attributeData.ConstructorArguments [0].Value!;
break;
case 2:
// there are two possible cases in this situation.
// 1. The second argument is an ArgumentSemantic
// 2. The second argument is a T
selector = (string?) attributeData.ConstructorArguments [0].Value!;
argumentSemantic = (ArgumentSemantic) attributeData.ConstructorArguments [1].Value!;
break;
default:
// 0 should not be an option..
return false;
}

if (attributeData.NamedArguments.Length == 0) {
data = new (selector, argumentSemantic);
return true;
}

foreach (var (name, value) in attributeData.NamedArguments) {
switch (name) {
case "Selector":
selector = (string?) value.Value!;
break;
case "ArgumentSemantic":
argumentSemantic = (ArgumentSemantic) value.Value!;
break;
default:
data = null;
return false;
}
}

data = new (selector, argumentSemantic);
return true;
}

public bool Equals (ExportData other)
{
throw new NotImplementedException ();
if (Selector != other.Selector)
return false;
return ArgumentSemantic == other.ArgumentSemantic;
}

/// <inheritdoc />
Expand All @@ -19,7 +101,7 @@ public override bool Equals (object? obj)
/// <inheritdoc />
public override int GetHashCode ()
{
throw new NotImplementedException ();
return HashCode.Combine (Selector, ArgumentSemantic);
}

public static bool operator == (ExportData x, ExportData y)
Expand All @@ -31,4 +113,10 @@ public override int GetHashCode ()
{
return !(x == y);
}

/// <inheritdoc />
public override string ToString ()
{
return $"{{ Selector: '{Selector ?? "null"}', ArgumentSemantic: {ArgumentSemantic} }}";
}
}
1 change: 1 addition & 0 deletions src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static class AttributesNames {
public const string DisableDefaultCtorAttribute = "DisableDefaultCtorAttribute";
public const string DisposeAttribute = "DisposeAttribute";
public const string ErrorDomainAttribute = "ErrorDomainAttribute";
public const string ExportAttribute = "Foundation.ExportAttribute";
public const string FieldAttribute = "Foundation.FieldAttribute";
public const string AdviceAttribute = "Foundation.AdviceAttribute";
public const string ModelAttribute = "Foundation.ModelAttribute";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Macios.Transformer.Attributes;
using Xamarin.Tests;
using Xamarin.Utils;
using Microsoft.Macios.Generator.Extensions;
using ObjCRuntime;

namespace Microsoft.Macios.Transformer.Tests.DataModel;

public class ExportDataTests : BaseTransformerTestClass {


class TestDataTryCreate : IEnumerable<object []> {
public IEnumerator<object []> GetEnumerator ()
{
const string selectorOnly = @"
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace Test;
[NoTV]
[MacCatalyst (13, 1)]
[DisableDefaultCtor]
[Abstract]
[BaseType (typeof (NSObject))]
interface UIFeedbackGenerator : UIInteraction {
[Export (""prepare"")]
void Prepare ();
}
";
yield return [(Source: selectorOnly, Path: "/some/random/path.cs"), new ExportData (selector: "prepare")];

const string selectorWithArgumentSemantic = @"
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace Test;
[NoTV]
[MacCatalyst (13, 1)]
[DisableDefaultCtor]
[Abstract]
[BaseType (typeof (NSObject))]
interface UIFeedbackGenerator : UIInteraction {
[Export (""prepare"", ArgumentSemantic.Retain)]
void Prepare ();
}
";
yield return [(Source: selectorWithArgumentSemantic, Path: "/some/random/path.cs"), new ExportData (selector: "prepare", argumentSemantic: ArgumentSemantic.Retain)];

}

IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
}

[Theory]
[AllSupportedPlatformsClassData<TestDataTryCreate>]
public void TryeCreateTests (ApplePlatform platform, (string Source, string Path) source, ExportData expectedData)
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Assert.NotNull (semanticModel);

var declaration = syntaxTree.GetRoot ()
.DescendantNodes ().OfType<MethodDeclarationSyntax> ()
.FirstOrDefault ();
Assert.NotNull (declaration);

var symbol = semanticModel.GetDeclaredSymbol (declaration);
Assert.NotNull (symbol);
var exportData = symbol.GetAttribute<ExportData> (AttributesNames.ExportAttribute, ExportData.TryParse);
Assert.Equal (expectedData, exportData);
}
}

0 comments on commit efd6ff9

Please sign in to comment.