80 lines
4.2 KiB
C#
80 lines
4.2 KiB
C#
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Microsoft.CodeAnalysis.Text;
|
|
|
|
namespace AyCode.Core.Serializers.SourceGenerator;
|
|
|
|
/// <summary>
|
|
/// Incremental source generator for <c>[AcBinarySerializable]</c> types. Emits an
|
|
/// <c>IGeneratedBinaryWriter</c> + <c>IGeneratedBinaryReader</c> implementation for every annotated
|
|
/// type, plus a <c>[ModuleInitializer]</c>-based registry hook that wires the generated instances
|
|
/// into the runtime serializer at startup.
|
|
///
|
|
/// <para><b>Source organization</b> — this generator class is split across multiple partial files
|
|
/// for navigational clarity:</para>
|
|
/// <list type="bullet">
|
|
/// <item><c>AcBinarySourceGenerator.cs</c> (this file) — entry point: <c>[Generator]</c> attribute,
|
|
/// <c>Initialize</c> + <c>Execute</c> orchestration.</item>
|
|
/// <item><c>AcBinarySourceGenerator.Models.cs</c> — non-partial model types
|
|
/// (<c>SerializableClassInfo</c>, <c>PropInfo</c>, <c>PropertyTypeKind</c>).</item>
|
|
/// <item><c>AcBinarySourceGenerator.TypeAnalysis.cs</c> — Roslyn-symbol utility passes
|
|
/// (kind detection, FNV hashing, name flattening, scan-need recursion).</item>
|
|
/// <item><c>AcBinarySourceGenerator.Diagnostics.cs</c> — ACBIN001 (cycle warning) + ACBIN002
|
|
/// (polymorph misuse error) descriptors and detection methods.</item>
|
|
/// <item><c>AcBinarySourceGenerator.GetClassInfo.cs</c> — class-info extraction pass (attribute
|
|
/// flags + property metadata building).</item>
|
|
/// <item><c>AcBinarySourceGenerator.GenWriter.cs</c> — writer-side emit (WriteProperties, ScanObject,
|
|
/// ScanForDuplicates, per-property emit helpers).</item>
|
|
/// <item><c>AcBinarySourceGenerator.GenReader.cs</c> — reader-side emit (ReadProperties, ReadObject,
|
|
/// per-property read helpers).</item>
|
|
/// <item><c>AcBinarySourceGenerator.GenInit.cs</c> — ModuleInitializer-based registry hook emit.</item>
|
|
/// </list>
|
|
/// </summary>
|
|
[Generator]
|
|
public partial class AcBinarySourceGenerator : IIncrementalGenerator
|
|
{
|
|
private const string AttributeName = "AyCode.Core.Serializers.Attributes.AcBinarySerializableAttribute";
|
|
|
|
// Feature gates on the SGen-emitted writer / scan code are driven by `[AcBinarySerializable]`
|
|
// attribute flags. Two such gates are wired through SerializableClassInfo:
|
|
// • EnablePropertyFilter → omits the per-property `HasPropertyFilter` branch when false.
|
|
// • EnablePolymorphDetect → omits the `ObjectWithTypeName + AQN` prefix on `System.Object`-
|
|
// declared properties when false (then ACBIN002 guards misuse).
|
|
// • EnableInternString → omits StringInterned* case-emit in the reader switch when false.
|
|
// All default `true`; opt-out is per type via the attribute ctor parameters.
|
|
|
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
|
{
|
|
var classDeclarations = context.SyntaxProvider
|
|
.ForAttributeWithMetadataName(
|
|
AttributeName,
|
|
predicate: static (node, _) => node is ClassDeclarationSyntax || node is StructDeclarationSyntax,
|
|
transform: static (ctx, _) => GetClassInfo(ctx))
|
|
.Where(static info => info != null);
|
|
|
|
context.RegisterSourceOutput(classDeclarations.Collect(),
|
|
static (spc, classes) => Execute(classes!, spc));
|
|
}
|
|
|
|
private static void Execute(ImmutableArray<SerializableClassInfo?> classes, SourceProductionContext context)
|
|
{
|
|
if (classes.IsDefaultOrEmpty) return;
|
|
var valid = classes.Where(c => c != null).Cast<SerializableClassInfo>().ToList();
|
|
if (valid.Count == 0) return;
|
|
|
|
DetectAndReportCycles(valid, context);
|
|
DetectAndReportPolymorphicMisuse(valid, context);
|
|
|
|
foreach (var ci in valid)
|
|
{
|
|
context.AddSource($"{ci.ClassName}_GeneratedWriter.g.cs", SourceText.From(GenWriter(ci), Encoding.UTF8));
|
|
context.AddSource($"{ci.ClassName}_GeneratedReader.g.cs", SourceText.From(GenReader(ci), Encoding.UTF8));
|
|
}
|
|
|
|
context.AddSource("AcBinaryGeneratedWriters_Init.g.cs", SourceText.From(GenInit(valid), Encoding.UTF8));
|
|
}
|
|
}
|