Skip to content

Commit

Permalink
[RGen] Implement the BindFrom attribute. (#22087)
Browse files Browse the repository at this point in the history
The BindFromAttribute is similar to the BindAsAttribute but in the
opossite direction. This allows the bindings to be cleaner since we can
specify in the partial method the return type we really want and the
NSNumber/NSString transformation will be done in the generated code.

---------

Co-authored-by: GitHub Actions Autoformatter <[email protected]>
Co-authored-by: Jonathan Peppers <[email protected]>
  • Loading branch information
3 people authored Feb 1, 2025
1 parent f3c4396 commit 5c5febc
Show file tree
Hide file tree
Showing 21 changed files with 345 additions and 38 deletions.
35 changes: 35 additions & 0 deletions src/ObjCBindings/BindFromAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;
using ObjCRuntime;

#nullable enable

namespace ObjCBindings {

/// <summary>
/// Attribute to bind from a specific type.
/// </summary>
[Experimental ("APL0003")]
[AttributeUsage (AttributeTargets.ReturnValue | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)]
public class BindFromAttribute : Attribute {

/// <summary>
/// Initializes a new instance of the <see cref="BindFromAttribute"/> class.
/// </summary>
public BindFromAttribute (Type type)
{
Type = type;
}

/// <summary>
/// The type to bind from.
/// </summary>
public Type Type { get; set; }

/// <summary>
/// The original type that was bound from.
/// </summary>
public Type? OriginalType { get; set; } = null;
}
}
1 change: 1 addition & 0 deletions src/frameworks.sources
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,7 @@ SHARED_CORE_SOURCES = \
DotNetGlobals.cs \
MinimumVersions.cs \
MonoPInvokeCallbackAttribute.cs \
ObjCBindings/BindFromAttribute.cs \
ObjCBindings/BindingTypeAttribute.cs \
ObjCBindings/BindingTypeTag.cs \
ObjCBindings/ExportAttribute.cs \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
<Compile Include="../Microsoft.Macios.Generator/DictionaryComparer.cs" >
<Link>Generator/DictionaryComparer.cs</Link>
</Compile>
<Compile Include="../Microsoft.Macios.Generator/Attributes/BindFromData.cs" >
<Link>Generator/Attributes/BindFromData.cs</Link>
</Compile>
<Compile Include="../Microsoft.Macios.Generator/Attributes/FieldData.cs" >
<Link>Generator/Attributes/FieldData.cs</Link>
</Compile>
Expand Down
98 changes: 98 additions & 0 deletions src/rgen/Microsoft.Macios.Generator/Attributes/BindFromData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

namespace Microsoft.Macios.Generator.Attributes;

readonly struct BindFromData : IEquatable<BindFromData> {

public string Type { get; }
public string? OriginalType { get; }

public BindFromData (string type)
{
Type = type;
}

public BindFromData (string type, string? originalType)
{
Type = type;
OriginalType = originalType;
}


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

switch (count) {
case 1:
type = ((INamedTypeSymbol) attributeData.ConstructorArguments [0].Value!).ToDisplayString ();
break;
default:
// no other constructors are available
return false;
}

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

foreach (var (name, value) in attributeData.NamedArguments) {
switch (name) {
case "Type":
type = ((INamedTypeSymbol) value.Value!).ToDisplayString ();
break;
case "OriginalType":
originalType = ((INamedTypeSymbol) value.Value!).ToDisplayString ();
break;
default:
data = null;
return false;
}
}
data = new (type, originalType);
return true;
}

/// <inheritdoc />
public bool Equals (BindFromData other)
{
return Type == other.Type && OriginalType == other.OriginalType;
}

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

/// <inheritdoc />
public override int GetHashCode ()
{
return HashCode.Combine (Type, OriginalType);
}

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

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

public override string ToString ()
{
return $"{{ Type: '{Type}', OriginalType: '{OriginalType ?? "null"}' }}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,6 @@ public override int GetHashCode ()
/// <inheritdoc />
public override string ToString ()
{
return $"{{ SymbolName: '{SymbolName}' LibraryName: '{LibraryName ?? "null"}', Flags: '{Flags}' }}";
return $"{{ SymbolName: '{SymbolName}', LibraryName: '{LibraryName ?? "null"}', Flags: '{Flags}' }}";
}
}
1 change: 1 addition & 0 deletions src/rgen/Microsoft.Macios.Generator/AttributesNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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 BindFromAttribute = "ObjCBindings.BindFromAttribute";
public const string BindingProtocolAttribute = "ObjCBindings.BindingTypeAttribute<ObjCBindings.Protocol>";
public const string BindingStrongDictionaryAttribute = "ObjCBindings.BindingTypeAttribute<ObjCBindings.StrongDictionary>";
public const string FieldAttribute = "ObjCBindings.FieldAttribute";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ readonly partial struct Method {
/// </summary>
public ExportData<ObjCBindings.Method> ExportMethodData { get; }

/// <summary>
/// Returns the bind from data if present in the binding.
/// </summary>
public BindFromData? BindAs { get; init; }

/// <summary>
/// Returns if the method was marked as thread safe.
/// </summary>
Expand Down Expand Up @@ -79,7 +84,9 @@ public static bool TryCreate (MethodDeclarationSyntax declaration, RootBindingCo
exportMethodData: exportData,
attributes: attributes,
modifiers: [.. declaration.Modifiers],
parameters: parametersBucket.ToImmutableArray ());
parameters: parametersBucket.ToImmutableArray ()) {
BindAs = method.GetBindFromData (),
};

return true;
}
Expand Down
4 changes: 4 additions & 0 deletions src/rgen/Microsoft.Macios.Generator/DataModel/Method.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public bool Equals (Method other)
return false;
if (ExportMethodData != other.ExportMethodData)
return false;
if (BindAs != other.BindAs)
return false;

var attrsComparer = new AttributesEqualityComparer ();
if (!attrsComparer.Equals (Attributes, other.Attributes))
Expand All @@ -86,6 +88,7 @@ public override int GetHashCode ()
hashCode.Add (Type);
hashCode.Add (Name);
hashCode.Add (ReturnType);
hashCode.Add (BindAs);
foreach (var modifier in Modifiers) {
hashCode.Add (modifier);
}
Expand Down Expand Up @@ -119,6 +122,7 @@ public override string ToString ()
sb.Append ($"ReturnType: {ReturnType}, ");
sb.Append ($"SymbolAvailability: {SymbolAvailability}, ");
sb.Append ($"ExportMethodData: {ExportMethodData}, ");
sb.Append ($"BindAs: {BindAs}, ");
sb.Append ("Attributes: [");
sb.AppendJoin (", ", Attributes);
sb.Append ("], Modifiers: [");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Macios.Generator.Attributes;
using Microsoft.Macios.Generator.Extensions;

namespace Microsoft.Macios.Generator.DataModel;

readonly partial struct Parameter {
Expand All @@ -15,6 +21,33 @@ public enum VariableType {
StringPointer,
}

/// <summary>
/// Returns the bind from data if present in the binding.
/// </summary>
public BindFromData? BindAs { get; init; }

public static bool TryCreate (IParameterSymbol symbol, ParameterSyntax declaration, SemanticModel semanticModel,
[NotNullWhen (true)] out Parameter? parameter)
{
DelegateInfo? delegateInfo = null;
if (symbol.Type is INamedTypeSymbol namedTypeSymbol
&& namedTypeSymbol.DelegateInvokeMethod is not null) {
DelegateInfo.TryCreate (namedTypeSymbol.DelegateInvokeMethod, out delegateInfo);
}

parameter = new (symbol.Ordinal, new (symbol.Type), symbol.Name) {
BindAs = symbol.GetBindFromData (),
IsOptional = symbol.IsOptional,
IsParams = symbol.IsParams,
IsThis = symbol.IsThis,
DefaultValue = (symbol.HasExplicitDefaultValue) ? symbol.ExplicitDefaultValue?.ToString () : null,
ReferenceKind = symbol.RefKind.ToReferenceKind (),
Delegate = delegateInfo,
Attributes = declaration.GetAttributeCodeChanges (semanticModel),
};
return true;
}

/// <summary>
/// Returns the name of the aux variable that would have needed for the given parameter. Use the
/// variable type to name it.
Expand Down
29 changes: 6 additions & 23 deletions src/rgen/Microsoft.Macios.Generator/DataModel/Parameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,6 @@ public Parameter (int position, TypeInfo type, string name)
Name = name;
}

public static bool TryCreate (IParameterSymbol symbol, ParameterSyntax declaration, SemanticModel semanticModel,
[NotNullWhen (true)] out Parameter? parameter)
{
DelegateInfo? delegateInfo = null;
if (symbol.Type is INamedTypeSymbol namedTypeSymbol
&& namedTypeSymbol.DelegateInvokeMethod is not null) {
DelegateInfo.TryCreate (namedTypeSymbol.DelegateInvokeMethod, out delegateInfo);
}

parameter = new (symbol.Ordinal, new (symbol.Type), symbol.Name) {
IsOptional = symbol.IsOptional,
IsParams = symbol.IsParams,
IsThis = symbol.IsThis,
DefaultValue = (symbol.HasExplicitDefaultValue) ? symbol.ExplicitDefaultValue?.ToString () : null,
ReferenceKind = symbol.RefKind.ToReferenceKind (),
Delegate = delegateInfo,
Attributes = declaration.GetAttributeCodeChanges (semanticModel),
};
return true;
}

/// <inheritdoc/>
public bool Equals (Parameter other)
{
Expand All @@ -119,6 +98,8 @@ public bool Equals (Parameter other)
return false;
if (ReferenceKind != other.ReferenceKind)
return false;
if (BindAs != other.BindAs)
return false;
if (Delegate != other.Delegate)
return false;

Expand All @@ -145,6 +126,7 @@ public override int GetHashCode ()
hashCode.Add (DefaultValue);
hashCode.Add ((int) ReferenceKind);
hashCode.Add (Delegate);
hashCode.Add (BindAs);
return hashCode.ToHashCode ();
}

Expand All @@ -165,13 +147,14 @@ public override string ToString ()
sb.Append ($"Position: {Position}, ");
sb.Append ($"Type: {Type}, ");
sb.Append ($"Name: {Name}, ");
sb.Append ("Attributes: ");
sb.Append ("Attributes: [");
sb.AppendJoin (", ", Attributes);
sb.Append ($" IsOptional: {IsOptional}, ");
sb.Append ($"] IsOptional: {IsOptional}, ");
sb.Append ($"IsParams: {IsParams}, ");
sb.Append ($"IsThis: {IsThis}, ");
sb.Append ($"DefaultValue: {DefaultValue}, ");
sb.Append ($"ReferenceKind: {ReferenceKind}, ");
sb.Append ($"BindAs: {BindAs?.ToString () ?? "null"}, ");
sb.Append ($"Delegate: {Delegate?.ToString () ?? "null"} }}");
return sb.ToString ();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public bool IsNotification
public bool MarshalNativeExceptions
=> IsProperty && ExportPropertyData.Value.Flags.HasFlag (ObjCBindings.Property.MarshalNativeExceptions);

/// <summary>
/// Returns the bind from data if present in the binding.
/// </summary>
public BindFromData? BindAs { get; init; }

/// <summary>
/// True if the property should be generated without a backing field.
/// </summary>
Expand Down Expand Up @@ -164,6 +169,7 @@ public static bool TryCreate (PropertyDeclarationSyntax declaration, RootBinding
attributes: attributes,
modifiers: [.. declaration.Modifiers],
accessors: accessorCodeChanges) {
BindAs = propertySymbol.GetBindFromData (),
ExportFieldData = GetFieldInfo (context, propertySymbol),
ExportPropertyData = propertySymbol.GetExportData<ObjCBindings.Property> (),
};
Expand All @@ -190,6 +196,7 @@ public override string ToString ()
sb.Append ($"IsTransient: '{IsTransient}', ");
sb.Append ($"NeedsBackingField: '{NeedsBackingField}', ");
sb.Append ($"RequiresDirtyCheck: '{RequiresDirtyCheck}', ");
sb.Append ($"BindAs: '{BindAs}', ");
sb.Append ("Attributes: [");
sb.AppendJoin (",", Attributes);
sb.Append ("], Modifiers: [");
Expand Down
2 changes: 2 additions & 0 deletions src/rgen/Microsoft.Macios.Generator/DataModel/Property.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ bool CoreEquals (Property other)
return false;
if (ExportPropertyData != other.ExportPropertyData)
return false;
if (BindAs != other.BindAs)
return false;

var attrsComparer = new AttributesEqualityComparer ();
if (!attrsComparer.Equals (Attributes, other.Attributes))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ static partial class TypeSymbolExtensions {
public static Dictionary<string, List<AttributeData>> GetAttributeData (this ISymbol symbol)
{
var boundAttributes = symbol.GetAttributes ();
if (boundAttributes.Length == 0) {
// return an empty dictionary if there are no attributes
return new ();
}

var attributes = new Dictionary<string, List<AttributeData>> ();
foreach (var attributeData in boundAttributes) {
var attrName = attributeData.AttributeClass?.ToDisplayString ();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.Macios.Generator.Attributes;
using Microsoft.Macios.Generator.Availability;
Expand Down Expand Up @@ -134,6 +133,9 @@ public static BindingTypeData<T> GetBindingData<T> (this ISymbol symbol) where T
public static FieldData<T>? GetFieldData<T> (this ISymbol symbol) where T : Enum
=> GetAttribute<FieldData<T>> (symbol, AttributesNames.GetFieldAttributeName<T>, FieldData<T>.TryParse);

public static BindFromData? GetBindFromData (this ISymbol symbol)
=> GetAttribute<BindFromData> (symbol, AttributesNames.BindFromAttribute, BindFromData.TryParse);

public static bool X86NeedStret (ITypeSymbol returnType)
{
if (!returnType.IsValueType || returnType.SpecialType == SpecialType.System_Enum ||
Expand Down
Loading

0 comments on commit 5c5febc

Please sign in to comment.