Skip to content

Commit

Permalink
[Rgen] Add the BindingData struct as part as the code struct. (#21874)
Browse files Browse the repository at this point in the history
This addition allows the code emitters to access the binding type
configuration.

---------

Co-authored-by: GitHub Actions Autoformatter <github-actions-autoformatter@xamarin.com>
  • Loading branch information
mandel-macaque and GitHub Actions Autoformatter authored Dec 31, 2024
1 parent 859dd99 commit 570dd82
Show file tree
Hide file tree
Showing 12 changed files with 843 additions and 236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
<Compile Include="../Microsoft.Macios.Generator/Attributes/AttributeParsingError.cs" >
<Link>Generator/Attributes/AttributeParsingError.cs</Link>
</Compile>
<Compile Include="../Microsoft.Macios.Generator/Attributes/BindingTypeData.cs" >
<Link>Generator/Attributes/BindingTypeData.cs</Link>
</Compile>
<Compile Include="../Microsoft.Macios.Generator/Attributes/ObsoletedOSPlatformData.cs" >
<Link>Generator/Attributes/ObsoletedOSPlatformData.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public static bool TryParse (AttributeData attributeData,
var count = attributeData.ConstructorArguments.Length;
string? name;
switch (count) {
case 0:
name = null;
break;
case 1:
name = (string?) attributeData.ConstructorArguments [0].Value!;
break;
Expand Down Expand Up @@ -106,6 +109,12 @@ public BindingTypeData (string? name)
Name = name;
}

public BindingTypeData (T? flags)
{
Name = null;
Flags = flags;
}

public BindingTypeData (string? name, T? flags)
{
Name = name;
Expand Down
22 changes: 21 additions & 1 deletion src/rgen/Microsoft.Macios.Generator/AttributesNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ namespace Microsoft.Macios.Generator;
/// <summary>
/// Contains all the names of the attributes that are used by the binding generator.
/// </summary>
public static class AttributesNames {
static class AttributesNames {

public const string BindingAttribute = "ObjCBindings.BindingTypeAttribute";
public const string BindingCategoryAttribute = "ObjCBindings.BindingTypeAttribute<ObjCBindings.Category>";
public const string BindingClassAttribute = "ObjCBindings.BindingTypeAttribute<ObjCBindings.Class>";
public const string BindingProtocolAttribute = "ObjCBindings.BindingTypeAttribute<ObjCBindings.Protocol>";
public const string FieldAttribute = "ObjCBindings.FieldAttribute";
public const string EnumFieldAttribute = "ObjCBindings.FieldAttribute<ObjCBindings.EnumValue>";
public const string ExportFieldAttribute = "ObjCBindings.ExportAttribute<ObjCBindings.Field>";
Expand All @@ -17,6 +20,23 @@ public static class AttributesNames {
public const string UnsupportedOSPlatformAttribute = "System.Runtime.Versioning.UnsupportedOSPlatformAttribute";
public const string ObsoletedOSPlatformAttribute = "System.Runtime.Versioning.ObsoletedOSPlatformAttribute";


public static string? GetBindingTypeAttributeName<T> () where T : Enum
{
var type = typeof (T);
if (type == typeof (ObjCBindings.Category)) {
return BindingCategoryAttribute;
}
if (type == typeof (ObjCBindings.Class)) {
return BindingClassAttribute;
}
if (type == typeof (ObjCBindings.Protocol)) {
return BindingProtocolAttribute;
}

return null;
}

public static string? GetFieldAttributeName<T> () where T : Enum
{
// we cannot use a switch statement because typeof is not a constant value
Expand Down
43 changes: 26 additions & 17 deletions src/rgen/Microsoft.Macios.Generator/DataModel/CodeChanges.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,36 @@ readonly struct CodeChanges {
/// <summary>
/// Represents the type of binding that the code changes are for.
/// </summary>
public BindingType BindingType { get; } = BindingType.Unknown;
public BindingType BindingType => BindingData.BindingType;

readonly BindingData bindingData = default;
/// <summary>
/// Represents the binding data that will be used to generate the code.
/// </summary>
public BindingData BindingData => bindingData;

readonly string name = string.Empty;
/// <summary>
/// The name of the named type that generated the code change.
/// </summary>
public string Name { get; }
public string Name => name;

readonly ImmutableArray<string> namespaces = ImmutableArray<string>.Empty;
/// <summary>
/// The namespace that contains the named type that generated the code change.
/// </summary>
public ImmutableArray<string> Namespace { get; }
public ImmutableArray<string> Namespace => namespaces;

/// <summary>
/// Fully qualified name of the symbol that the code changes are for.
/// </summary>
public string FullyQualifiedSymbol { get; }

readonly SymbolAvailability availability = new ();
/// <summary>
/// The platform availability of the named type.
/// </summary>
public SymbolAvailability SymbolAvailability { get; }
public SymbolAvailability SymbolAvailability => availability;

/// <summary>
/// Changes to the attributes of the symbol.
Expand Down Expand Up @@ -208,19 +217,19 @@ static void GetMembers<T, TR> (TypeDeclarationSyntax baseDeclarationSyntax, Sema
/// <summary>
/// Internal constructor added for testing purposes.
/// </summary>
/// <param name="bindingType">The type of binding for the given code changes.</param>
/// <param name="bindingData">The binding data of binding for the given code changes.</param>
/// <param name="name">The name of the named type that created the code change.</param>
/// <param name="namespace">The namespace that contains the named type.</param>
/// <param name="fullyQualifiedSymbol">The fully qualified name of the symbol.</param>
/// <param name="symbolAvailability">The platform availability of the named symbol.</param>
internal CodeChanges (BindingType bindingType, string name, ImmutableArray<string> @namespace,
internal CodeChanges (BindingData bindingData, string name, ImmutableArray<string> @namespace,
string fullyQualifiedSymbol, SymbolAvailability symbolAvailability)
{
BindingType = bindingType;
Name = name;
Namespace = @namespace;
this.bindingData = bindingData;
this.name = name;
this.namespaces = @namespace;
FullyQualifiedSymbol = fullyQualifiedSymbol;
SymbolAvailability = symbolAvailability;
this.availability = symbolAvailability;
}

/// <summary>
Expand All @@ -230,8 +239,8 @@ internal CodeChanges (BindingType bindingType, string name, ImmutableArray<strin
/// <param name="semanticModel">The semantic model of the compilation.</param>
CodeChanges (EnumDeclarationSyntax enumDeclaration, SemanticModel semanticModel)
{
(Name, Namespace, SymbolAvailability) = semanticModel.GetSymbolData (enumDeclaration);
BindingType = BindingType.SmartEnum;
semanticModel.GetSymbolData (
enumDeclaration, BindingType.SmartEnum, out name, out namespaces, out availability, out bindingData);
FullyQualifiedSymbol = enumDeclaration.GetFullyQualifiedIdentifier ();
Attributes = enumDeclaration.GetAttributeCodeChanges (semanticModel);
Modifiers = [.. enumDeclaration.Modifiers];
Expand Down Expand Up @@ -263,8 +272,8 @@ internal CodeChanges (BindingType bindingType, string name, ImmutableArray<strin
/// <param name="semanticModel">The semantic model of the compilation.</param>
CodeChanges (ClassDeclarationSyntax classDeclaration, SemanticModel semanticModel)
{
(Name, Namespace, SymbolAvailability) = semanticModel.GetSymbolData (classDeclaration);
BindingType = BindingType.Class;
semanticModel.GetSymbolData (
classDeclaration, BindingType.Class, out name, out namespaces, out availability, out bindingData);
FullyQualifiedSymbol = classDeclaration.GetFullyQualifiedIdentifier ();
Attributes = classDeclaration.GetAttributeCodeChanges (semanticModel);
Modifiers = [.. classDeclaration.Modifiers];
Expand All @@ -287,8 +296,8 @@ internal CodeChanges (BindingType bindingType, string name, ImmutableArray<strin
/// <param name="semanticModel">The semantic model of the compilation.</param>
CodeChanges (InterfaceDeclarationSyntax interfaceDeclaration, SemanticModel semanticModel)
{
(Name, Namespace, SymbolAvailability) = semanticModel.GetSymbolData (interfaceDeclaration);
BindingType = BindingType.Protocol;
semanticModel.GetSymbolData (
interfaceDeclaration, BindingType.Protocol, out name, out namespaces, out availability, out bindingData);
FullyQualifiedSymbol = interfaceDeclaration.GetFullyQualifiedIdentifier ();
Attributes = interfaceDeclaration.GetAttributeCodeChanges (semanticModel);
Modifiers = [.. interfaceDeclaration.Modifiers];
Expand Down Expand Up @@ -323,7 +332,7 @@ internal CodeChanges (BindingType bindingType, string name, ImmutableArray<strin
public override string ToString ()
{
var sb = new StringBuilder ("Changes: {");
sb.Append ($"BindingType: '{BindingType}', Name: '{Name}', Namespace: [");
sb.Append ($"BindingData: '{BindingData}', Name: '{Name}', Namespace: [");
sb.AppendJoin (", ", Namespace);
sb.Append ($"], FullyQualifiedSymbol: '{FullyQualifiedSymbol}', SymbolAvailability: {SymbolAvailability}, ");
sb.Append ("Attributes: [");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,18 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Macios.Generator.Availability;
using Microsoft.Macios.Generator.DataModel;

namespace Microsoft.Macios.Generator.Extensions;

static class SemanticModelExtensions {

/// <summary>
/// Returns the name and namespace of the symbol that has been declared in the passed base type declaration
/// syntax node.
/// </summary>
/// <param name="self">The current semantic model.</param>
/// <param name="declaration">The named type declaration syntaxt.</param>
/// <returns>A tuple containing the name and namespace of the type. If they could not be calculated, they will
/// be set to be string.Empty.</returns>
public static (string Name, ImmutableArray<string> Namespace, SymbolAvailability SymbolAvailability) GetSymbolData (this SemanticModel self,
BaseTypeDeclarationSyntax declaration)
public static void GetSymbolData (ISymbol? symbol,
out string name,
out ImmutableArray<string> namespaces,
out SymbolAvailability symbolAvailability)
{
var symbol = self.GetDeclaredSymbol (declaration);
var name = symbol?.Name ?? string.Empty;
name = symbol?.Name ?? string.Empty;
var bucket = ImmutableArray.CreateBuilder<string> ();
var ns = symbol?.ContainingNamespace;
while (ns is not null) {
Expand All @@ -28,7 +22,31 @@ public static (string Name, ImmutableArray<string> Namespace, SymbolAvailability
bucket.Insert (0, ns.Name);
ns = ns.ContainingNamespace;
}
var availability = symbol?.GetSupportedPlatforms () ?? new SymbolAvailability ();
return (name, bucket.ToImmutableArray (), availability);

symbolAvailability = symbol?.GetSupportedPlatforms () ?? new SymbolAvailability ();
namespaces = bucket.ToImmutableArray ();
}

public static void GetSymbolData (this SemanticModel self, BaseTypeDeclarationSyntax declaration,
BindingType bindingType,
out string name,
out ImmutableArray<string> namespaces,
out SymbolAvailability symbolAvailability,
out BindingData bindingData)
{
var symbol = self.GetDeclaredSymbol (declaration);
GetSymbolData (symbol, out name, out namespaces, out symbolAvailability);
if (symbol is null)
bindingData = default;
else {
bindingData = bindingType switch {
BindingType.Category => new BindingData (symbol.GetBindingData<ObjCBindings.Category> ()),
BindingType.Class => new BindingData (symbol.GetBindingData<ObjCBindings.Class> ()),
BindingType.Protocol => new BindingData (symbol.GetBindingData<ObjCBindings.Protocol> ()),
BindingType.SmartEnum => new BindingData (BindingType.SmartEnum, symbol.GetBindingData ()),
_ => default,
};
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,47 @@ public static SymbolAvailability GetSupportedPlatforms (this ISymbol symbol)
return availability;
}

public static BindingTypeData GetBindingData (this ISymbol symbol)
{
var boundAttributes = symbol.GetAttributes ();
if (boundAttributes.Length == 0) {
// no attrs in the symbol, therefore the symbol is supported in all platforms
return default;
}
// we are looking for the basic BindingAttribute attr
foreach (var attributeData in boundAttributes) {
var attrName = attributeData.AttributeClass?.ToDisplayString ();
if (string.IsNullOrEmpty (attrName) || attrName != AttributesNames.BindingAttribute)
continue;
if (BindingTypeData.TryParse (attributeData, out var bindingData)) {
return bindingData.Value;
}
}

return default;
}

public static BindingTypeData<T> GetBindingData<T> (this ISymbol symbol) where T : Enum
{
var boundAttributes = symbol.GetAttributes ();
if (boundAttributes.Length == 0) {
// no attrs in the symbol, therefore the symbol is supported in all platforms
return default;
}

var targetAttrName = AttributesNames.GetBindingTypeAttributeName<T> ();
foreach (var attributeData in boundAttributes) {
var attrName = attributeData.AttributeClass?.ToDisplayString ();
if (string.IsNullOrEmpty (attrName) || attrName != targetAttrName)
continue;
if (BindingTypeData<T>.TryParse (attributeData, out var bindingData)) {
return bindingData.Value;
}
}

return default;
}

/// <summary>
/// Retrieve the data of an export attribute on a symbol.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,16 @@ public void GetFieldAttributeName<T> (T @enum, string? expectedName) where T : E
Assert.NotNull (@enum);
Assert.Equal (expectedName, AttributesNames.GetFieldAttributeName<T> ());
}

[Theory]
[InlineData (StringComparison.Ordinal, null)]
[InlineData (EnumValue.Default, null)]
[InlineData (Category.Default, AttributesNames.BindingCategoryAttribute)]
[InlineData (Class.Default, AttributesNames.BindingClassAttribute)]
[InlineData (Protocol.Default, AttributesNames.BindingProtocolAttribute)]
public void GetBindingTypeAttributeName<T> (T @enum, string? expectedName) where T : Enum
{
Assert.NotNull (@enum);
Assert.Equal (expectedName, AttributesNames.GetBindingTypeAttributeName<T> ());
}
}
Loading

10 comments on commit 570dd82

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ [CI Build] Build passed (Detect API changes) ✅

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ [CI Build] Build passed (Build packages) ✅

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ [CI Build] Build passed (Build macOS tests) ✅

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ API diff for current PR / commit

.NET (No breaking changes)

❗ API diff vs stable (Breaking changes)

.NET (:heavy_exclamation_mark: Breaking changes :heavy_exclamation_mark:)
  • iOS: vsdrops gist (:heavy_exclamation_mark: Breaking changes :heavy_exclamation_mark:)
  • tvOS: vsdrops gist (:heavy_exclamation_mark: Breaking changes :heavy_exclamation_mark:)
  • MacCatalyst: vsdrops gist (:heavy_exclamation_mark: Breaking changes :heavy_exclamation_mark:)
  • macOS: vsdrops gist (:heavy_exclamation_mark: Breaking changes :heavy_exclamation_mark:)

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💻 [CI Build] Tests on macOS X64 - Mac Sonoma (14) passed 💻

All tests on macOS X64 - Mac Sonoma (14) passed.

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💻 [CI Build] Tests on macOS M1 - Mac Monterey (12) passed 💻

All tests on macOS M1 - Mac Monterey (12) passed.

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💻 [CI Build] Tests on macOS M1 - Mac Ventura (13) passed 💻

All tests on macOS M1 - Mac Ventura (13) passed.

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💻 [CI Build] Tests on macOS arm64 - Mac Sequoia (15) passed 💻

All tests on macOS arm64 - Mac Sequoia (15) passed.

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💻 [CI Build] Windows Integration Tests passed 💻

All Windows Integration Tests passed.

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 [CI Build] Test results 🚀

Test results

✅ All tests passed on VSTS: test results.

🎉 All 107 tests passed 🎉

Tests counts

✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (iOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (MacCatalyst): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (macOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (Multiple platforms): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (tvOS): All 1 tests passed. Html Report (VSDrops) Download
✅ framework: All 2 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 4 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. Html Report (VSDrops) Download
✅ interdependent-binding-projects: All 4 tests passed. Html Report (VSDrops) Download
✅ introspection: All 4 tests passed. Html Report (VSDrops) Download
✅ linker: All 40 tests passed. Html Report (VSDrops) Download
✅ monotouch (iOS): All 8 tests passed. Html Report (VSDrops) Download
✅ monotouch (MacCatalyst): All 10 tests passed. Html Report (VSDrops) Download
✅ monotouch (macOS): All 9 tests passed. Html Report (VSDrops) Download
✅ monotouch (tvOS): All 8 tests passed. Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ xcframework: All 4 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

Pipeline on Agent
Hash: 570dd825be72ccbd0356ab1a98834e487ac18abd [CI build]

Please sign in to comment.