mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-06 17:58:05 +02:00
Cache datadef types (#6801)
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
public sealed record DataDefinition(
|
||||
@@ -8,4 +10,45 @@ public sealed record DataDefinition(
|
||||
List<DataField> Fields,
|
||||
bool HasHooks,
|
||||
bool InvalidFields,
|
||||
bool IsRecord);
|
||||
bool IsRecord)
|
||||
{
|
||||
private readonly Dictionary<ITypeSymbol, (bool Definition, bool Record)> _classificationCache =
|
||||
new(SymbolEqualityComparer.Default);
|
||||
|
||||
private bool _firstDataDefinitionBaseTypeResolved;
|
||||
private ITypeSymbol? _firstDataDefinitionBaseType;
|
||||
|
||||
internal bool IsDataDefinition(ITypeSymbol? type, out bool isDataRecord)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
isDataRecord = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_classificationCache.TryGetValue(type, out var result))
|
||||
{
|
||||
result = (DataDefinitionHelper.IsDataDefinition(type, out var record), record);
|
||||
_classificationCache.Add(type, result);
|
||||
}
|
||||
|
||||
isDataRecord = result.Record;
|
||||
return result.Definition;
|
||||
}
|
||||
|
||||
internal ITypeSymbol? GetFirstDataDefinitionBaseType()
|
||||
{
|
||||
if (_firstDataDefinitionBaseTypeResolved)
|
||||
return _firstDataDefinitionBaseType;
|
||||
|
||||
_firstDataDefinitionBaseTypeResolved = true;
|
||||
var parent = Type;
|
||||
while ((parent = parent.BaseType) != null)
|
||||
{
|
||||
if (IsDataDefinition(parent, out _))
|
||||
return _firstDataDefinitionBaseType = parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,18 +43,20 @@ public class Generator : IIncrementalGenerator
|
||||
IncrementalValuesProvider<(string name, string code)?> dataDefinitions = initContext.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
static (node, _) => IsCandidateTypeDeclaration(node),
|
||||
static (context, _) =>
|
||||
static (context, cancellationToken) =>
|
||||
{
|
||||
var type = (TypeDeclarationSyntax)context.Node;
|
||||
var symbol = (ITypeSymbol)context.SemanticModel.GetDeclaredSymbol(type)!;
|
||||
if (context.SemanticModel.GetDeclaredSymbol(type, cancellationToken) is not INamedTypeSymbol symbol)
|
||||
return null;
|
||||
|
||||
if (symbol.TypeKind == TypeKind.Interface ||
|
||||
!IsCanonicalCandidateDeclaration(type, symbol, cancellationToken) ||
|
||||
!IsDataDefinition(symbol, out var isDataRecord))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return GenerateForDataDefinition(type, symbol, isDataRecord);
|
||||
return GenerateForDataDefinition(type, symbol, isDataRecord, cancellationToken);
|
||||
}
|
||||
)
|
||||
.Where(static type => type != null);
|
||||
@@ -84,10 +86,46 @@ public class Generator : IIncrementalGenerator
|
||||
node is TypeDeclarationSyntax { BaseList: not null } and not InterfaceDeclarationSyntax;
|
||||
}
|
||||
|
||||
private static bool IsCanonicalCandidateDeclaration(
|
||||
TypeDeclarationSyntax declaration,
|
||||
INamedTypeSymbol symbol,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
TypeDeclarationSyntax? canonical = null;
|
||||
|
||||
foreach (var reference in symbol.DeclaringSyntaxReferences)
|
||||
{
|
||||
if (reference.GetSyntax(cancellationToken) is not TypeDeclarationSyntax syntax ||
|
||||
!IsCandidateTypeDeclaration(syntax))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (canonical == null || CompareDeclarations(syntax, canonical) < 0)
|
||||
canonical = syntax;
|
||||
}
|
||||
|
||||
return canonical == declaration;
|
||||
}
|
||||
|
||||
private static int CompareDeclarations(TypeDeclarationSyntax left, TypeDeclarationSyntax right)
|
||||
{
|
||||
var pathComparison = string.Compare(
|
||||
left.SyntaxTree.FilePath,
|
||||
right.SyntaxTree.FilePath,
|
||||
StringComparison.Ordinal);
|
||||
|
||||
if (pathComparison != 0)
|
||||
return pathComparison;
|
||||
|
||||
return left.SpanStart.CompareTo(right.SpanStart);
|
||||
}
|
||||
|
||||
private static (string, string)? GenerateForDataDefinition(
|
||||
TypeDeclarationSyntax declaration,
|
||||
ITypeSymbol type,
|
||||
bool isDataRecord)
|
||||
INamedTypeSymbol type,
|
||||
bool isDataRecord,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var containingTypes = new Stack<INamedTypeSymbol>();
|
||||
@@ -115,7 +153,7 @@ public class Generator : IIncrementalGenerator
|
||||
var containingTypesEnd = new StringBuilder();
|
||||
foreach (var parent in containingTypes)
|
||||
{
|
||||
var syntax = (TypeDeclarationSyntax)parent.DeclaringSyntaxReferences[0].GetSyntax();
|
||||
var syntax = (TypeDeclarationSyntax)parent.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken);
|
||||
if (!IsPartial(syntax))
|
||||
{
|
||||
nonPartial = true;
|
||||
@@ -339,11 +377,11 @@ public class Generator : IIncrementalGenerator
|
||||
? "private"
|
||||
: "protected";
|
||||
|
||||
var copyBaseCall = IsDataDefinition(definition.Type.BaseType, out _)
|
||||
var copyBaseCall = definition.IsDataDefinition(definition.Type.BaseType, out _)
|
||||
? "base(ISerializationGeneratedCopy, source, serialization, hookCtx, context)"
|
||||
: "this()";
|
||||
|
||||
var readBaseCall = IsDataDefinition(definition.Type.BaseType, out _)
|
||||
var readBaseCall = definition.IsDataDefinition(definition.Type.BaseType, out _)
|
||||
? "base(ISerializationGeneratedRead, mappingDataNode, serialization, hookCtx, context)"
|
||||
: "this()";
|
||||
|
||||
@@ -486,7 +524,7 @@ public class Generator : IIncrementalGenerator
|
||||
method = $"ReadEnum<{fieldTypeName}>";
|
||||
nullableString = string.Empty;
|
||||
}
|
||||
else if (field.Type.IsValueType && IsDataDefinition(field.Type, out _))
|
||||
else if (field.Type.IsValueType && definition.IsDataDefinition(field.Type, out _))
|
||||
{
|
||||
method = $"ReadStructDefinition<{fieldTypeName}>";
|
||||
nullableString = string.Empty;
|
||||
@@ -508,7 +546,7 @@ public class Generator : IIncrementalGenerator
|
||||
nullableString = string.Empty;
|
||||
}
|
||||
}
|
||||
else if (IsDataDefinition(field.Type, out _) && field.Type.TypeKind != TypeKind.Interface)
|
||||
else if (definition.IsDataDefinition(field.Type, out _) && field.Type.TypeKind != TypeKind.Interface)
|
||||
{
|
||||
method = $"ReadDefinition<{fieldTypeName}>";
|
||||
}
|
||||
@@ -588,7 +626,7 @@ public class Generator : IIncrementalGenerator
|
||||
var builder = new StringBuilder();
|
||||
var modifiers = string.Empty;
|
||||
|
||||
if (GetFirstDataDefinitionBaseType(definition.Type) != null)
|
||||
if (definition.GetFirstDataDefinitionBaseType() != null)
|
||||
modifiers = "override ";
|
||||
else if (IsVirtualClass(definition.Type))
|
||||
modifiers = "virtual ";
|
||||
@@ -701,7 +739,7 @@ public class Generator : IIncrementalGenerator
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
|
||||
if (GetFirstDataDefinitionBaseType(definition.Type) is { } baseType)
|
||||
if (definition.GetFirstDataDefinitionBaseType() is { } baseType)
|
||||
builder.AppendLine($"{baseType.ToDisplayString()}.Validate(nodes, node, serialization, context);");
|
||||
|
||||
return $$"""
|
||||
@@ -721,7 +759,7 @@ public class Generator : IIncrementalGenerator
|
||||
var baseDefinition = false;
|
||||
while (baseType != null)
|
||||
{
|
||||
if (!baseDefinition && IsDataDefinition(baseType, out _))
|
||||
if (!baseDefinition && definition.IsDataDefinition(baseType, out _))
|
||||
baseDefinition = true;
|
||||
|
||||
GetCopierMethod(definition, baseType, baseType.ToDisplayString(), true, builder, requiredFields);
|
||||
@@ -741,7 +779,7 @@ public class Generator : IIncrementalGenerator
|
||||
StringBuilder builder,
|
||||
string requiredFields)
|
||||
{
|
||||
if (!IsDataDefinition(type, out _))
|
||||
if (!definition.IsDataDefinition(type, out _))
|
||||
return;
|
||||
|
||||
var sameType = definition.Type.Equals(type, SymbolEqualityComparer.Default) &&
|
||||
@@ -805,7 +843,7 @@ public class Generator : IIncrementalGenerator
|
||||
}
|
||||
else
|
||||
{
|
||||
var baseCopy = IsDataDefinition(definition.Type.BaseType, out _)
|
||||
var baseCopy = definition.IsDataDefinition(definition.Type.BaseType, out _)
|
||||
? $$"""
|
||||
var definitionCast = ({{definition.Type.BaseType!.ToDisplayString()}})target;
|
||||
base.Copy(ref definitionCast, serialization, hookCtx, context);
|
||||
@@ -851,7 +889,7 @@ public class Generator : IIncrementalGenerator
|
||||
string body;
|
||||
if (definition.Type.IsAbstract)
|
||||
{
|
||||
var baseRead = IsDataDefinition(definition.Type.BaseType, out _)
|
||||
var baseRead = definition.IsDataDefinition(definition.Type.BaseType, out _)
|
||||
? $$"""
|
||||
var definitionCast = ({{definition.Type.BaseType!.ToDisplayString()}})target;
|
||||
{{definition.Type.BaseType!.ToDisplayString()}}.Read(ref definitionCast, mappingDataNode, serialization, hookCtx, context);
|
||||
@@ -881,7 +919,7 @@ public class Generator : IIncrementalGenerator
|
||||
}
|
||||
else
|
||||
{
|
||||
var baseRead = IsDataDefinition(definition.Type.BaseType, out _)
|
||||
var baseRead = definition.IsDataDefinition(definition.Type.BaseType, out _)
|
||||
? $$"""
|
||||
var definitionCast = ({{definition.Type.BaseType!.ToDisplayString()}})target;
|
||||
{{definition.Type.BaseType!.ToDisplayString()}}.Read(ref definitionCast, mappingDataNode, serialization, hookCtx, context);
|
||||
@@ -1042,7 +1080,7 @@ public class Generator : IIncrementalGenerator
|
||||
|
||||
}
|
||||
|
||||
if (GetFirstDataDefinitionBaseType(definition.Type) is { } baseType)
|
||||
if (definition.GetFirstDataDefinitionBaseType() is { } baseType)
|
||||
{
|
||||
var baseTypeName = baseType.ToDisplayString();
|
||||
builder.AppendLine(
|
||||
@@ -1102,7 +1140,7 @@ public class Generator : IIncrementalGenerator
|
||||
fieldTags.Add($"\"{field.Attribute.Tag}\"");
|
||||
}
|
||||
|
||||
if (GetFirstDataDefinitionBaseType(definition.Type) is { } baseType)
|
||||
if (definition.GetFirstDataDefinitionBaseType() is { } baseType)
|
||||
builder.AppendLine(
|
||||
$"{baseType.ToDisplayString()}.GetFieldDefinitions(instance, fields, [{string.Join(", ", fieldTags)}]);");
|
||||
|
||||
@@ -1230,7 +1268,7 @@ public class Generator : IIncrementalGenerator
|
||||
{
|
||||
""");
|
||||
|
||||
if (IsDataDefinition(type, out _) && !type.IsAbstract &&
|
||||
if (definition.IsDataDefinition(type, out _) && !type.IsAbstract &&
|
||||
type is not INamedTypeSymbol { TypeKind: TypeKind.Interface })
|
||||
{
|
||||
var nullable = !type.IsValueType || IsNullableType(type);
|
||||
|
||||
@@ -403,18 +403,6 @@ internal static class Types
|
||||
return setsRequired;
|
||||
}
|
||||
|
||||
internal static ITypeSymbol? GetFirstDataDefinitionBaseType(ITypeSymbol type)
|
||||
{
|
||||
var parent = type;
|
||||
while ((parent = parent.BaseType) != null)
|
||||
{
|
||||
if (IsDataDefinition(parent, out _))
|
||||
return parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static IEnumerable<(ISymbol Field, ITypeSymbol Type, DataFieldAttribute Attribute)> GetAllDataFields(ITypeSymbol? definition, bool isDataRecord)
|
||||
{
|
||||
while (definition != null)
|
||||
|
||||
Reference in New Issue
Block a user