Skip to content

Commit

Permalink
[Rgen] Add support to parse the strong dict attr in the transformer.
Browse files Browse the repository at this point in the history
  • Loading branch information
mandel-macaque committed Jan 27, 2025
1 parent 71ed12c commit d79592d
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

namespace Microsoft.Macios.Transformer.Attributes;

readonly struct StrongDictionaryData : IEquatable<StrongDictionaryData> {

public string TypeWithKeys { get; }
public string? Suffix { get; }

public StrongDictionaryData (string typeWithKeys)
{
TypeWithKeys = typeWithKeys;
}

public StrongDictionaryData (string typeWithKeys, string? suffix)
{
TypeWithKeys = typeWithKeys;
Suffix = suffix;
}

public static bool TryParse (AttributeData attributeData,
[NotNullWhen (true)] out StrongDictionaryData? data)
{
data = null;
var count = attributeData.ConstructorArguments.Length;
string typeWithKeys;
string? suffix = null;

// custom marshal directive values

switch (count) {
case 1:
typeWithKeys = (string) attributeData.ConstructorArguments [0].Value!;
break;
default:
// 0 should not be an option..
return false;
}

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

foreach (var (argumentName, value) in attributeData.NamedArguments) {
switch (argumentName) {
case "TypeWithKeys":
typeWithKeys = (string?) value.Value!;
break;
case "Suffix":
suffix = (string?) value.Value!;
break;
}
}

data = new (typeWithKeys, suffix);
return true;
}

public bool Equals (StrongDictionaryData other)
{
if (TypeWithKeys != other.TypeWithKeys)
return false;
return Suffix == other.Suffix;
}

/// <inheritdoc />
public override bool Equals (object? obj)
{
return obj is StrongDictionaryData other && Equals (other);
}

/// <inheritdoc />
public override int GetHashCode ()
=> HashCode.Combine (TypeWithKeys, Suffix);

public static bool operator == (StrongDictionaryData x, StrongDictionaryData y)
{
return x.Equals (y);
}

public static bool operator != (StrongDictionaryData x, StrongDictionaryData y)
{
return !(x == y);
}

public override string ToString ()
=> $"{{ TypeWithKeys: '{TypeWithKeys}' Suffix: '{Suffix}' }}";
}
6 changes: 6 additions & 0 deletions src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,12 @@ static class AttributesNames {
/// </summary>
[BindingFlag (AttributeTargets.Class)]
public const string StaticAttribute = "StaticAttribute";

/// <summary>
/// When this attribute is applied to an interface, it directs the generator to
/// create a strongly typed DictionaryContainer for the specified fields.
/// </summary>
[BindingAttribute(typeof(StrongDictionaryData), AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Property)]
public const string StrongDictionaryAttribute = "StrongDictionaryAttribute";

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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 StrongDictionaryDataTests : BaseTransformerTestClass {

class TestDataTryCreate : IEnumerable<object []> {
public IEnumerator<object []> GetEnumerator ()
{
const string path = "/some/random/path.cs";

const string strongDictionary = @"
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace Test;
[StrongDictionary (""AVCapturePhotoSettingsThumbnailFormatKeys"")]
interface AVCapturePhotoSettingsThumbnailFormat {
NSString Codec { get; set; }
NSNumber Width { get; set; }
NSNumber Height { get; set; }
}
";

yield return [(Source: strongDictionary, Path: "/some/random/path.cs"), new StrongDictionaryData("AVCapturePhotoSettingsThumbnailFormatKeys")];
}

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

[Theory]
[AllSupportedPlatformsClassData<TestDataTryCreate>]
void TryCreateTests (ApplePlatform platform, (string Source, string Path) source, StrongDictionaryData 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<BaseTypeDeclarationSyntax> ()
.FirstOrDefault ();
Assert.NotNull (declaration);

var symbol = semanticModel.GetDeclaredSymbol (declaration);
Assert.NotNull (symbol);
var attribute = symbol.GetAttribute<StrongDictionaryData> (AttributesNames.StrongDictionaryAttribute, StrongDictionaryData.TryParse);
Assert.Equal (expectedData, attribute);
}
}

0 comments on commit d79592d

Please sign in to comment.