Rework metadata struct loading to use new struct versioning

This commit is contained in:
LukeFZ
2024-08-17 13:52:09 +02:00
parent 6c59434984
commit 43d7433e12
69 changed files with 496 additions and 382 deletions

View File

@@ -1,4 +1,5 @@
using VersionedSerialization.Generator.Utils;
using Microsoft.CodeAnalysis.CSharp;
using VersionedSerialization.Generator.Utils;
namespace VersionedSerialization.Generator.Models;
@@ -6,8 +7,7 @@ public sealed record ObjectSerializationInfo(
string Namespace,
string Name,
bool HasBaseType,
bool IsStruct,
bool ShouldGenerateSizeMethod,
SyntaxKind DefinitionType,
bool CanGenerateSizeMethod,
ImmutableEquatableArray<PropertySerializationInfo> Properties
);

View File

@@ -6,6 +6,6 @@ public sealed record PropertySerializationInfo(
string Name,
string ReadMethod,
string SizeExpression,
int Alignment,
PropertyType Type,
ImmutableEquatableArray<VersionCondition> VersionConditions
);

View File

@@ -16,6 +16,9 @@ public enum PropertyType
Int32,
Int64,
String,
Custom,
NativeInteger,
UNativeInteger,
}
public static class PropertyTypeExtensions
@@ -35,6 +38,9 @@ public static class PropertyTypeExtensions
PropertyType.Int32 => nameof(PropertyType.Int32),
PropertyType.Int64 => nameof(PropertyType.Int64),
PropertyType.String => nameof(String),
PropertyType.Custom => "",
PropertyType.NativeInteger => "NInt",
PropertyType.UNativeInteger => "NUInt",
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
@@ -43,6 +49,40 @@ public static class PropertyTypeExtensions
{
PropertyType.Boolean => true,
PropertyType.String => true,
PropertyType.Custom => true,
PropertyType.NativeInteger => true,
PropertyType.UNativeInteger => true,
_ => false
};
public static int GetTypeSize(this PropertyType type)
=> type switch
{
PropertyType.Unsupported => -1,
PropertyType.None => 0,
PropertyType.UInt8 => 1,
PropertyType.Int8 => 1,
PropertyType.Boolean => 1,
PropertyType.UInt16 => 2,
PropertyType.UInt32 => 4,
PropertyType.UInt64 => 8,
PropertyType.Int16 => 2,
PropertyType.Int32 => 4,
PropertyType.Int64 => 8,
PropertyType.String => -1,
PropertyType.Custom => -1,
PropertyType.NativeInteger => -1,
PropertyType.UNativeInteger => -1,
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
public static bool IsUnsignedType(this PropertyType type)
=> type switch
{
PropertyType.UInt8
or PropertyType.UInt16
or PropertyType.UInt32
or PropertyType.UInt64 => true,
_ => false
};
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
@@ -20,7 +19,7 @@ namespace VersionedSerialization.Generator
var valueProvider = context.SyntaxProvider
.ForAttributeWithMetadataName(Constants.VersionedStructAttribute,
static (node, _) => node is ClassDeclarationSyntax or StructDeclarationSyntax,
static (node, _) => node is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax,
static (context, _) => (ContextClass: (TypeDeclarationSyntax)context.TargetNode, context.SemanticModel))
.Combine(context.CompilationProvider)
.Select(static (tuple, cancellationToken) => ParseSerializationInfo(tuple.Left.ContextClass, tuple.Left.SemanticModel, tuple.Right, cancellationToken))
@@ -63,7 +62,16 @@ namespace VersionedSerialization.Generator
generator.LeaveScope();
}
generator.EnterScope($"public partial {(info.IsStruct ? "struct" : "class")} {info.Name} : IReadable");
var definitionType = info.DefinitionType switch
{
SyntaxKind.ClassDeclaration => "class",
SyntaxKind.StructDeclaration => "struct",
SyntaxKind.RecordDeclaration => "record",
SyntaxKind.RecordStructDeclaration => "record struct",
_ => throw new IndexOutOfRangeException()
};
generator.EnterScope($"public partial {definitionType} {info.Name} : IReadable");
GenerateReadMethod(generator, info);
generator.AppendLine();
GenerateSizeMethod(generator, info);
@@ -92,12 +100,7 @@ namespace VersionedSerialization.Generator
GenerateVersionCondition(property.VersionConditions, generator);
generator.EnterScope();
generator.AppendLine($"size += {property.SizeExpression};");
if (property.Alignment != 0)
generator.AppendLine($"size += size % {property.Alignment} == 0 ? 0 : {property.Alignment} - (size % {property.Alignment});");
generator.LeaveScope();
}
@@ -121,10 +124,6 @@ namespace VersionedSerialization.Generator
generator.EnterScope();
generator.AppendLine($"this.{property.Name} = {property.ReadMethod}");
if (property.Alignment != 0)
generator.AppendLine($"reader.Align({property.Alignment});");
generator.LeaveScope();
}
@@ -176,9 +175,10 @@ namespace VersionedSerialization.Generator
{
var classSymbol = model.GetDeclaredSymbol(contextClass, cancellationToken) ?? throw new InvalidOperationException();
var alignedAttribute = compilation.GetTypeByMetadataName(Constants.AlignedAttribute);
//var versionedStructAttribute = compilation.GetTypeByMetadataName(Constants.VersionedStructAttribute);
var versionConditionAttribute = compilation.GetTypeByMetadataName(Constants.VersionConditionAttribute);
var customSerializationAttribute = compilation.GetTypeByMetadataName(Constants.CustomSerializationAttribute);
var nativeIntegerAttribute = compilation.GetTypeByMetadataName(Constants.NativeIntegerAttribute);
var canGenerateSizeMethod = true;
@@ -190,7 +190,6 @@ namespace VersionedSerialization.Generator
|| member is IPropertySymbol { SetMethod: null })
continue;
var alignment = 0;
var versionConditions = new List<VersionCondition>();
ITypeSymbol type;
@@ -207,41 +206,12 @@ namespace VersionedSerialization.Generator
}
var typeInfo = ParseType(type);
canGenerateSizeMethod &= typeInfo.Type != PropertyType.String;
string readMethod;
if (typeInfo.Type == PropertyType.None)
{
readMethod = $"reader.ReadVersionedObject<{typeInfo.ComplexTypeName}>(in version);";
}
else
{
readMethod = typeInfo.Type.IsSeperateMethod()
? $"reader.Read{typeInfo.Type.GetTypeName()}();"
: $"reader.ReadPrimitive<{typeInfo.Type.GetTypeName()}>();";
if (typeInfo.ComplexTypeName != "")
readMethod = $"({typeInfo.ComplexTypeName}){readMethod}";
}
string sizeExpression;
if (typeInfo.Type == PropertyType.None)
{
sizeExpression = $"{typeInfo.ComplexTypeName}.Size(in version, is32Bit)";
}
else
{
sizeExpression = $"sizeof({typeInfo.Type.GetTypeName()})";
}
string? readMethod = null;
string? sizeExpression = null;
foreach (var attribute in member.GetAttributes())
{
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, alignedAttribute))
{
alignment = (int)attribute.ConstructorArguments[0].Value!;
}
else if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, versionConditionAttribute))
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, versionConditionAttribute))
{
StructVersion? lessThan = null,
moreThan = null,
@@ -278,16 +248,54 @@ namespace VersionedSerialization.Generator
}
else if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, customSerializationAttribute))
{
typeInfo = (PropertyType.Custom, "", typeInfo.IsArray);
readMethod = (string)attribute.ConstructorArguments[0].Value!;
sizeExpression = (string)attribute.ConstructorArguments[1].Value!;
}
else if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, nativeIntegerAttribute))
{
typeInfo = (typeInfo.Type.IsUnsignedType()
? PropertyType.UNativeInteger
: PropertyType.NativeInteger,
typeInfo.ComplexTypeName == ""
? typeInfo.Type.GetTypeName()
: typeInfo.ComplexTypeName,
typeInfo.IsArray);
}
}
canGenerateSizeMethod &= typeInfo.Type != PropertyType.String;
if (readMethod == null)
{
if (typeInfo.Type == PropertyType.None)
{
readMethod = $"reader.ReadVersionedObject<{typeInfo.ComplexTypeName}>(in version);";
}
else
{
readMethod = typeInfo.Type.IsSeperateMethod()
? $"reader.Read{typeInfo.Type.GetTypeName()}();"
: $"reader.ReadPrimitive<{typeInfo.Type.GetTypeName()}>();";
if (typeInfo.ComplexTypeName != "")
readMethod = $"({typeInfo.ComplexTypeName}){readMethod}";
}
}
sizeExpression ??= typeInfo.Type switch
{
PropertyType.None => $"{typeInfo.ComplexTypeName}.Size(in version, is32Bit)",
PropertyType.NativeInteger or PropertyType.UNativeInteger =>
"is32Bit ? sizeof(uint) : sizeof(ulong)",
_ => $"sizeof({typeInfo.Type.GetTypeName()})"
};
properties.Add(new PropertySerializationInfo(
member.Name,
readMethod,
sizeExpression,
alignment,
typeInfo.Type,
versionConditions.ToImmutableEquatableArray()
));
}
@@ -307,8 +315,7 @@ namespace VersionedSerialization.Generator
classSymbol.ContainingNamespace.ToDisplayString(),
classSymbol.Name,
hasBaseType,
contextClass.Kind() == SyntaxKind.StructDeclaration,
true,
contextClass.Kind(),
canGenerateSizeMethod,
properties.ToImmutableEquatableArray()
);

View File

@@ -5,9 +5,9 @@ public static class Constants
private const string AttributeNamespace = "VersionedSerialization.Attributes";
public const string VersionedStructAttribute = $"{AttributeNamespace}.{nameof(VersionedStructAttribute)}";
public const string AlignedAttribute = $"{AttributeNamespace}.{nameof(AlignedAttribute)}";
public const string VersionConditionAttribute = $"{AttributeNamespace}.{nameof(VersionConditionAttribute)}";
public const string CustomSerializationAttribute = $"{AttributeNamespace}.{nameof(CustomSerializationAttribute)}";
public const string NativeIntegerAttribute = $"{AttributeNamespace}.{nameof(NativeIntegerAttribute)}";
public const string LessThan = nameof(LessThan);
public const string GreaterThan = nameof(GreaterThan);