Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public Constructor (string type,
ExportData<ObjCBindings.Constructor> exportData,
ImmutableArray<AttributeCodeChange> attributes,
ImmutableArray<SyntaxToken> modifiers,
ImmutableArray<Parameter> parameters)
ImmutableArray<Parameter> parameters) : this (StructState.Initialized)
{
Type = type;
SymbolAvailability = symbolAvailability;
Expand Down
24 changes: 23 additions & 1 deletion src/rgen/Microsoft.Macios.Generator/DataModel/Constructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ namespace Microsoft.Macios.Generator.DataModel;

[StructLayout (LayoutKind.Auto)]
readonly partial struct Constructor : IEquatable<Constructor> {

/// <summary>
/// The initialization state of the struct.
/// </summary>
StructState State { get; init; } = StructState.Default;

/// <summary>
/// Gets the default, uninitialized instance of <see cref="Constructor"/>.
/// </summary>
public static Constructor Default { get; } = new (StructState.Default);

/// <summary>
/// Gets a value indicating whether the instance is the default, uninitialized instance.
/// </summary>
public bool IsNullOrDefault => State == StructState.Default;

/// <summary>
/// Type name that owns the constructor.
/// </summary>
Expand All @@ -37,11 +53,17 @@ namespace Microsoft.Macios.Generator.DataModel;
/// </summary>
public ImmutableArray<Parameter> Parameters { get; init; } = [];

Constructor (StructState state)
{
State = state;
Type = string.Empty;
}

public Constructor (string type,
SymbolAvailability symbolAvailability,
ImmutableArray<AttributeCodeChange> attributes,
ImmutableArray<SyntaxToken> modifiers,
ImmutableArray<Parameter> parameters)
ImmutableArray<Parameter> parameters) : this (StructState.Initialized)
{
Type = type;
SymbolAvailability = symbolAvailability;
Expand Down
27 changes: 27 additions & 0 deletions src/rgen/Microsoft.Macios.Generator/DataModel/Method.Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,33 @@ .. Modifiers.Where (m =>
};
}

/// <summary>
/// Converts the current factory method into a constructor for a given target class.
/// </summary>
/// <param name="targetClass">The type information of the class for which the constructor is being created.</param>
/// <returns>
/// A new <see cref="Constructor"/> instance if the method is a factory method;
/// otherwise, an uninitialized <see cref="Constructor"/> instance.
/// </returns>
public Constructor ToConstructor (TypeInfo targetClass)
{
// if the method is not a factory, we cannot convert it to a constructor so we will return the default value
// which is an uninitialized instance
if (!IsFactory)
return Constructor.Default;

// we need to create a constructor with the same modifiers, parameters and the availability of the method
// since there is no guarantee that the target class has the same availability as the method
return new (
type: targetClass.Name,
exportData: new (ExportMethodData.Selector),
symbolAvailability: SymbolAvailability,
attributes: [], // we do not really care about the attributes on the constructor that is going to be inlined
modifiers: modifiers,
parameters: Parameters
);
}

/// <summary>
/// Creates a new method instance with the specified modifiers.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1193,4 +1193,108 @@ interface IAVAudioMixing {
Assert.Contains (newMethod.Modifiers, m => m.IsKind (SyntaxKind.InternalKeyword));
Assert.Contains (newMethod.Modifiers, m => m.IsKind (SyntaxKind.StaticKeyword));
}

class TestDataFromMethodDeclarationToConstructor : IEnumerable<object []> {
public IEnumerator<object []> GetEnumerator ()
{

const string noFactoryMethod = @"
using System;
using ObjCBindings;
using ObjCRuntime;

namespace NS {
public class MyClass {

[Export<Method> (""initWithInputs:"", Flags = ObjCBindings.Method.Default)]
public static MyClass MyFactoryMethod () { }
}
}
";

yield return [noFactoryMethod];

const string noParameter = @"
using System;
using ObjCBindings;
using ObjCRuntime;

namespace NS {
public class MyClass {

[Export<Method> (""initWithInputs:"", Flags = ObjCBindings.Method.Factory)]
public static MyClass MyFactoryMethod () { }
}
}
";

yield return [noParameter];

const string singleParameter = @"
using System;
using ObjCBindings;
using ObjCRuntime;

namespace NS {
public class MyClass {

[Export<Method> (""initWithInputs:"", Flags = ObjCBindings.Method.Factory)]
public static MyClass MyFactoryMethod (string[]? input) { }
}
}
";

yield return [singleParameter];

const string multiParameter = @"
using System;
using ObjCBindings;
using ObjCRuntime;

namespace NS {
public class MyClass {

[Export<Method> (""initWithInputs:other:"", Flags = ObjCBindings.Method.Factory)]
public static MyClass MyFactoryMethod (string[]? input, int other) { }
}
}
";

yield return [multiParameter];

}

IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
}

[Theory]
[AllSupportedPlatformsClassData<TestDataFromMethodDeclarationToConstructor>]
void FromMethodDeclarationToConstructor (ApplePlatform platform, string inputText)
{
var (compilation, syntaxTrees) = CreateCompilation (platform, sources: inputText);
Assert.Single (syntaxTrees);
var semanticModel = compilation.GetSemanticModel (syntaxTrees [0]);
var declaration = syntaxTrees [0].GetRoot ()
.DescendantNodes ().OfType<MethodDeclarationSyntax> ()
.FirstOrDefault ();
Assert.NotNull (declaration);
Assert.True (Method.TryCreate (declaration, semanticModel, out var changes));
Assert.NotNull (changes);
var constructorType = ReturnTypeForClass ("NS.MyClass");
var constructor = changes.Value.ToConstructor (constructorType);
if (changes.Value.IsFactory) {
Assert.Equal (constructorType.Name, constructor.Type);
Assert.Equal (changes.Value.Selector, constructor.Selector);
// will compare that all parameters are the same, that is, same type, name and position
var parameterEqualityComparer = new MethodParameterEqualityComparer ();
Assert.Equal (changes.Value.Parameters, constructor.Parameters, parameterEqualityComparer);
Assert.Equal (changes.Value.SymbolAvailability, constructor.SymbolAvailability);
var modifiersComparer = new ModifiersEqualityComparer ();
Assert.Equal (changes.Value.Modifiers, constructor.Modifiers, modifiersComparer);
Assert.False (constructor.IsNullOrDefault);
} else {
Assert.True (constructor.IsNullOrDefault);
}
}
}

Loading