-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 实体序列化支持
- Loading branch information
Showing
14 changed files
with
403 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
namespace ET.Analyzer | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public class EntityHashCodeAnalyzer : DiagnosticAnalyzer | ||
{ | ||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(EntityHashCodeAnalyzerRule.Rule); | ||
public override void Initialize(AnalysisContext context) | ||
{ | ||
if (!AnalyzerGlobalSetting.EnableAnalyzer) | ||
{ | ||
return; | ||
} | ||
|
||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
context.EnableConcurrentExecution(); | ||
context.RegisterCompilationStartAction((this.CompilationStartAnalysis)); | ||
} | ||
|
||
private void CompilationStartAnalysis(CompilationStartAnalysisContext context) | ||
{ | ||
var entityHashCodeMap = new ConcurrentDictionary<long, string>(); | ||
context.RegisterSemanticModelAction((analysisContext => | ||
{ | ||
if (AnalyzerHelper.IsSemanticModelNeedAnalyze(analysisContext.SemanticModel,UnityCodesPath.UnityModel)) | ||
{ | ||
AnalyzeSemanticModel(analysisContext, entityHashCodeMap); | ||
} | ||
} )); | ||
} | ||
|
||
private void AnalyzeSemanticModel(SemanticModelAnalysisContext analysisContext, ConcurrentDictionary<long, string> entityHashCodeMap) | ||
{ | ||
foreach (var classDeclarationSyntax in analysisContext.SemanticModel.SyntaxTree.GetRoot().DescendantNodes<ClassDeclarationSyntax>()) | ||
{ | ||
var classTypeSymbol = analysisContext.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax); | ||
if (classTypeSymbol!=null) | ||
{ | ||
AnalyzeTypeSymbol(analysisContext, classTypeSymbol,entityHashCodeMap); | ||
} | ||
} | ||
} | ||
|
||
private void AnalyzeTypeSymbol(SemanticModelAnalysisContext context, INamedTypeSymbol namedTypeSymbol,ConcurrentDictionary<long, string> entityHashCodeMap) | ||
{ | ||
var baseType = namedTypeSymbol.BaseType?.ToString(); | ||
|
||
// 筛选出实体类 | ||
if (baseType!= Definition.EntityType && baseType != Definition.LSEntityType) | ||
{ | ||
return; | ||
} | ||
|
||
var entityName = namedTypeSymbol.ToString(); | ||
var hashCode = entityName.GetLongHashCode(); | ||
|
||
if (entityHashCodeMap.TryGetValue(hashCode, out var existEntityName)) | ||
{ | ||
if (existEntityName == entityName) | ||
{ | ||
return; | ||
} | ||
var classDeclarationSyntax = namedTypeSymbol.DeclaringSyntaxReferences.First().GetSyntax() as ClassDeclarationSyntax; | ||
Diagnostic diagnostic = Diagnostic.Create(EntityHashCodeAnalyzerRule.Rule, classDeclarationSyntax?.Identifier.GetLocation(), entityName,existEntityName,hashCode.ToString()); | ||
context.ReportDiagnostic(diagnostic); | ||
} | ||
else | ||
{ | ||
entityHashCodeMap[hashCode] = entityName; | ||
} | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
208 changes: 208 additions & 0 deletions
208
Share/Share.SourceGenerator/Generator/ETEntitySerializeFormatterGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using ET.Analyzer; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace ET.Generator; | ||
|
||
[Generator(LanguageNames.CSharp)] | ||
public class ETEntitySerializeFormatterGenerator : ISourceGenerator | ||
{ | ||
public void Initialize(GeneratorInitializationContext context) | ||
{ | ||
context.RegisterForSyntaxNotifications((() => ETEntitySerializeFormatterSyntaxContextReceiver.Create())); | ||
} | ||
|
||
public void Execute(GeneratorExecutionContext context) | ||
{ | ||
|
||
if (context.SyntaxContextReceiver is not ETEntitySerializeFormatterSyntaxContextReceiver receiver || receiver.entities.Count==0) | ||
{ | ||
return; | ||
} | ||
|
||
int count = receiver.entities.Count; | ||
string typeHashCodeMapDeclaration = GenerateTypeHashCodeMapDeclaration(receiver); | ||
string serializeContent = GenerateSerializeContent(receiver); | ||
string deserializeContent = GenerateDeserializeContent(receiver); | ||
string genericTypeParam = context.Compilation.AssemblyName == AnalyzeAssembly.DotNetModel? "<TBufferWriter>" : ""; | ||
string scopedCode = context.Compilation.AssemblyName == AnalyzeAssembly.DotNetModel? "scoped" : ""; | ||
string code = $$""" | ||
#nullable enable | ||
#pragma warning disable CS0108 // hides inherited member | ||
#pragma warning disable CS0162 // Unreachable code | ||
#pragma warning disable CS0164 // This label has not been referenced | ||
#pragma warning disable CS0219 // Variable assigned but never used | ||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. | ||
#pragma warning disable CS8601 // Possible null reference assignment | ||
#pragma warning disable CS8602 | ||
#pragma warning disable CS8604 // Possible null reference argument for parameter | ||
#pragma warning disable CS8619 | ||
#pragma warning disable CS8620 | ||
#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method | ||
#pragma warning disable CS8765 // Nullability of type of parameter | ||
#pragma warning disable CS9074 // The 'scoped' modifier of parameter doesn't match overridden or implemented member | ||
#pragma warning disable CA1050 // Declare types in namespaces. | ||
|
||
using System; | ||
using MemoryPack; | ||
|
||
[global::MemoryPack.Internal.Preserve] | ||
public class ETEntitySerializeFormatter : MemoryPackFormatter<global::{{Definition.EntityType}}> | ||
{ | ||
static readonly System.Collections.Generic.Dictionary<Type, long> __typeToTag = new({{count}}) | ||
{ | ||
{{typeHashCodeMapDeclaration}} | ||
}; | ||
|
||
[global::MemoryPack.Internal.Preserve] | ||
public override void Serialize{{genericTypeParam}}(ref MemoryPackWriter{{genericTypeParam}} writer,{{scopedCode}} ref global::{{Definition.EntityType}}? value) | ||
{ | ||
|
||
if (value == null) | ||
{ | ||
writer.WriteNullUnionHeader(); | ||
return; | ||
} | ||
|
||
if (__typeToTag.TryGetValue(value.GetType(), out var tag)) | ||
{ | ||
writer.WriteValue<byte>(global::MemoryPack.MemoryPackCode.WideTag); | ||
writer.WriteValue<long>(tag); | ||
switch (tag) | ||
{ | ||
{{serializeContent}} | ||
default: | ||
break; | ||
} | ||
} | ||
else | ||
{ | ||
MemoryPackSerializationException.ThrowNotFoundInUnionType(value.GetType(), typeof(global::{{Definition.EntityType}})); | ||
} | ||
} | ||
|
||
[global::MemoryPack.Internal.Preserve] | ||
public override void Deserialize(ref MemoryPackReader reader,{{scopedCode}} ref global::{{Definition.EntityType}}? value) | ||
{ | ||
|
||
bool isNull = reader.ReadValue<byte>() == global::MemoryPack.MemoryPackCode.NullObject; | ||
if (isNull) | ||
{ | ||
value = default; | ||
return; | ||
} | ||
|
||
var tag = reader.ReadValue<long>(); | ||
|
||
switch (tag) | ||
{ | ||
{{deserializeContent}} | ||
default: | ||
//MemoryPackSerializationException.ThrowInvalidTag(tag, typeof(global::IForExternalUnion)); | ||
break; | ||
} | ||
} | ||
} | ||
namespace ET | ||
{ | ||
public static partial class EntitySerializeRegister | ||
{ | ||
static partial void Register() | ||
{ | ||
if (!global::MemoryPack.MemoryPackFormatterProvider.IsRegistered<global::{{Definition.EntityType}}>()) | ||
{ | ||
global::MemoryPack.MemoryPackFormatterProvider.Register(new ETEntitySerializeFormatter()); | ||
} | ||
} | ||
} | ||
} | ||
"""; | ||
context.AddSource($"ETEntitySerializeFormatterGenerator.g.cs",code); | ||
} | ||
|
||
private string GenerateTypeHashCodeMapDeclaration(ETEntitySerializeFormatterSyntaxContextReceiver receiver) | ||
{ | ||
StringBuilder sb = new StringBuilder(); | ||
foreach (var entityName in receiver.entities) | ||
{ | ||
sb.AppendLine($$""" { typeof(global::{{entityName}}), {{entityName.GetLongHashCode()}} },"""); | ||
} | ||
return sb.ToString(); | ||
} | ||
|
||
private string GenerateSerializeContent(ETEntitySerializeFormatterSyntaxContextReceiver receiver) | ||
{ | ||
StringBuilder sb = new StringBuilder(); | ||
foreach (var entityName in receiver.entities) | ||
{ | ||
sb.AppendLine($$""" case {{entityName.GetLongHashCode()}}: writer.WritePackable(System.Runtime.CompilerServices.Unsafe.As<global::{{Definition.EntityType}}?, global::{{entityName}}>(ref value)); break;"""); | ||
} | ||
return sb.ToString(); | ||
} | ||
|
||
private string GenerateDeserializeContent(ETEntitySerializeFormatterSyntaxContextReceiver receiver) | ||
{ | ||
StringBuilder sb = new StringBuilder(); | ||
foreach (var entityName in receiver.entities) | ||
{ | ||
sb.AppendLine($$""" | ||
case {{entityName.GetLongHashCode()}}: | ||
if(value is global::{{entityName}}) | ||
{ | ||
reader.ReadPackable(ref System.Runtime.CompilerServices.Unsafe.As<global::{{Definition.EntityType}}?, global::{{entityName}}>(ref value)); | ||
}else{ | ||
value = (global::{{entityName}})reader.ReadPackable<global::{{entityName}}>(); | ||
} | ||
break; | ||
"""); | ||
} | ||
return sb.ToString(); | ||
} | ||
|
||
class ETEntitySerializeFormatterSyntaxContextReceiver : ISyntaxContextReceiver | ||
{ | ||
internal static ISyntaxContextReceiver Create() | ||
{ | ||
return new ETEntitySerializeFormatterSyntaxContextReceiver(); | ||
} | ||
|
||
public HashSet<string> entities = new HashSet<string>(); | ||
|
||
public void OnVisitSyntaxNode(GeneratorSyntaxContext context) | ||
{ | ||
if (!AnalyzerHelper.IsSemanticModelNeedAnalyze(context.SemanticModel, UnityCodesPath.UnityModel)) | ||
{ | ||
return; | ||
} | ||
|
||
if (context.Node is not ClassDeclarationSyntax classDeclarationSyntax) | ||
{ | ||
return; | ||
} | ||
|
||
var classTypeSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax); | ||
if (classTypeSymbol==null) | ||
{ | ||
return; | ||
} | ||
|
||
var baseType = classTypeSymbol.BaseType?.ToString(); | ||
|
||
// 筛选出实体类 | ||
if (baseType!= Definition.EntityType && baseType != Definition.LSEntityType) | ||
{ | ||
return; | ||
} | ||
|
||
if (!classTypeSymbol.HasAttribute("MemoryPack.MemoryPackableAttribute")) | ||
{ | ||
return; | ||
} | ||
|
||
entities.Add(classTypeSymbol.ToString()); | ||
} | ||
} | ||
} |
Oops, something went wrong.