Skip to content

Commit

Permalink
Merge pull request #83 from Nyrest/RefactorGenerators
Browse files Browse the repository at this point in the history
Refactor generators
  • Loading branch information
Nyrest authored Aug 15, 2024
2 parents 43c0ea0 + ec928fc commit 775b0bd
Show file tree
Hide file tree
Showing 19 changed files with 99 additions and 322 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,12 @@ private static void BuildSource(in SourceProductionContext context, Compilation
.ToArray();
BuildGeneratorOptionsConstructor(in context, symbolsWithAttrData);
BuildCodeBuilderPreProcessorDefinitions(in context, symbolsWithAttrData);
BuildCodeGeneratorExtraCheck(in context, symbolsWithAttrData);
}

public static void BuildGeneratorOptionsConstructor(in SourceProductionContext context, (IPropertySymbol symbol, AttributeData attrData)[] symbolsWithAttrData)
{
const string indent = " ";
StringBuilder sb = new StringBuilder("""
StringBuilder sb = new("""
#nullable enable
namespace FastGenericNew.SourceGenerator;
Expand Down Expand Up @@ -94,7 +93,7 @@ public GeneratorOptions(AnalyzerConfigOptionsProvider? provider)

public static void BuildCodeBuilderPreProcessorDefinitions(in SourceProductionContext context, (IPropertySymbol symbol, AttributeData attrData)[] symbolsWithAttrData)
{
StringBuilder sb = new StringBuilder("""
StringBuilder sb = new("""
#nullable enable
namespace FastGenericNew.SourceGenerator.Utilities;
Expand Down Expand Up @@ -124,41 +123,5 @@ private partial void _Write_PreProcessorDefinitions()
""");
context.AddSource("CodeBuilder.PreProcessor.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
}

public static void BuildCodeGeneratorExtraCheck(in SourceProductionContext context, (IPropertySymbol symbol, AttributeData attrData)[] symbolsWithAttrData)
{
const string indent = " ";
StringBuilder sb = new StringBuilder("""
#nullable enable
namespace FastGenericNew.SourceGenerator;
partial class CodeGenerator
{
private static partial bool PreProcessorRelatedCheck(in GeneratorOptions oldValue, in GeneratorOptions newValue) =>
""", 4096);
bool isFirst = true;
foreach (var (symbol, attrData) in symbolsWithAttrData)
{
if (!attrData.TryGetNamedArgument(GeneratorOptionAttributeGenerator.Arg_PresentPreProcessor, out bool value) || !value)
continue;
var propertyName = symbol.Name;
sb.Append(indent);

if (!isFirst)
{
sb.Append("|| ");
}
else isFirst = false;
sb.AppendLine($"oldValue.{propertyName} != newValue.{propertyName}");
}
if (isFirst) sb.Append("false");
sb.Append("""
;
}
""");
context.AddSource("CodeGenerator.PreProcessorRelatedCheck.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
}
}
}
18 changes: 10 additions & 8 deletions src/FastGenericNew.SourceGenerator/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ public abstract partial class CodeGenerator
{
public abstract string Filename { get; }

public virtual bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue) =>
oldValue.Namespace != newValue.Namespace
|| oldValue.MaxParameterCount != newValue.MaxParameterCount
|| oldValue.AlertGeneratedFile != newValue.AlertGeneratedFile
|| oldValue.PrettyOutput != newValue.PrettyOutput
|| PreProcessorRelatedCheck(in oldValue, in newValue);

public abstract CodeGenerationResult Generate(in GeneratorOptions options);

private static partial bool PreProcessorRelatedCheck(in GeneratorOptions oldValue, in GeneratorOptions newValue);
public virtual GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return new GeneratorOptions() with
{
Namespace = options.Namespace,
MaxParameterCount = options.MaxParameterCount,
AlertGeneratedFile = options.AlertGeneratedFile,
PrettyOutput = options.PrettyOutput
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,11 @@ public static T CreateInstance()
return builder.BuildAndDispose(this);
}

public override bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue)
public override GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return base.ShouldUpdate(oldValue, newValue) && newValue.AllowUnsafeImplementation;
return base.GetOptionsSubset(options) with
{
AllowUnsafeImplementation = options.AllowUnsafeImplementation,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,11 @@ public static T NewOrDefault<
return builder.BuildAndDispose(this);
}

public override bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue)
public override GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return base.ShouldUpdate(oldValue, newValue) && newValue.GenerateCreateInstance;
return base.GetOptionsSubset(options) with
{
GenerateCreateInstance = options.GenerateCreateInstance,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,11 @@ public override CodeGenerationResult Generate(in GeneratorOptions options)
return builder.BuildAndDispose(this);
}

public override bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue) =>
base.ShouldUpdate(oldValue, newValue)
|| oldValue.ForceFastNewDelegate != newValue.ForceFastNewDelegate;
public override GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return base.GetOptionsSubset(options) with
{
ForceFastNewDelegate = options.ForceFastNewDelegate,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ public override CodeGenerationResult Generate(in GeneratorOptions options)
return builder.BuildAndDispose(this);
}

public override bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue) =>
base.ShouldUpdate(oldValue, newValue)
|| oldValue.ForceFastNewDelegate != newValue.ForceFastNewDelegate;
public override GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return base.GetOptionsSubset(options) with
{
ForceFastNewDelegate = options.ForceFastNewDelegate,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ public override CodeGenerationResult Generate(in GeneratorOptions options)
return builder.BuildAndDispose(this);
}

public override bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue) =>
base.ShouldUpdate(oldValue, newValue)
|| oldValue.PublicFastNew != newValue.PublicFastNew;
public override GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return base.GetOptionsSubset(options) with
{
PublicFastNew = options.PublicFastNew,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ public static bool TryNewOrDefault<
return builder.BuildAndDispose(this);
}

public override bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue)
public override GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return base.ShouldUpdate(oldValue, newValue) && newValue.GenerateTryCreateInstance;
return base.GetOptionsSubset(options) with
{
GenerateTryCreateInstance = options.GenerateTryCreateInstance,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ public override CodeGenerationResult Generate(in GeneratorOptions options)
return builder.BuildAndDispose(this);
}

// Since this won't be invoked if the oldValue equals the newValue.
// So just do it.
public override bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue) => newValue.OutputGenerationInfo;
public override GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return base.GetOptionsSubset(options) with
{
OutputGenerationInfo = options.OutputGenerationInfo,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ public static T SmartThrowImpl<T>()
return builder.BuildAndDispose(this);
}

public override bool ShouldUpdate(in GeneratorOptions oldValue, in GeneratorOptions newValue) =>
oldValue.Namespace != newValue.Namespace
|| oldValue.AlertGeneratedFile != newValue.AlertGeneratedFile;
public override GeneratorOptions GetOptionsSubset(GeneratorOptions options)
{
return base.GetOptionsSubset(options) with
{
AlertGeneratedFile = options.AlertGeneratedFile,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
<CompilerVisibleProperty Include="FastNew_Namespace" />
<CompilerVisibleProperty Include="FastNew_ForceFastNewDelegate" />
<CompilerVisibleProperty Include="FastNew_AlertGeneratedFile" />
<CompilerVisibleProperty Include="FastNew_DisableGeneratorCache" />
<CompilerVisibleProperty Include="FastNew_PrettyOutput" />
<CompilerVisibleProperty Include="FastNew_MultiThreadedGeneration" />
<CompilerVisibleProperty Include="FastNew_OutputGenerationInfo" />
<CompilerVisibleProperty Include="FastNew_AllowUnsafeImplementation" />
<CompilerVisibleProperty Include="FastNew_PublicFastNew" />
Expand Down
83 changes: 21 additions & 62 deletions src/FastGenericNew.SourceGenerator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,89 +3,48 @@
[Generator(LanguageNames.CSharp)]
public unsafe class Generator : IIncrementalGenerator
{
private static GeneratorOptions _lastOptions;

private static readonly nint[] generatorPointers = Assembly
private static readonly CodeGenerator[] generators = Assembly
.GetCallingAssembly()
.GetTypes()
.Where(static x => !x.IsAbstract && typeof(CodeGenerator).IsAssignableFrom(x))
.Select(static x =>
(nint)
typeof(GeneratorInstance<>)
.MakeGenericType(x)
.GetMethod(nameof(GeneratorInstance<FastNewCoreGenerator>.Generate))
.MethodHandle.GetFunctionPointer()
)
.Select(static x => (CodeGenerator)Activator.CreateInstance(x))
.ToArray();

private static readonly object _lock = new();

public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterSourceOutput(context.AnalyzerConfigOptionsProvider.WithComparer(AnalyzerConfigComparer.Instance), BuildSource);
static void BuildSource(SourceProductionContext sourceContext, AnalyzerConfigOptionsProvider optionsProvider)
foreach (var generator in generators)
{
var newOptions = new GeneratorOptions(optionsProvider);

if (newOptions.DisableGeneratorCache)
{
_lastOptions = default;
}

if (newOptions.MultiThreadedGeneration)
{
Parallel.ForEach(generatorPointers, nativePointer =>
{
var function = (delegate* managed<in GeneratorOptions, in GeneratorOptions, CodeGenerationResult>)nativePointer;
var result = function(in _lastOptions, in newOptions);
if (result.SourceText != null)
{
lock (_lock)
{
sourceContext.AddSource(result.Filename, result.SourceText);
}
}
if (result.Diagnostics != null)
{
lock (_lock)
{
foreach (Diagnostic diag in result.Diagnostics)
{
sourceContext.ReportDiagnostic(diag);
}
}
}
});
}
else
var comparer = new AnalyzerConfigComparer(generator);
context.RegisterSourceOutput(context.AnalyzerConfigOptionsProvider.WithComparer(comparer), (SourceProductionContext sourceContext, AnalyzerConfigOptionsProvider optionsProvider) =>
{
foreach (var nativePointer in generatorPointers)
var options = new GeneratorOptions(optionsProvider);
var result = generator.Generate(in options);
if (result.SourceText != null)
sourceContext.AddSource(result.Filename, result.SourceText);
if (result.Diagnostics != null)
{
var function = (delegate* managed<in GeneratorOptions, in GeneratorOptions, CodeGenerationResult>)nativePointer;
var result = function(in _lastOptions, in newOptions);
if (result.SourceText != null)
sourceContext.AddSource(result.Filename, result.SourceText);
if (result.Diagnostics != null)
foreach (Diagnostic diag in result.Diagnostics)
{
foreach (Diagnostic diag in result.Diagnostics)
{
sourceContext.ReportDiagnostic(diag);
}
sourceContext.ReportDiagnostic(diag);
}
}
}
_lastOptions = newOptions;
});
}
}

class AnalyzerConfigComparer : IEqualityComparer<AnalyzerConfigOptionsProvider>
{
public static readonly AnalyzerConfigComparer Instance = new();
private readonly CodeGenerator generator;

public AnalyzerConfigComparer(CodeGenerator generator)
{
this.generator = generator;
}

public bool Equals(AnalyzerConfigOptionsProvider x, AnalyzerConfigOptionsProvider y) =>
new GeneratorOptions(x).Equals(new GeneratorOptions(y));
generator.GetOptionsSubset(new GeneratorOptions(x)).Equals(generator.GetOptionsSubset(new GeneratorOptions(y)));

public int GetHashCode(AnalyzerConfigOptionsProvider obj) =>
new GeneratorOptions(obj).GetHashCode();
generator.GetOptionsSubset(new GeneratorOptions(obj)).GetHashCode();
}
}
32 changes: 13 additions & 19 deletions src/FastGenericNew.SourceGenerator/GeneratorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,43 @@
public readonly partial record struct GeneratorOptions
{
[GeneratorOption(16)]
public int MaxParameterCount { get; }
public int MaxParameterCount { get; init; }

[GeneratorOption(false)]
public bool PublicFastNewCore { get; }
public bool PublicFastNewCore { get; init; }

[GeneratorOption(true)]
public bool GenerateTryCreateInstance { get; }
public bool GenerateTryCreateInstance { get; init; }

[GeneratorOption(true)]
public bool GenerateCreateInstance { get; }
public bool GenerateCreateInstance { get; init; }

[GeneratorOption(true)]
public bool GenerateTypeCreateInstance { get; }
public bool GenerateTypeCreateInstance { get; init; }

[GeneratorOption(true)]
public bool NonPublicConstructorSupport { get; }
public bool NonPublicConstructorSupport { get; init; }

[GeneratorOption("FastGenericNew")]
public string Namespace { get; }
public string Namespace { get; init; }

[GeneratorOption(false)]
public bool ForceFastNewDelegate { get; }
public bool ForceFastNewDelegate { get; init; }

[GeneratorOption(true)]
public bool AlertGeneratedFile { get; }

[GeneratorOption(true)]
public bool DisableGeneratorCache { get; }
public bool AlertGeneratedFile { get; init; }

[GeneratorOption(false)]
public bool PrettyOutput { get; }

[GeneratorOption(true)]
public bool MultiThreadedGeneration { get; }
public bool PrettyOutput { get; init; }

[GeneratorOption(false)]
public bool OutputGenerationInfo { get; }
public bool OutputGenerationInfo { get; init; }

[GeneratorOption(false, PresentPreProcessor = true)]
public bool AllowUnsafeImplementation { get; }
public bool AllowUnsafeImplementation { get; init; }

[GeneratorOption(false)]
public bool PublicFastNew { get; }
public bool PublicFastNew { get; init; }

// ctor will be generated by InternalGenerator
//public GeneratorOptions(AnalyzerConfigOptionsProvider? provider)
Expand Down
Loading

0 comments on commit 775b0bd

Please sign in to comment.