Skip to content

Commit

Permalink
[Rgen] Add support to the export method to provide custom marshalling.
Browse files Browse the repository at this point in the history
Add a new flag that can be used to let the generator know that a exposed
method or property can have a custom marshalling call.

Later the analyzer will make sure that only when the appropiate flag is
set, that the extra name parameters are allowed.
  • Loading branch information
mandel-macaque committed Jan 18, 2025
1 parent 9a50089 commit f0e0e27
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 11 deletions.
24 changes: 24 additions & 0 deletions src/ObjCBindings/ExportAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,30 @@ public class ExportAttribute<T> : Attribute where T : Enum {
/// </summary >
public ArgumentSemantic ArgumentSemantic { get; set; } = ArgumentSemantic.None;

/// <summary>
/// Get/Set the native prefix to be used in the custom marshal directive.
///
/// The generator will only respect this value if the CustomMarshalDirective flag is set.
/// If the flag is not set the analyzer will raise an compiling error.
/// </summary >
public string? NativePrefix { get; set; } = null;

/// <summary>
/// Get/Set the native suffix to be used in the custom marshal directive.
///
/// The generator will only respect this value if the CustomMarshalDirective flag is set.
/// If the flag is not set the analyzer will raise an compiling error.
/// </summary >
public string? NativeSuffix { get; set; } = null;

/// <summary>
/// Get/Set the library to be used in the custom marshal directive.
///
/// The generator will only respect this value if the CustomMarshalDirective flag is set.
/// If the flag is not set the analyzer will raise an compiling error.
/// </summary >
public string? Library { get; set; } = null;

protected ExportAttribute () { }

/// <summary>
Expand Down
19 changes: 19 additions & 0 deletions src/ObjCBindings/ExportTag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ public enum Method : Int64 {
/// </summary>
MarshalNativeExceptions = 1 << 4,


/// <summary>
/// Instruct the generator to use a custom marshal directive for the method. When this flag is applied the
/// following name parameters must be provided:
/// - NativePrefix: The prefix to be used in the native method name.
/// - NativeSuffix: The suffix to be used in the native method name.
/// - Library: The library to be used in the custom marshal directive.
/// </summary>
CustomMarshalDirective = 1 << 5,

}

/// <summary>
Expand Down Expand Up @@ -78,6 +88,15 @@ public enum Property : Int64 {
/// </summary>
MarshalNativeExceptions = 1 << 4,

/// <summary>
/// Instruct the generator to use a custom marshal directive for the method. When this flag is applied the
/// following name parameters must be provided:
/// - NativePrefix: The prefix to be used in the native method name.
/// - NativeSuffix: The suffix to be used in the native method name.
/// - Library: The library to be used in the custom marshal directive.
/// </summary>
CustomMarshalDirective = 1 << 5,

}
}

86 changes: 79 additions & 7 deletions src/rgen/Microsoft.Macios.Generator/Attributes/ExportData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.CodeAnalysis;
using ObjCRuntime;

Expand All @@ -27,6 +28,27 @@ namespace Microsoft.Macios.Generator.Attributes;
/// Argument semantics to use with the selector.
/// </summary>
public ArgumentSemantic ArgumentSemantic { get; } = ArgumentSemantic.None;

/// <summary>
/// Get the native prefix to be used in the custom marshal directive.
///
/// Should only be present with the CustomeMarshalDirective flag.
/// </summary >
public string? NativePrefix { get; init; }

/// <summary>
/// Get the native sufix to be used in the custom marshal directive.
///
/// Should only be present with the CustomeMarshalDirective flag.
/// </summary >
public string? NativeSuffix { get; init; }

/// <summary>
/// Get the library to be used in the custom marshal directive.
///
/// Should only be present with the CustomeMarshalDirective flag.
/// </summary >
public string? Library { get; init; }

public ExportData () { }

Expand Down Expand Up @@ -62,6 +84,12 @@ public static bool TryParse (AttributeData attributeData,
string? selector = null;
ArgumentSemantic argumentSemantic = ArgumentSemantic.None;
T? flags = default;

// custom marshal directive values
string? nativePrefix = null;
string? nativeSuffix = null;
string? library = null;

switch (count) {
case 1:
selector = (string?) attributeData.ConstructorArguments [0].Value!;
Expand Down Expand Up @@ -106,14 +134,35 @@ public static bool TryParse (AttributeData attributeData,
case "Flags":
flags = (T) value.Value!;
break;
case "NativePrefix":
nativePrefix = (string?) value.Value!;
break;
case "NativeSuffix":
nativeSuffix = (string?) value.Value!;
break;
case "Library":
library = (string?) value.Value!;
break;
default:
data = null;
return false;
}
}

data = flags is not null ?
new (selector, argumentSemantic, flags) : new (selector, argumentSemantic);
if (flags is not null) {
data = new (selector, argumentSemantic, flags) {
NativePrefix = nativePrefix,
NativeSuffix = nativeSuffix,
Library = library
};
return true;
}

data = new(selector, argumentSemantic) {
NativePrefix = nativePrefix,
NativeSuffix = nativeSuffix,
Library = library
};
return true;
}

Expand All @@ -124,10 +173,18 @@ public bool Equals (ExportData<T> other)
return false;
if (ArgumentSemantic != other.ArgumentSemantic)
return false;
if (Flags is not null && other.Flags is not null) {
return Flags.Equals (other.Flags);
}
return false;
if (NativePrefix != other.NativePrefix)
return false;
if (NativeSuffix != other.NativeSuffix)
return false;
if (Library != other.Library)
return false;
return (Flags, other.Flags) switch {
(null, null) => true,
(null, _) => false,
(_, null) => false,
(_, _) => Flags!.Equals (other.Flags)
};
}

/// <inheritdoc />
Expand Down Expand Up @@ -155,6 +212,21 @@ public override int GetHashCode ()
/// <inheritdoc />
public override string ToString ()
{
return $"{{ Type: '{typeof (T).FullName}', Selector: '{Selector ?? "null"}', ArgumentSemantic: '{ArgumentSemantic}', Flags: '{Flags}' }}";
var sb = new StringBuilder ("{ Type: '");
sb.Append (typeof (T).FullName);
sb.Append ("', Selector: '");
sb.Append (Selector ?? "null");
sb.Append ("', ArgumentSemantic: '");
sb.Append (ArgumentSemantic);
sb.Append ("', Flags: '");
sb.Append (Flags);
sb.Append ("', NativePrefix: '");
sb.Append (NativePrefix ?? "null");
sb.Append ("', NativeSuffix: '");
sb.Append (NativeSuffix ?? "null");
sb.Append ("', Library: '");
sb.Append (Library ?? "null");
sb.Append ("' }");
return sb.ToString ();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,31 @@ public IEnumerator<object []> GetEnumerator ()
yield return [
Method.Default,
new ExportData<Method> ("symbol", ArgumentSemantic.None, Method.Default),
"{ Type: 'ObjCBindings.Method', Selector: 'symbol', ArgumentSemantic: 'None', Flags: 'Default' }"
"{ Type: 'ObjCBindings.Method', Selector: 'symbol', ArgumentSemantic: 'None', Flags: 'Default', NativePrefix: 'null', NativeSuffix: 'null', Library: 'null' }"
];
yield return [
Method.Default,
new ExportData<Method> ("symbol"),
"{ Type: 'ObjCBindings.Method', Selector: 'symbol', ArgumentSemantic: 'None', Flags: 'Default' }"
"{ Type: 'ObjCBindings.Method', Selector: 'symbol', ArgumentSemantic: 'None', Flags: 'Default', NativePrefix: 'null', NativeSuffix: 'null', Library: 'null' }"
];
yield return [
Property.Default,
new ExportData<Property> ("symbol", ArgumentSemantic.Retain, Property.Default),
"{ Type: 'ObjCBindings.Property', Selector: 'symbol', ArgumentSemantic: 'Retain', Flags: 'Default' }"
"{ Type: 'ObjCBindings.Property', Selector: 'symbol', ArgumentSemantic: 'Retain', Flags: 'Default', NativePrefix: 'null', NativeSuffix: 'null', Library: 'null' }"
];
yield return [
Property.Default,
new ExportData<Property> ("symbol"),
"{ Type: 'ObjCBindings.Property', Selector: 'symbol', ArgumentSemantic: 'None', Flags: 'Default' }"
"{ Type: 'ObjCBindings.Property', Selector: 'symbol', ArgumentSemantic: 'None', Flags: 'Default', NativePrefix: 'null', NativeSuffix: 'null', Library: 'null' }"
];
yield return [
Method.Default,
new ExportData<Method> ("symbol", ArgumentSemantic.None,
Method.Default | Method.CustomMarshalDirective) {
NativePrefix = "xamarin_",
Library = "__Internal"
},
"{ Type: 'ObjCBindings.Method', Selector: 'symbol', ArgumentSemantic: 'None', Flags: 'CustomMarshalDirective', NativePrefix: 'xamarin_', NativeSuffix: 'null', Library: '__Internal' }"
];
}

Expand All @@ -81,6 +90,7 @@ IEnumerator IEnumerable.GetEnumerator ()
[ClassData (typeof (TestDataToString))]
void TestFieldDataToString<T> (T @enum, ExportData<T> x, string expected) where T : Enum
{
var str = x.ToString ();
Assert.NotNull (@enum);
Assert.Equal (expected, x.ToString ());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,78 @@ public partial class MyClass {
]
}
];

const string customMarshallingProperty = @"
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
[Export<Property> (""name"", Flags = Property.CustomMarshalDirective, NativePrefix = ""xamarin_"", Library = ""__Internal"")]
public partial string Name { get; set; } = string.Empty;
}
";

yield return [
customMarshallingProperty,
new CodeChanges (
bindingInfo: new (new BindingTypeData<Class> ()),
name: "MyClass",
@namespace: ["NS"],
fullyQualifiedSymbol: "NS.MyClass",
symbolAvailability: new ()
) {
Base = "object",
Interfaces = ImmutableArray<string>.Empty,
Attributes = [
new ("ObjCBindings.BindingTypeAttribute<ObjCBindings.Class>")
],
UsingDirectives = new HashSet<string> { "ObjCBindings" },
Modifiers = [
SyntaxFactory.Token (SyntaxKind.PublicKeyword),
SyntaxFactory.Token (SyntaxKind.PartialKeyword)
],
Properties = [
new (
name: "Name",
returnType: ReturnTypeForString (),
symbolAvailability: new (),
attributes: [
new ("ObjCBindings.ExportAttribute<ObjCBindings.Property>", ["name", "ObjCBindings.Property.CustomMarshalDirective", "xamarin_", "__Internal"])
],
modifiers: [
SyntaxFactory.Token (SyntaxKind.PublicKeyword),
SyntaxFactory.Token (SyntaxKind.PartialKeyword),
],
accessors: [
new (
accessorKind: AccessorKind.Getter,
symbolAvailability: new (),
exportPropertyData: null,
attributes: [],
modifiers: []
),
new (
accessorKind: AccessorKind.Setter,
symbolAvailability: new (),
exportPropertyData: null,
attributes: [],
modifiers: []
),
]
) {
ExportPropertyData = new (
selector: "name",
argumentSemantic: ArgumentSemantic.None,
flags: Property.Default | Property.CustomMarshalDirective) {
NativePrefix = "xamarin_",
Library = "__Internal"
}
}
]
}
];

const string multiPropertyClass = @"
using ObjCBindings;
Expand Down
Loading

0 comments on commit f0e0e27

Please sign in to comment.