using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
namespace NewLife.SourceGenerator;
/// <summary>Json 成员访问生成器。扫描 <c>[JsonSerializable(typeof(T))]</c> 标记,为 T 的公开实例属性与字段生成静态读写委托注册,JsonHelper 序列化时优先使用,避免反射与表达式编译</summary>
/// <remarks>
/// 依赖 System.Text.Json 的 <c>JsonSerializableAttribute</c>(net5+ 共享框架自带,无需额外包)。
/// 只生成可直接静态访问的成员(public、非只读/init-only 赋值、非元组/指针/ref struct、类型可访问);
/// 未标记类型与不可生成成员保持原反射路径,行为不变。
/// </remarks>
[Generator(LanguageNames.CSharp)]
public sealed class JsonSerializableGenerator : IIncrementalGenerator
{
#region 生成入口
/// <summary>初始化增量生成管线</summary>
/// <param name="context">生成上下文</param>
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var lines = context.SyntaxProvider
.ForAttributeWithMetadataName(
"System.Text.Json.Serialization.JsonSerializableAttribute",
predicate: static (node, token) => true,
transform: static (ctx, token) => Extract(ctx, token))
.SelectMany(static (items, token) => items)
.Where(static x => x != null);
var combined = context.CompilationProvider.Combine(lines.Collect());
context.RegisterSourceOutput(combined, static (spc, pair) => Emit(spc, pair.Left, pair.Right));
}
#endregion
#region 提取
// 提取标记类型的所有可生成成员注册行(值语义字符串,增量缓存友好)
private static ImmutableArray<String?> Extract(GeneratorAttributeSyntaxContext context, CancellationToken token)
{
var builder = ImmutableArray.CreateBuilder<String?>();
var compilation = context.SemanticModel.Compilation;
foreach (var attribute in context.Attributes)
{
if (attribute.ConstructorArguments.Length == 0) continue;
if (attribute.ConstructorArguments[0].Value is not INamedTypeSymbol type) continue;
MemberLineBuilder.AddMembers(builder, type, compilation);
}
return builder.ToImmutable();
}
#endregion
#region 产出
// 输出生成源码。仅 .NET 5+(含模块初始化器)且在可用语言版本下生成
private static void Emit(SourceProductionContext context, Compilation compilation, ImmutableArray<String?> items)
{
if (items.IsDefaultOrEmpty) return;
var set = new SortedSet<String>(StringComparer.Ordinal);
foreach (var item in items)
{
if (!String.IsNullOrEmpty(item)) set.Add(item!);
}
if (set.Count == 0) return;
var tree = compilation.SyntaxTrees.FirstOrDefault();
if (tree?.Options is not CSharpParseOptions options) return;
if (!options.PreprocessorSymbolNames.Contains("NET5_0_OR_GREATER")) return;
var builder = new StringBuilder();
builder.AppendLine("// <auto-generated/>");
if ((Int32)options.LanguageVersion >= (Int32)LanguageVersion.CSharp8) builder.AppendLine("#nullable enable");
builder.AppendLine();
builder.AppendLine("namespace NewLife.SourceGenerator.Generated");
builder.AppendLine("{");
builder.AppendLine(" /// <summary>源生成的 Json 成员访问注册。NewLife.Core 源生成器自动生成,请勿修改</summary>");
builder.AppendLine(" internal static class JsonMemberRegistrar");
builder.AppendLine(" {");
builder.AppendLine(" [global::System.Runtime.CompilerServices.ModuleInitializer]");
builder.AppendLine(" internal static void Initialize()");
builder.AppendLine(" {");
foreach (var line in set)
{
builder.Append(" ").AppendLine(line);
}
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine("}");
context.AddSource("JsonMemberRegistrar.g.cs", SourceText.From(builder.ToString(), Encoding.UTF8));
}
#endregion
}
|