Skip to content

Commit 252e0f8

Browse files
[RGen] Remove all the Nullable<Method> boxing that happens in the generator (#23431)
1 parent 074c924 commit 252e0f8

File tree

11 files changed

+461
-35
lines changed

11 files changed

+461
-35
lines changed

src/rgen/Microsoft.Macios.Generator/DataModel/Method.Generator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public Method (string type, string name, TypeInfo returnType,
100100
ExportData<ObjCBindings.Method> exportMethodData,
101101
ImmutableArray<AttributeCodeChange> attributes,
102102
ImmutableArray<SyntaxToken> modifiers,
103-
ImmutableArray<Parameter> parameters)
103+
ImmutableArray<Parameter> parameters) : this (StructState.Initialized)
104104
{
105105
Type = type;
106106
Name = name;

src/rgen/Microsoft.Macios.Generator/DataModel/Method.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,30 @@ namespace Microsoft.Macios.Generator.DataModel;
1515
[StructLayout (LayoutKind.Auto)]
1616
readonly partial struct Method : IEquatable<Method> {
1717

18+
/// <summary>
19+
/// The initialization state of the struct.
20+
/// </summary>
21+
StructState State { get; init; } = StructState.Default;
22+
23+
/// <summary>
24+
/// Gets the default, uninitialized instance of <see cref="Method"/>.
25+
/// </summary>
26+
public static Method Default { get; } = new (StructState.Default);
27+
28+
/// <summary>
29+
/// Gets a value indicating whether the instance is the default, uninitialized instance.
30+
/// </summary>
31+
public bool IsNullOrDefault => State == StructState.Default;
32+
1833
/// <summary>
1934
/// Type name that owns the method.
2035
/// </summary>
21-
public string Type { get; }
36+
public string Type { get; } = string.Empty;
2237

2338
/// <summary>
2439
/// Method name.
2540
/// </summary>
26-
public string Name { get; init; }
41+
public string Name { get; init; } = string.Empty;
2742

2843
/// <summary>
2944
/// True if the method is an extension method.
@@ -74,9 +89,16 @@ public ImmutableArray<SyntaxToken> Modifiers {
7489
/// </summary>
7590
public ImmutableArray<Parameter> Parameters { get; init; } = [];
7691

92+
internal Method (StructState state)
93+
{
94+
State = state;
95+
}
96+
7797
/// <inheritdoc/>
7898
public bool Equals (Method other)
7999
{
100+
if (State == StructState.Default && other.State == StructState.Default)
101+
return true;
80102
if (Type != other.Type)
81103
return false;
82104
if (Name != other.Name)

src/rgen/Microsoft.Macios.Generator/DataModel/Property.Generator.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,13 @@ public Property ToStrongDelegate ()
322322
/// </summary>
323323
/// <param name="typeInfo">The type information for the 'this' parameter of the extension methods.</param>
324324
/// <returns>A tuple containing the getter method and an optional setter method (null if the property is read-only).</returns>
325-
public (Method? Getter, Method? Setter) ToExtensionMethods (TypeInfo typeInfo)
325+
public (Method Getter, Method Setter) ToExtensionMethods (TypeInfo typeInfo)
326326
{
327327
// create the parameter with the provided type info
328328
var thisParameter = new Parameter (0, typeInfo, "self") { IsThis = true, };
329329

330330
var getter = GetAccessor (AccessorKind.Getter);
331-
Method? getterMethod = null;
331+
Method getterMethod = Method.Default;
332332
if (!getter.IsNullOrDefault)
333333
getterMethod = new Method (
334334
type: typeInfo.FullyQualifiedName,
@@ -337,13 +337,16 @@ public Property ToStrongDelegate ()
337337
symbolAvailability: getter.SymbolAvailability,
338338
exportMethodData: new (getter.GetSelector (this)),
339339
attributes: [],
340-
modifiers: [Token (SyntaxKind.InternalKeyword), Token (SyntaxKind.StaticKeyword)],
340+
modifiers: [
341+
Token (SyntaxKind.InternalKeyword).WithTrailingTrivia (Space),
342+
Token (SyntaxKind.StaticKeyword).WithTrailingTrivia (Space)
343+
],
341344
parameters: [thisParameter]) {
342345
BindAs = BindAs // return bindas is the same as the property bindas
343346
};
344347

345348
var setter = GetAccessor (AccessorKind.Setter);
346-
Method? setterMethod = null;
349+
Method setterMethod = Method.Default;
347350
if (!setter.IsNullOrDefault) {
348351
// we need a second parameter for the setter
349352
var valueParameter = new Parameter (1, ReturnType, "value") {
@@ -356,7 +359,10 @@ public Property ToStrongDelegate ()
356359
symbolAvailability: setter.SymbolAvailability,
357360
exportMethodData: new (setter.GetSelector (this)),
358361
attributes: [],
359-
modifiers: [Token (SyntaxKind.InternalKeyword), Token (SyntaxKind.StaticKeyword)],
362+
modifiers: [
363+
Token (SyntaxKind.InternalKeyword).WithTrailingTrivia (Space),
364+
Token (SyntaxKind.StaticKeyword).WithTrailingTrivia (Space)
365+
],
360366
parameters: [thisParameter, valueParameter]);
361367
}
362368
return (getterMethod, setterMethod);

src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.KnownTypes.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,13 @@ static partial class BindingSyntaxFactory {
181181
@namespace: ["Foundation"],
182182
@class: "NSMutableDictionary");
183183

184+
/// <summary>
185+
/// TypeSyntax for Foundation.RequiredMember.
186+
/// </summary>
187+
public static readonly TypeSyntax RequiredMember = StringExtensions.GetIdentifierName (
188+
@namespace: ["Foundation"],
189+
@class: "RequiredMember");
190+
184191
// CoreMedia types
185192

186193
/// <summary>

src/rgen/Microsoft.Macios.Generator/Emitters/ProtocolEmitter.cs

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.Macios.Generator.Attributes;
1010
using Microsoft.Macios.Generator.Context;
1111
using Microsoft.Macios.Generator.DataModel;
12+
using Microsoft.Macios.Generator.Formatters;
1213
using Microsoft.Macios.Generator.IO;
1314
using ObjCBindings;
1415
using static Microsoft.Macios.Generator.Emitters.BindingSyntaxFactory;
@@ -21,7 +22,7 @@ namespace Microsoft.Macios.Generator.Emitters;
2122
/// Emitter responsible for generating protocol interfaces.
2223
/// Generates C# interfaces that represent Objective-C protocols with proper protocol member attributes.
2324
/// </summary>
24-
class ProtocolEmitter : ICodeEmitter {
25+
class ProtocolEmitter : IClassEmitter {
2526
/// <inheritdoc />
2627
public string GetSymbolName (in Binding binding) => binding.Name;
2728

@@ -49,17 +50,65 @@ void EmitDefaultConstructors (in BindingContext bindingContext, TabbedWriter<Str
4950
");
5051
}
5152

53+
/// <summary>
54+
/// Emits the properties for the protocol interface, including their getter and setter methods.
55+
/// </summary>
56+
/// <param name="context">The binding context containing protocol information.</param>
57+
/// <param name="properties">A collection of properties to emit, along with their getter and setter methods.</param>
58+
/// <param name="classBlock">The writer for the class block.</param>
59+
void EmitProperties (in BindingContext context, in ImmutableArray<(Property Property, Method Getter, Method Setter)> properties, TabbedWriter<StringWriter> classBlock)
60+
{
61+
var uiThreadCheck = (context.NeedsThreadChecks)
62+
? EnsureUiThread (context.RootContext.CurrentPlatform)
63+
: null;
64+
foreach (var (property, getter, setter) in properties) {
65+
// protocol properties are emitted in the following format:
66+
// - _GetFoo method if the property has a getter
67+
// - _SetFoo method if the property has a setter
68+
// - Foo property that uses the getter/setter methods
69+
70+
if (!getter.IsNullOrDefault) {
71+
this.EmitMethod (context, getter, classBlock, uiThreadCheck);
72+
}
73+
74+
if (!setter.IsNullOrDefault) {
75+
this.EmitMethod (context, setter, classBlock, uiThreadCheck);
76+
}
77+
78+
// write the property declarations
79+
classBlock.WriteLine ();
80+
classBlock.AppendMemberAvailability (property.SymbolAvailability);
81+
classBlock.AppendGeneratedCodeAttribute (optimizable: true);
82+
if (!property.IsOptional) {
83+
classBlock.AppendRequiredMemberAttribute ();
84+
}
85+
using (var propertyBlock = classBlock.CreateBlock (property.ToDeclaration ().ToString (), block: true)) {
86+
// we do not need to get the property accessors since we already have the getters/setters, we can
87+
// decide what needs to be added based on those methods
88+
if (!getter.IsNullOrDefault) {
89+
propertyBlock.AppendMemberAvailability (getter.SymbolAvailability);
90+
propertyBlock.WriteLine ($"get => {getter.Name} (this);");
91+
}
92+
93+
if (!setter.IsNullOrDefault) {
94+
propertyBlock.AppendMemberAvailability (setter.SymbolAvailability);
95+
propertyBlock.WriteLine ($"set => {setter.Name} (this, value);");
96+
}
97+
}
98+
}
99+
}
100+
52101
/// <summary>
53102
/// Gets the properties from the binding context and their corresponding extension methods.
54103
/// Returns a collection of tuples containing the property and its optional getter/setter methods.
55104
/// </summary>
56105
/// <param name="bindingContext">The binding context containing the properties.</param>
57106
/// <returns>An immutable array of tuples containing properties and their extension methods.</returns>
58-
static ImmutableArray<(Property Property, Method? Getter, Method? Setter)> GetProperties (in BindingContext bindingContext)
107+
static ImmutableArray<(Property Property, Method Getter, Method Setter)> CreatePropertyExtensionMethods (in BindingContext bindingContext)
59108
{
60109
// collect all properties and generate the extension methods, this will be used to generate the protocol
61110
// member data and later the extension methods.
62-
var propertiesBucket = ImmutableArray.CreateBuilder<(Property Property, Method? Getter, Method? Setter)> (bindingContext.Changes.Properties.Length);
111+
var propertiesBucket = ImmutableArray.CreateBuilder<(Property Property, Method Getter, Method Setter)> (bindingContext.Changes.Properties.Length);
63112
foreach (var property in bindingContext.Changes.Properties.OrderBy (p => p.Name)) {
64113
var (getter, setter) = property.ToExtensionMethods (new (bindingContext.Changes.Name, SpecialType.None));
65114
propertiesBucket.Add ((property, getter, setter));
@@ -95,7 +144,7 @@ public bool TryEmit (in BindingContext bindingContext, [NotNullWhen (false)] out
95144

96145
// we need to collect the properties extension methods, we do that with a helper method
97146
// that will return the properties and their getters/setters.
98-
var properties = GetProperties (bindingContext);
147+
var properties = CreatePropertyExtensionMethods (bindingContext);
99148

100149
// append the properties to the protocol member data
101150
foreach (var (property, getter, setter) in properties) {
@@ -113,6 +162,9 @@ public bool TryEmit (in BindingContext bindingContext, [NotNullWhen (false)] out
113162

114163
// emit static constructor
115164
EmitDefaultConstructors (in bindingContext, interfaceBlock);
165+
166+
// emit the properties, this will generate the getters/setters and the properties themselves
167+
EmitProperties (in bindingContext, in properties, interfaceBlock);
116168
}
117169
}
118170
return true;

src/rgen/Microsoft.Macios.Generator/Formatters/TypeInfoFormatter.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public static TypeSyntax GetIdentifierSyntax (this in TypeInfo typeInfo)
2222
TypeSyntax classSyntax;
2323
// the type info already provides the correct name, but we need to build the actual class for arrays and
2424
// generic types
25+
if (typeInfo.IsVoid) {
26+
return IdentifierName ("void");
27+
}
2528
if (typeInfo.IsArray) {
2629
// could be a params array or simply an array
2730
classSyntax = ArrayType (IdentifierName (typeInfo.Name))

src/rgen/Microsoft.Macios.Generator/IO/TabbedStringBuilderExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,16 @@ public static TabbedWriter<StringWriter> AppendProtocolAttribute (this TabbedWri
221221
self.WriteLine ($"[Protocol (Name = \"{name}\", WrapperType = typeof ({wrapperName}))]");
222222
return self;
223223
}
224+
225+
/// <summary>
226+
/// Appends a `[Foundation.RequiredMember]` attribute to the current writer.
227+
/// This attribute is used to mark a member as required.
228+
/// </summary>
229+
/// <param name="self">A tabbed string writer.</param>
230+
/// <returns>The current writer.</returns>
231+
public static TabbedWriter<StringWriter> AppendRequiredMemberAttribute (this TabbedWriter<StringWriter> self)
232+
{
233+
self.WriteLine ($"[{RequiredMember}]");
234+
return self;
235+
}
224236
}

tests/rgen/Microsoft.Macios.Generator.Tests/DataModel/PropertyTests/FromDeclarationTests.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ public class TestClass {
11471147
},
11481148
]
11491149
),
1150-
null!
1150+
Method.Default,
11511151
];
11521152

11531153
const string getterSetter = @"
@@ -1331,7 +1331,7 @@ IEnumerator IEnumerable.GetEnumerator ()
13311331

13321332
[Theory]
13331333
[AllSupportedPlatformsClassData<TestDataToExtensionMethods>]
1334-
void ToExtensionMethods (ApplePlatform platform, string inputText, TypeInfo protocolType, Method? expectedGetter, Method? expectedSetter)
1334+
void ToExtensionMethods (ApplePlatform platform, string inputText, TypeInfo protocolType, Method expectedGetter, Method expectedSetter)
13351335
{
13361336
var (compilation, syntaxTrees) = CreateCompilation (platform, sources: inputText);
13371337
Assert.Single (syntaxTrees);
@@ -1343,20 +1343,20 @@ void ToExtensionMethods (ApplePlatform platform, string inputText, TypeInfo prot
13431343
Assert.True (Property.TryCreate (declaration, semanticModel, out var changes));
13441344
Assert.NotNull (changes);
13451345
var (getter, setter) = changes.Value.ToExtensionMethods (protocolType);
1346-
if (expectedGetter is null) {
1347-
Assert.Null (getter);
1346+
if (expectedGetter.IsNullOrDefault) {
1347+
Assert.True (getter.IsNullOrDefault);
13481348
} else {
1349-
Assert.NotNull (getter);
1350-
Assert.True (getter.Value.IsExtension);
1351-
Assert.Equal (expectedGetter.Value, getter.Value);
1349+
Assert.False (getter.IsNullOrDefault);
1350+
Assert.True (getter.IsExtension);
1351+
Assert.Equal (expectedGetter, getter);
13521352
}
13531353

1354-
if (expectedSetter is null) {
1355-
Assert.Null (setter);
1354+
if (expectedSetter.IsNullOrDefault) {
1355+
Assert.True (setter.IsNullOrDefault);
13561356
} else {
1357-
Assert.NotNull (setter);
1358-
Assert.True (setter.Value.IsExtension);
1359-
Assert.Equal (expectedSetter.Value, setter.Value);
1357+
Assert.False (setter.IsNullOrDefault);
1358+
Assert.True (setter.IsExtension);
1359+
Assert.Equal (expectedSetter, setter);
13601360
}
13611361
}
13621362
}

tests/rgen/Microsoft.Macios.Generator.Tests/IO/TabbedStringBuilderTests.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,22 @@ public void AppendProtocolAttributeTests (string name, string wrapperName, int t
336336
Assert.Equal (expected, result);
337337
}
338338

339+
[Theory]
340+
[InlineData (0, "")]
341+
[InlineData (1, "\t")]
342+
[InlineData (5, "\t\t\t\t\t")]
343+
public void AppendRequiredMemberAttributeTests (int tabCount, string expectedTabs)
344+
{
345+
var expected = $"{expectedTabs}[global::Foundation.RequiredMember]\n";
346+
string result;
347+
using (var block = new TabbedStringBuilder (sb, tabCount)) {
348+
block.AppendRequiredMemberAttribute ();
349+
result = block.ToCode ();
350+
}
351+
352+
Assert.Equal (expected, result);
353+
}
354+
339355
[Theory]
340356
[InlineData (0, "")]
341357
[InlineData (1, "\t")]
@@ -618,7 +634,7 @@ public static IEnumerable<object []> AppendProtocolMemberDataTestData {
618634
ReturnTypeDelegateProxy = null,
619635
ParameterBlockProxy = [null],
620636
},
621-
"[ProtocolMember (IsRequired = true, IsProperty = false, IsStatic = false, Name = \"MyMethod\", Selector = \"completeRequestReturningItems:completionHandler:\", ReturnType = typeof (global::System.void), ParameterType = new Type [] { typeof (string) }, ParameterByRef = new bool [] { false }, ParameterBlockProxy = new Type? [] { null })]\n"
637+
"[ProtocolMember (IsRequired = true, IsProperty = false, IsStatic = false, Name = \"MyMethod\", Selector = \"completeRequestReturningItems:completionHandler:\", ReturnType = typeof (void), ParameterType = new Type [] { typeof (string) }, ParameterByRef = new bool [] { false }, ParameterBlockProxy = new Type? [] { null })]\n"
622638
];
623639

624640
yield return [
@@ -635,7 +651,7 @@ public static IEnumerable<object []> AppendProtocolMemberDataTestData {
635651
ReturnTypeDelegateProxy = null,
636652
ParameterBlockProxy = [null],
637653
},
638-
"[ProtocolMember (IsRequired = true, IsProperty = false, IsStatic = true, Name = \"MyMethod\", Selector = \"completeRequestReturningItems:completionHandler:\", ReturnType = typeof (global::System.void), ParameterType = new Type [] { typeof (string) }, ParameterByRef = new bool [] { false }, ParameterBlockProxy = new Type? [] { null })]\n"
654+
"[ProtocolMember (IsRequired = true, IsProperty = false, IsStatic = true, Name = \"MyMethod\", Selector = \"completeRequestReturningItems:completionHandler:\", ReturnType = typeof (void), ParameterType = new Type [] { typeof (string) }, ParameterByRef = new bool [] { false }, ParameterBlockProxy = new Type? [] { null })]\n"
639655
];
640656

641657
yield return [
@@ -721,7 +737,7 @@ public static IEnumerable<object []> AppendProtocolMemberDataTestData {
721737
ParameterBlockProxy = [null],
722738
IsVariadic = true,
723739
},
724-
"[ProtocolMember (IsRequired = true, IsProperty = false, IsStatic = false, Name = \"MyMethod\", Selector = \"completeRequestReturningItems:completionHandler:\", ReturnType = typeof (global::System.void), ParameterType = new Type [] { typeof (string) }, ParameterByRef = new bool [] { false }, ParameterBlockProxy = new Type? [] { null })]\n"
740+
"[ProtocolMember (IsRequired = true, IsProperty = false, IsStatic = false, Name = \"MyMethod\", Selector = \"completeRequestReturningItems:completionHandler:\", ReturnType = typeof (void), ParameterType = new Type [] { typeof (string) }, ParameterByRef = new bool [] { false }, ParameterBlockProxy = new Type? [] { null })]\n"
725741
];
726742

727743
yield return [
@@ -773,7 +789,7 @@ public static IEnumerable<object []> AppendProtocolMemberDataTestData {
773789
ParameterBlockProxy = [null],
774790
IsVariadic = false,
775791
},
776-
"[ProtocolMember (IsRequired = false, IsProperty = false, IsStatic = false, Name = \"MyMethod\", Selector = \"completeRequestReturningItems:completionHandler:\", ReturnType = typeof (global::System.void), ParameterType = new Type [] { typeof (string) }, ParameterByRef = new bool [] { false }, ParameterBlockProxy = new Type? [] { null })]\n"
792+
"[ProtocolMember (IsRequired = false, IsProperty = false, IsStatic = false, Name = \"MyMethod\", Selector = \"completeRequestReturningItems:completionHandler:\", ReturnType = typeof (void), ParameterType = new Type [] { typeof (string) }, ParameterByRef = new bool [] { false }, ParameterBlockProxy = new Type? [] { null })]\n"
777793
];
778794
}
779795
}

0 commit comments

Comments
 (0)