Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split generated code into multiple files #32

Merged
Show file tree
Hide file tree
Changes from 2 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
70 changes: 37 additions & 33 deletions src/Controls/src/BindingSourceGen/BindingCodeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,11 @@

namespace Microsoft.Maui.Controls.BindingSourceGen;

public sealed class BindingCodeWriter
public static class BindingCodeWriter
{
public static string GeneratedCodeAttribute => $"[GeneratedCodeAttribute(\"{typeof(BindingCodeWriter).Assembly.FullName}\", \"{typeof(BindingCodeWriter).Assembly.GetName().Version}\")]";

public string GenerateCode()
{
if (_bindings.Count == 0)
{
return string.Empty;
}

return DoGenerateCode();
}

private string DoGenerateCode() => $$"""
public static string GenerateCommonCode() => $$"""
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a .NET MAUI source generator.
Expand All @@ -35,7 +25,7 @@ namespace System.Runtime.CompilerServices

{{GeneratedCodeAttribute}}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
internal sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
Expand All @@ -52,16 +42,11 @@ public InterceptsLocationAttribute(string filePath, int line, int column)

namespace Microsoft.Maui.Controls.Generated
{
using System;
using System.CodeDom.Compiler;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls.Internals;

{{GeneratedCodeAttribute}}
file static class GeneratedBindableObjectExtensions
internal static partial class GeneratedBindableObjectExtensions
{
{{GenerateBindingMethods(indent: 2)}}

private static bool ShouldUseSetter(BindingMode mode, BindableProperty bindableProperty)
=> mode == BindingMode.OneWayToSource
|| mode == BindingMode.TwoWay
Expand All @@ -72,27 +57,48 @@ private static bool ShouldUseSetter(BindingMode mode, BindableProperty bindableP
}
""";

private readonly List<SetBindingInvocationDescription> _bindings = new();
private static string GenerateBindingCode(string bindingMethodBody) => $$"""
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a .NET MAUI source generator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable

public void AddBinding(SetBindingInvocationDescription binding)
namespace Microsoft.Maui.Controls.Generated
{
using System;
using System.CodeDom.Compiler;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls.Internals;

internal static partial class GeneratedBindableObjectExtensions
{
{{bindingMethodBody}}
}
}
""";


public static string GenerateBinding(SetBindingInvocationDescription binding, uint id)
{
if (!binding.NullableContextEnabled)
{
var referenceTypesConditionalAccessTransformer = new ReferenceTypesConditionalAccessTransformer();
binding = referenceTypesConditionalAccessTransformer.Transform(binding);
}
_bindings.Add(binding);

var bindingMethod = GenerateBindingMethod(binding, id);
return GenerateBindingCode(bindingMethod);
}

private string GenerateBindingMethods(int indent)
private static string GenerateBindingMethod(SetBindingInvocationDescription binding, uint id)
{
using var builder = new BindingInterceptorCodeBuilder(indent);

for (int i = 0; i < _bindings.Count; i++)
{
builder.AppendSetBindingInterceptor(id: i + 1, _bindings[i]);
}

using var builder = new BindingInterceptorCodeBuilder(indent: 2);
builder.AppendSetBindingInterceptor(id: id, binding: binding);
return builder.ToString();
}

Expand All @@ -113,10 +119,8 @@ public BindingInterceptorCodeBuilder(int indent = 0)
_indentedTextWriter = new IndentedTextWriter(_stringWriter, "\t") { Indent = indent };
}

public void AppendSetBindingInterceptor(int id, SetBindingInvocationDescription binding)
public void AppendSetBindingInterceptor(uint id, SetBindingInvocationDescription binding)
{
AppendBlankLine();

AppendLine(GeneratedCodeAttribute);
AppendInterceptorAttribute(binding.Location);
Append($"public static void SetBinding{id}");
Expand Down
19 changes: 9 additions & 10 deletions src/Controls/src/BindingSourceGen/BindingSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var bindings = bindingsWithDiagnostics
.Where(static binding => !binding.HasDiagnostics)
.Select(static (binding, t) => binding.Value)
.WithTrackingName(TrackingNames.Bindings)
.Collect();
.WithTrackingName(TrackingNames.Bindings);


context.RegisterSourceOutput(bindings, (spc, bindings) =>
context.RegisterPostInitializationOutput(spc =>
{
var codeWriter = new BindingCodeWriter();
spc.AddSource("GeneratedBindableObjectExtensionsCommon.g.cs", BindingCodeWriter.GenerateCommonCode());
});

foreach (var binding in bindings)
{
codeWriter.AddBinding(binding);
}
context.RegisterSourceOutput(bindings, (spc, binding) =>
{

spc.AddSource("GeneratedBindableObjectExtensions.g.cs", codeWriter.GenerateCode());
var fileName = $"{binding.Location.FilePath}/GeneratedBindableObjectExtensions-{binding.Location.Line}-{binding.Location.Column}.g.cs";
spc.AddSource(fileName, BindingCodeWriter.GenerateBinding(binding, BindingGenerationUtilities.ComputeSha256Hash(fileName)));
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.CodeAnalysis;

namespace Microsoft.Maui.Controls.BindingSourceGen;

internal static class BindingGenerationUtilities
public static class BindingGenerationUtilities
{
internal static bool IsTypeNullable(ITypeSymbol typeInfo, bool enabledNullable)
{
Expand Down Expand Up @@ -42,4 +44,14 @@ internal static string GetGlobalName(ITypeSymbol typeSymbol, bool isNullable, bo

return typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
}

public static uint ComputeSha256Hash(string rawData)
{
using SHA256 sha256Hash = SHA256.Create();
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));

uint hashInt = BitConverter.ToUInt32(bytes, 0);

return hashInt;
}
}
2 changes: 1 addition & 1 deletion src/Controls/src/BindingSourceGen/GeneratorDataModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public interface IPathPart : IEquatable<IPathPart>
public string? PropertyName { get; }
}

internal sealed record Result<T>(T? OptionalValue, EquatableArray<DiagnosticInfo> Diagnostics)
public sealed record Result<T>(T? OptionalValue, EquatableArray<DiagnosticInfo> Diagnostics)
{
public bool HasDiagnostics => Diagnostics.Length > 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ internal static void CodeIsEqual(string expectedCode, string actualCode)
{
Assert.Equal(expectedLine, actualLine);
}

Assert.Equal(expectedLines.Count(), actualLines.Count());
}

internal static void BindingsAreEqual(SetBindingInvocationDescription expectedBinding, CodeGeneratorResult codeGeneratorResult)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,9 @@ namespace BindingSourceGen.UnitTests;
public class BindingCodeWriterTests
{
[Fact]
public void BuildsWholeDocument()
public void BuildsCommonCode()
{
var codeWriter = new BindingCodeWriter();
codeWriter.AddBinding(new SetBindingInvocationDescription(
Location: new InterceptorLocation(FilePath: @"Path\To\Program.cs", Line: 20, Column: 30),
SourceType: new TypeDescription("global::MyNamespace.MySourceClass", IsValueType: false, IsNullable: false, IsGenericParameter: false),
PropertyType: new TypeDescription("global::MyNamespace.MyPropertyClass", IsValueType: false, IsNullable: false, IsGenericParameter: false),
Path: new EquatableArray<IPathPart>([
new MemberAccess("A"),
new ConditionalAccess(new MemberAccess("B")),
new ConditionalAccess(new MemberAccess("C")),
]),
SetterOptions: new(IsWritable: true, AcceptsNullValue: false),
NullableContextEnabled: true));

var code = codeWriter.GenerateCode();
var code = BindingCodeWriter.GenerateCommonCode();
AssertExtensions.CodeIsEqual(
$$"""
//------------------------------------------------------------------------------
Expand All @@ -42,30 +29,75 @@ namespace System.Runtime.CompilerServices

{{BindingCodeWriter.GeneratedCodeAttribute}}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
internal sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
FilePath = filePath;
Line = line;
Column = column;
}

public string FilePath { get; }
public int Line { get; }
public int Column { get; }
}
}

namespace Microsoft.Maui.Controls.Generated
{
using System.CodeDom.Compiler;

{{BindingCodeWriter.GeneratedCodeAttribute}}
internal static partial class GeneratedBindableObjectExtensions
{
private static bool ShouldUseSetter(BindingMode mode, BindableProperty bindableProperty)
=> mode == BindingMode.OneWayToSource
|| mode == BindingMode.TwoWay
|| (mode == BindingMode.Default
&& (bindableProperty.DefaultBindingMode == BindingMode.OneWayToSource
|| bindableProperty.DefaultBindingMode == BindingMode.TwoWay));
}
}
""",
code);
}

[Fact]
public void BuildsWholeBinding()
{
var code = BindingCodeWriter.GenerateBinding(new SetBindingInvocationDescription(
Location: new InterceptorLocation(FilePath: @"Path\To\Program.cs", Line: 20, Column: 30),
SourceType: new TypeDescription("global::MyNamespace.MySourceClass", IsValueType: false, IsNullable: false, IsGenericParameter: false),
PropertyType: new TypeDescription("global::MyNamespace.MyPropertyClass", IsValueType: false, IsNullable: false, IsGenericParameter: false),
Path: new EquatableArray<IPathPart>([
new MemberAccess("A"),
new ConditionalAccess(new MemberAccess("B")),
new ConditionalAccess(new MemberAccess("C")),
]),
SetterOptions: new(IsWritable: true, AcceptsNullValue: false),
NullableContextEnabled: true), id: 1);

AssertExtensions.CodeIsEqual(
$$"""
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a .NET MAUI source generator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable

namespace Microsoft.Maui.Controls.Generated
{
using System;
using System.CodeDom.Compiler;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls.Internals;

{{BindingCodeWriter.GeneratedCodeAttribute}}
file static class GeneratedBindableObjectExtensions
internal static partial class GeneratedBindableObjectExtensions
{

{{BindingCodeWriter.GeneratedCodeAttribute}}
Expand Down Expand Up @@ -115,13 +147,6 @@ public static void SetBinding1(
};
bindableObject.SetBinding(bindableProperty, binding);
}

private static bool ShouldUseSetter(BindingMode mode, BindableProperty bindableProperty)
=> mode == BindingMode.OneWayToSource
|| mode == BindingMode.TwoWay
|| (mode == BindingMode.Default
&& (bindableProperty.DefaultBindingMode == BindingMode.OneWayToSource
|| bindableProperty.DefaultBindingMode == BindingMode.TwoWay));
}
}
""",
Expand Down Expand Up @@ -482,5 +507,4 @@ public static void SetBinding1(
""",
code);
}

}
Loading
Loading