Skip to content

Commit

Permalink
Improve binder gen binding logic (init, member binding, polymorphism) (
Browse files Browse the repository at this point in the history
…#91717)

* Improve binder gen binding logic (init, member minding, polymorphism)

* Improve handling of types with no bindable members

* Enhance bind-parse-type-from-method-param to comprehensive baseline test

* Move emitter methods to the files they are used in

* Address feedback regarding assertions
  • Loading branch information
layomia authored Sep 11, 2023
1 parent e21a975 commit 9a68b7e
Show file tree
Hide file tree
Showing 36 changed files with 1,170 additions and 720 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using SourceGenerators;

Expand All @@ -16,12 +13,6 @@ private sealed partial class Emitter
{
private readonly SourceProductionContext _context;
private readonly SourceGenerationSpec _sourceGenSpec;

private bool _emitBlankLineBeforeNextStatement;
private int _valueSuffixIndex;

private static readonly Regex s_arrayBracketsRegex = new(Regex.Escape("[]"));

private readonly SourceWriter _writer = new();

public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec)
Expand Down Expand Up @@ -64,163 +55,6 @@ file static class {{Identifier.BindingExtensions}}
_context.AddSource($"{Identifier.BindingExtensions}.g.cs", _writer.ToSourceText());
}

private void EmitBindCoreCall(
TypeSpec type,
string memberAccessExpr,
string configArgExpr,
InitializationKind initKind,
Action<string>? writeOnSuccess = null)
{
Debug.Assert(type.CanInitialize);

if (!type.NeedsMemberBinding)
{
EmitObjectInit(memberAccessExpr, initKind);
return;
}

string tempIdentifier = GetIncrementalIdentifier(Identifier.temp);
if (initKind is InitializationKind.AssignmentWithNullCheck)
{
Debug.Assert(!type.IsValueType);
_writer.WriteLine($"{type.DisplayString}? {tempIdentifier} = {memberAccessExpr};");
EmitBindCoreCall(tempIdentifier, InitializationKind.AssignmentWithNullCheck);
}
else if (initKind is InitializationKind.None && type.IsValueType)
{
EmitBindCoreCall(tempIdentifier, InitializationKind.Declaration);
_writer.WriteLine($"{memberAccessExpr} = {tempIdentifier};");
}
else
{
EmitBindCoreCall(memberAccessExpr, initKind);
}

void EmitBindCoreCall(string instanceExpr, InitializationKind initKind)
{
string bindCoreCall = $@"{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configArgExpr}, ref {instanceExpr}, {Identifier.binderOptions});";
EmitObjectInit(instanceExpr, initKind);
_writer.WriteLine(bindCoreCall);
writeOnSuccess?.Invoke(instanceExpr);
}

void EmitObjectInit(string instanceExpr, InitializationKind initKind)
{
if (initKind is not InitializationKind.None)
{
this.EmitObjectInit(type, instanceExpr, initKind, configArgExpr);
}
}
}

private void EmitBindLogicFromString(
ParsableFromStringSpec type,
string sectionValueExpr,
string sectionPathExpr,
Action<string>? writeOnSuccess,
bool checkForNullSectionValue,
bool useIncrementalStringValueIdentifier)
{
StringParsableTypeKind typeKind = type.StringParsableTypeKind;
Debug.Assert(typeKind is not StringParsableTypeKind.None);

string nonNull_StringValue_Identifier = useIncrementalStringValueIdentifier ? GetIncrementalIdentifier(Identifier.value) : Identifier.value;
string stringValueToParse_Expr = checkForNullSectionValue ? nonNull_StringValue_Identifier : sectionValueExpr;

string parsedValueExpr;
if (typeKind is StringParsableTypeKind.AssignFromSectionValue)
{
parsedValueExpr = stringValueToParse_Expr;
}
else if (typeKind is StringParsableTypeKind.Enum)
{
parsedValueExpr = $"ParseEnum<{type.DisplayString}>({stringValueToParse_Expr}, () => {sectionPathExpr})";
}
else
{
parsedValueExpr = $"{type.ParseMethodName}({stringValueToParse_Expr}, () => {sectionPathExpr})";
}

if (!checkForNullSectionValue)
{
InvokeWriteOnSuccess();
}
else
{
EmitStartBlock($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
InvokeWriteOnSuccess();
EmitEndBlock();
}

void InvokeWriteOnSuccess() => writeOnSuccess?.Invoke(parsedValueExpr);
}

private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, InitializationKind initKind, string configArgExpr)
{
Debug.Assert(type.CanInitialize && initKind is not InitializationKind.None);

string initExpr;
CollectionSpec? collectionType = type as CollectionSpec;

string effectiveDisplayString = type.DisplayString;
if (collectionType is not null)
{
if (collectionType is EnumerableSpec { InitializationStrategy: InitializationStrategy.Array })
{
initExpr = $"new {s_arrayBracketsRegex.Replace(effectiveDisplayString, "[0]", 1)}";
}
else
{
effectiveDisplayString = (collectionType.ConcreteType ?? collectionType).DisplayString;
initExpr = $"new {effectiveDisplayString}()";
}
}
else if (type.InitializationStrategy is InitializationStrategy.ParameterlessConstructor)
{
initExpr = $"new {effectiveDisplayString}()";
}
else
{
Debug.Assert(type.InitializationStrategy is InitializationStrategy.ParameterizedConstructor);
string initMethodIdentifier = GetInitalizeMethodDisplayString(((ObjectSpec)type));
initExpr = $"{initMethodIdentifier}({configArgExpr}, {Identifier.binderOptions})";
}

if (initKind == InitializationKind.Declaration)
{
Debug.Assert(!memberAccessExpr.Contains("."));
_writer.WriteLine($"var {memberAccessExpr} = {initExpr};");
}
else if (initKind == InitializationKind.AssignmentWithNullCheck)
{
if (collectionType is CollectionSpec
{
InitializationStrategy: InitializationStrategy.ParameterizedConstructor or InitializationStrategy.ToEnumerableMethod
})
{
if (collectionType.InitializationStrategy is InitializationStrategy.ParameterizedConstructor)
{
_writer.WriteLine($"{memberAccessExpr} = {memberAccessExpr} is null ? {initExpr} : new {effectiveDisplayString}({memberAccessExpr});");
}
else
{
_writer.WriteLine($"{memberAccessExpr} = {memberAccessExpr} is null ? {initExpr} : {memberAccessExpr}.{collectionType.ToEnumerableMethodCall!};");
}
}
else
{
_writer.WriteLine($"{memberAccessExpr} ??= {initExpr};");
}
}
else
{
Debug.Assert(initKind is InitializationKind.SimpleAssignment);
_writer.WriteLine($"{memberAccessExpr} = {initExpr};");
}

return true;
}

private void EmitInterceptsLocationAttrDecl()
{
_writer.WriteLine();
Expand Down Expand Up @@ -250,18 +84,6 @@ private void EmitUsingStatements()
_writer.WriteLine($"using {@namespace};");
}
}

private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
{
string returnPostfix = voidReturn ? string.Empty : " null";
_writer.WriteLine($$"""
if (!{{Identifier.HasValueOrChildren}}({{Identifier.configuration}}))
{
return{{returnPostfix}};
}
""");
_writer.WriteLine();
}
}
}
}
Loading

0 comments on commit 9a68b7e

Please sign in to comment.