Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Lagrange.Proto.Generator/Entity/PolymorphicTypeInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.CodeAnalysis;

namespace Lagrange.Proto.Generator.Entity;

public class PolymorphicTypeInfo
{
public INamedTypeSymbol PolymorphicKeyType { get; internal set; } = null!;

public uint PolymorphicIndicateIndex { get; internal set; } = 0;

public bool PolymorphicFallbackToBaseType { get; internal set; } = true;

public List<PolymorphicDerivedTypeInfo> PolymorphicTypes { get; } = [];
}

public class PolymorphicDerivedTypeInfo
{
public INamedTypeSymbol DerivedType { get; internal set; } = null!;
public TypedConstant Key { get; internal set; }
}

public class BaseTypeInfo
{
public INamedTypeSymbol BaseType { get; internal set; } = null!;
public PolymorphicTypeInfo PolymorphicInfo { get; internal set; } = new();
public bool IgnoreDefaultFields { get; internal set; }
public Dictionary<int, ProtoFieldInfo> Fields { get; } = new();
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,65 @@ private partial class Emitter
private const string EncodeStringMethodName = "EncodeString";
private const string EncodeBytesMethodName = "EncodeBytes";
private const string EncodeResolvableMethodName = "EncodeResolvable";


private void EmitSerializeMethod(SourceWriter source)
{
source.WriteLine($"public static void SerializeHandler({_fullQualifiedName} {ObjectVarName}, {ProtoWriterTypeRef} {WriterVarName})");
source.WriteLine("{");
source.Indentation++;


if (parser.BaseTypeInfo?.BaseType.GetFullName() != _fullQualifiedName && parser.BaseTypeInfo?.PolymorphicInfo.PolymorphicIndicateIndex is > 0)
{
source.WriteLine($"{parser.BaseTypeInfo.BaseType.GetFullName()}.SerializeHandler({ObjectVarName},{WriterVarName});");
}
else
{
source.WriteLine($"SerializeHandlerCore({ObjectVarName}, {WriterVarName});");
}

source.Indentation--;
source.WriteLine("}");
source.WriteLine();

source.WriteLine($"public static void SerializeHandlerCore({_fullQualifiedName} {ObjectVarName}, {ProtoWriterTypeRef} {WriterVarName})");
source.WriteLine("{");
source.Indentation++;
if (parser.PolymorphicInfo.PolymorphicIndicateIndex > 0)
{
// write indicate field first
var idx = (int)parser.PolymorphicInfo.PolymorphicIndicateIndex;
var indicatorField = parser.Fields[idx];
EmitMembers(source, idx, indicatorField);
source.WriteLine();

source.WriteLine($"switch ({ObjectVarName})");
source.WriteLine("{");
source.Indentation++;
for (var index = 0; index < parser.PolymorphicInfo.PolymorphicTypes.Count; index++)
{
var kv = parser.PolymorphicInfo.PolymorphicTypes[index];
source.WriteLine($"case {kv.DerivedType.GetFullName()} _:");
source.Indentation++;
source.WriteLine($"{kv.DerivedType.GetFullName()}.SerializeHandlerCore(({kv.DerivedType.GetFullName()}){ObjectVarName}, {WriterVarName});");
source.WriteLine("break;");
source.Indentation--;
}
source.Indentation--;
source.WriteLine("}");
source.WriteLine();
}
foreach (var kv in parser.Fields)
{
int field = kv.Key;
if (parser.PolymorphicInfo.PolymorphicIndicateIndex == field) continue; // already written
var info = kv.Value;

EmitMembers(source, field, info);
source.WriteLine();
}


source.Indentation--;
source.WriteLine("}");
}
Expand Down
133 changes: 120 additions & 13 deletions Lagrange.Proto.Generator/ProtoSourceGenerator.Emitter.TypeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Lagrange.Proto.Generator.Utility;
using Lagrange.Proto.Generator.Utility.Extension;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace Lagrange.Proto.Generator;

Expand All @@ -13,6 +14,9 @@ private partial class Emitter
private const string TypeInfoPropertyName = "TypeInfo";

private const string ProtoObjectInfoTypeRef = "global::Lagrange.Proto.Serialization.Metadata.ProtoObjectInfo<{0}>";
private const string ProtoPolymorphicObjectInfoTypeRef = "global::Lagrange.Proto.Serialization.Metadata.ProtoPolymorphicObjectInfo<{0}>";
private const string ProtoPolymorphicDerivedTypeDescriptorTypeRef = "global::Lagrange.Proto.Serialization.Metadata.ProtoPolymorphicDerivedTypeDescriptor<{0}>";
private const string ProtoPolymorphicDerivedTypeDescriptorBaseTypeRef = "global::Lagrange.Proto.Serialization.Metadata.ProtoPolymorphicDerivedTypeDescriptor";
private const string ProtoFieldInfoTypeRef = "global::Lagrange.Proto.Serialization.Metadata.ProtoFieldInfo";
private const string ProtoFieldInfoGenericTypeRef = "global::Lagrange.Proto.Serialization.Metadata.ProtoFieldInfo<{0}>";
private const string ProtoMapFieldInfoGenericTypeRef = "global::Lagrange.Proto.Serialization.Metadata.ProtoMapFieldInfo<{0}, {1}, {2}>";
Expand All @@ -37,11 +41,15 @@ private partial class Emitter

private void EmitTypeInfo(SourceWriter source)
{
source.WriteLine("#pragma warning disable CS0108");
source.WriteLine();
source.WriteLine($"public static {ProtoObjectInfoTypeRefGeneric}? {TypeInfoFieldName};");
source.WriteLine();

source.WriteLine($"public static {ProtoObjectInfoTypeRefGeneric} {TypeInfoPropertyName} => {TypeInfoFieldName} ??= GetTypeInfo();");
source.WriteLine();
source.WriteLine("#pragma warning restore CS0108");
source.WriteLine();

source.WriteLine($"private static {ProtoObjectInfoTypeRefGeneric} GetTypeInfo()");
source.WriteLine('{');
Expand All @@ -60,28 +68,20 @@ private void EmitTypeInfo(SourceWriter source)
source.WriteLine($"return new {ProtoObjectInfoTypeRefGeneric}()");
source.WriteLine('{');
source.Indentation++;

source.WriteLine($"Fields = new global::System.Collections.Generic.Dictionary<uint, {string.Format(ProtoFieldInfoTypeRef)}>()");
source.WriteLine('{');
source.Indentation++;
foreach (var kv in parser.Fields)
{
int field = kv.Key;
var info = kv.Value;

if (info.ExtraTypeInfo.Count == 0) EmitFieldInfo(source, field, info);
else EmitMapFieldInfo(source, field, info);
}
source.Indentation--;
source.WriteLine("},");
EmitFieldsInfo(source, parser.Fields);

source.WriteLine($"ObjectCreator = () => new {_fullQualifiedName}(),");
EmitPolymorphicInfo(source, parser.PolymorphicInfo, parser.BaseTypeInfo!);
source.WriteLine($"IgnoreDefaultFields = {parser.IgnoreDefaultFields.ToString().ToLower()}");

source.Indentation--;
source.WriteLine("};");
source.Indentation--;
source.WriteLine('}');

EmitPolymorphicDerivedTypeDescriptor(source, parser.PolymorphicInfo);

return;

static void EmitByTypeSymbol(SourceWriter source, ITypeSymbol typeSymbol)
Expand All @@ -106,6 +106,23 @@ static void EmitByTypeSymbol(SourceWriter source, ITypeSymbol typeSymbol)
}
}

private void EmitFieldsInfo(SourceWriter source,Dictionary<int, ProtoFieldInfo> fields )
Copy link

Copilot AI Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Missing space after comma in parameter list. Should be SourceWriter source, Dictionary<int, ProtoFieldInfo> fields.

Suggested change
private void EmitFieldsInfo(SourceWriter source,Dictionary<int, ProtoFieldInfo> fields )
private void EmitFieldsInfo(SourceWriter source, Dictionary<int, ProtoFieldInfo> fields )

Copilot uses AI. Check for mistakes.

{
source.WriteLine($"Fields = new global::System.Collections.Generic.Dictionary<uint, {string.Format(ProtoFieldInfoTypeRef)}>()");
source.WriteLine('{');
source.Indentation++;
foreach (var kv in fields)
{
int field = kv.Key;
var info = kv.Value;

if (info.ExtraTypeInfo.Count == 0) EmitFieldInfo(source, field, info);
else EmitMapFieldInfo(source, field, info);
}
source.Indentation--;
source.WriteLine("},");
}

private void EmitFieldInfo(SourceWriter source, int field, ProtoFieldInfo info)
{
int tag = field << 3 | (byte)info.WireType;
Expand Down Expand Up @@ -142,5 +159,95 @@ private void EmitMapFieldInfo(SourceWriter source, int field, ProtoFieldInfo inf
source.Indentation--;
source.WriteLine("},");
}

private void EmitPolymorphicInfo(SourceWriter source, PolymorphicTypeInfo polymorphicInfo, BaseTypeInfo baseTypeInfo)
{
if (polymorphicInfo.PolymorphicIndicateIndex == 0) return;
source.WriteLine($"PolymorphicInfo = new {string.Format(ProtoPolymorphicObjectInfoTypeRef, polymorphicInfo.PolymorphicKeyType.GetFullName())}()");
source.WriteLine('{');
source.Indentation++;

source.WriteLine($"PolymorphicIndicateIndex = {polymorphicInfo.PolymorphicIndicateIndex},");
source.WriteLine($"PolymorphicFallbackToBaseType = {polymorphicInfo.PolymorphicFallbackToBaseType.ToString().ToLower()},");
if (parser.BaseTypeInfo?.BaseType is not null && parser.BaseTypeInfo.BaseType.GetFullName() != _fullQualifiedName)
{
source.WriteLine($"RootTypeDescriptorGetter = () => new {string.Format(ProtoPolymorphicDerivedTypeDescriptorTypeRef, parser.BaseTypeInfo.BaseType.GetFullName())}()");
source.WriteLine('{');
source.Indentation++;
source.WriteLine($"FieldsGetter = () => {parser.BaseTypeInfo.BaseType.GetFullName()}.{TypeInfoPropertyName}.Fields,");
source.WriteLine($"ObjectCreator = () => new {parser.BaseTypeInfo.BaseType.GetFullName()}(),");
source.WriteLine($"IgnoreDefaultFieldsGetter = () => {parser.BaseTypeInfo.BaseType.GetFullName()}.{TypeInfoPropertyName}.IgnoreDefaultFields,");
source.WriteLine($"PolymorphicInfoGetter = () => {parser.BaseTypeInfo.BaseType.GetFullName()}.{TypeInfoPropertyName}.PolymorphicInfo,");
source.WriteLine($"CurrentType = typeof({parser.BaseTypeInfo.BaseType.GetFullName()})");
source.Indentation--;
source.WriteLine("},");
}
source.WriteLine(
$"PolymorphicDerivedTypes = new global::System.Collections.Generic.Dictionary<{polymorphicInfo.PolymorphicKeyType.GetFullName()}, {ProtoPolymorphicDerivedTypeDescriptorBaseTypeRef}>()");
source.WriteLine('{');
source.Indentation++;

foreach (var derivedTypeInfo in polymorphicInfo.PolymorphicTypes)
{
source.WriteLine($"[{derivedTypeInfo.Key.ToCSharpString()}] = new {string.Format(ProtoPolymorphicDerivedTypeDescriptorTypeRef, baseTypeInfo.BaseType.GetFullName())}()");
source.WriteLine('{');
source.Indentation++;
source.WriteLine($"CurrentType = typeof({derivedTypeInfo.DerivedType.GetFullName()}),");
source.WriteLine($"FieldsGetter = () => {derivedTypeInfo.DerivedType.GetFullName()}.{TypeInfoPropertyName}.Fields,");
source.WriteLine($"ObjectCreator = () => ({baseTypeInfo.BaseType.GetFullName()})new {derivedTypeInfo.DerivedType.GetFullName()}(),");
source.WriteLine($"IgnoreDefaultFieldsGetter = () => {derivedTypeInfo.DerivedType.GetFullName()}.{TypeInfoPropertyName}.IgnoreDefaultFields,");
source.WriteLine($"PolymorphicInfoGetter = () => {derivedTypeInfo.DerivedType.GetFullName()}.{TypeInfoPropertyName}.PolymorphicInfo");
source.Indentation--;
source.WriteLine("},");
}

source.Indentation--;
source.WriteLine("}");
source.Indentation--;
source.WriteLine("},");
}

private void EmitPolymorphicDerivedTypeDescriptor(SourceWriter source, PolymorphicTypeInfo polymorphicInfo)
{

source.WriteLine("#pragma warning disable CS0108");
source.WriteLine(
$"public static {string.Format(ProtoPolymorphicDerivedTypeDescriptorTypeRef,_fullQualifiedName)}? GetPolymorphicTypeDescriptor<TKey>(TKey discriminator)");
source.WriteLine('{');
source.Indentation++;
if (polymorphicInfo.PolymorphicIndicateIndex > 0)
{
source.WriteLine("switch (discriminator)");
source.WriteLine('{');
source.Indentation++;

foreach (var derivedTypeInfo in polymorphicInfo.PolymorphicTypes)
{
source.WriteLine($"case {derivedTypeInfo.Key.ToCSharpString()}: return new {string.Format(ProtoPolymorphicDerivedTypeDescriptorTypeRef, _fullQualifiedName)}()");
source.WriteLine('{');
source.Indentation++;
source.WriteLine($"FieldsGetter = () => {derivedTypeInfo.DerivedType.GetFullName()}.{TypeInfoPropertyName}.Fields,");
source.WriteLine($"ObjectCreator = () => ({_fullQualifiedName})new {derivedTypeInfo.DerivedType.GetFullName()}(),");
source.WriteLine($"IgnoreDefaultFieldsGetter = () => {derivedTypeInfo.DerivedType.GetFullName()}.{TypeInfoPropertyName}.IgnoreDefaultFields,");
source.WriteLine($"PolymorphicInfoGetter = () => {derivedTypeInfo.DerivedType.GetFullName()}.{TypeInfoPropertyName}.PolymorphicInfo,");
source.WriteLine($"CurrentType = typeof({derivedTypeInfo.DerivedType.GetFullName()})");
source.Indentation--;
source.WriteLine("};");
}

source.WriteLine("default: return null;");
source.Indentation--;
source.WriteLine("}");
}
else
{
source.WriteLine("return null;");
}

source.Indentation--;
source.WriteLine('}');
source.WriteLine("#pragma warning restore CS0108");
source.WriteLine();
}
}
}
Loading