Skip to content

Commit 98620d0

Browse files
[RGen] Provide a method to convert a protocol method to a constructor.
This allows to convert the method marked as factories in a protocol into constructors, later we can use this conversion to inline the factory methods as constructors in the classes that implement a protocol with factories.
1 parent a8a1070 commit 98620d0

File tree

4 files changed

+155
-2
lines changed

4 files changed

+155
-2
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public Constructor (string type,
4040
ExportData<ObjCBindings.Constructor> exportData,
4141
ImmutableArray<AttributeCodeChange> attributes,
4242
ImmutableArray<SyntaxToken> modifiers,
43-
ImmutableArray<Parameter> parameters)
43+
ImmutableArray<Parameter> parameters) : this (StructState.Initialized)
4444
{
4545
Type = type;
4646
SymbolAvailability = symbolAvailability;

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ namespace Microsoft.Macios.Generator.DataModel;
1212

1313
[StructLayout (LayoutKind.Auto)]
1414
readonly partial struct Constructor : IEquatable<Constructor> {
15+
16+
/// <summary>
17+
/// The initialization state of the struct.
18+
/// </summary>
19+
StructState State { get; init; } = StructState.Default;
20+
21+
/// <summary>
22+
/// Gets the default, uninitialized instance of <see cref="Constructor"/>.
23+
/// </summary>
24+
public static Constructor Default { get; } = new (StructState.Default);
25+
26+
/// <summary>
27+
/// Gets a value indicating whether the instance is the default, uninitialized instance.
28+
/// </summary>
29+
public bool IsNullOrDefault => State == StructState.Default;
30+
1531
/// <summary>
1632
/// Type name that owns the constructor.
1733
/// </summary>
@@ -36,12 +52,18 @@ namespace Microsoft.Macios.Generator.DataModel;
3652
/// Parameters list.
3753
/// </summary>
3854
public ImmutableArray<Parameter> Parameters { get; init; } = [];
55+
56+
Constructor (StructState state)
57+
{
58+
State = state;
59+
Type = string.Empty;
60+
}
3961

4062
public Constructor (string type,
4163
SymbolAvailability symbolAvailability,
4264
ImmutableArray<AttributeCodeChange> attributes,
4365
ImmutableArray<SyntaxToken> modifiers,
44-
ImmutableArray<Parameter> parameters)
66+
ImmutableArray<Parameter> parameters) : this (StructState.Initialized)
4567
{
4668
Type = type;
4769
SymbolAvailability = symbolAvailability;

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,33 @@ .. Modifiers.Where (m =>
307307
};
308308
}
309309

310+
/// <summary>
311+
/// Converts the current factory method into a constructor for a given target class.
312+
/// </summary>
313+
/// <param name="targetClass">The type information of the class for which the constructor is being created.</param>
314+
/// <returns>
315+
/// A new <see cref="Constructor"/> instance if the method is a factory method;
316+
/// otherwise, an uninitialized <see cref="Constructor"/> instance.
317+
/// </returns>
318+
public Constructor ToConstructor (TypeInfo targetClass)
319+
{
320+
// if the method is not a factory, we cannot convert it to a constructor so we will return the default value
321+
// which is an uninitialized instance
322+
if (!IsFactory)
323+
return Constructor.Default;
324+
325+
// we need to create a constructor with the same modifiers, parameters and the availability of the method
326+
// since there is no guarantee that the target class has the same availability as the method
327+
return new(
328+
type: targetClass.Name,
329+
exportData: new (ExportMethodData.Selector),
330+
symbolAvailability: SymbolAvailability,
331+
attributes: [], // we do not really care about the attributes on the constructor that is going to be inlined
332+
modifiers: modifiers,
333+
parameters: Parameters
334+
);
335+
}
336+
310337
/// <summary>
311338
/// Creates a new method instance with the specified modifiers.
312339
/// </summary>

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

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,4 +1193,108 @@ interface IAVAudioMixing {
11931193
Assert.Contains (newMethod.Modifiers, m => m.IsKind (SyntaxKind.InternalKeyword));
11941194
Assert.Contains (newMethod.Modifiers, m => m.IsKind (SyntaxKind.StaticKeyword));
11951195
}
1196+
1197+
class TestDataFromMethodDeclarationToConstructor : IEnumerable<object []> {
1198+
public IEnumerator<object []> GetEnumerator ()
1199+
{
1200+
1201+
const string noFactoryMethod = @"
1202+
using System;
1203+
using ObjCBindings;
1204+
using ObjCRuntime;
1205+
1206+
namespace NS {
1207+
public class MyClass {
1208+
1209+
[Export<Method> (""initWithInputs:"", Flags = ObjCBindings.Method.Default)]
1210+
public static MyClass MyFactoryMethod () { }
1211+
}
1212+
}
1213+
";
1214+
1215+
yield return [noFactoryMethod];
1216+
1217+
const string noParameter = @"
1218+
using System;
1219+
using ObjCBindings;
1220+
using ObjCRuntime;
1221+
1222+
namespace NS {
1223+
public class MyClass {
1224+
1225+
[Export<Method> (""initWithInputs:"", Flags = ObjCBindings.Method.Factory)]
1226+
public static MyClass MyFactoryMethod () { }
1227+
}
1228+
}
1229+
";
1230+
1231+
yield return [noParameter];
1232+
1233+
const string singleParameter = @"
1234+
using System;
1235+
using ObjCBindings;
1236+
using ObjCRuntime;
1237+
1238+
namespace NS {
1239+
public class MyClass {
1240+
1241+
[Export<Method> (""initWithInputs:"", Flags = ObjCBindings.Method.Factory)]
1242+
public static MyClass MyFactoryMethod (string[]? input) { }
1243+
}
1244+
}
1245+
";
1246+
1247+
yield return [singleParameter];
1248+
1249+
const string multiParameter = @"
1250+
using System;
1251+
using ObjCBindings;
1252+
using ObjCRuntime;
1253+
1254+
namespace NS {
1255+
public class MyClass {
1256+
1257+
[Export<Method> (""initWithInputs:other:"", Flags = ObjCBindings.Method.Factory)]
1258+
public static MyClass MyFactoryMethod (string[]? input, int other) { }
1259+
}
11961260
}
1261+
";
1262+
1263+
yield return [multiParameter];
1264+
1265+
}
1266+
1267+
IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
1268+
}
1269+
1270+
[Theory]
1271+
[AllSupportedPlatformsClassData<TestDataFromMethodDeclarationToConstructor>]
1272+
void FromMethodDeclarationToConstructor (ApplePlatform platform, string inputText)
1273+
{
1274+
var (compilation, syntaxTrees) = CreateCompilation (platform, sources: inputText);
1275+
Assert.Single (syntaxTrees);
1276+
var semanticModel = compilation.GetSemanticModel (syntaxTrees [0]);
1277+
var declaration = syntaxTrees [0].GetRoot ()
1278+
.DescendantNodes ().OfType<MethodDeclarationSyntax> ()
1279+
.FirstOrDefault ();
1280+
Assert.NotNull (declaration);
1281+
Assert.True (Method.TryCreate (declaration, semanticModel, out var changes));
1282+
Assert.NotNull (changes);
1283+
var constructorType = ReturnTypeForClass ("NS.MyClass");
1284+
var constructor = changes.Value.ToConstructor (constructorType);
1285+
if (changes.Value.IsFactory) {
1286+
Assert.Equal (constructorType.Name, constructor.Type);
1287+
Assert.Equal (changes.Value.Selector, constructor.Selector);
1288+
// will compare that all parameters are the same, that is, same type, name and position
1289+
var parameterEqualityComparer = new MethodParameterEqualityComparer ();
1290+
Assert.Equal (changes.Value.Parameters, constructor.Parameters, parameterEqualityComparer);
1291+
Assert.Equal (changes.Value.SymbolAvailability, constructor.SymbolAvailability);
1292+
var modifiersComparer = new ModifiersEqualityComparer ();
1293+
Assert.Equal (changes.Value.Modifiers, constructor.Modifiers, modifiersComparer);
1294+
Assert.False (constructor.IsNullOrDefault);
1295+
} else {
1296+
Assert.True (constructor.IsNullOrDefault);
1297+
}
1298+
}
1299+
}
1300+

0 commit comments

Comments
 (0)