mirror of
https://github.com/XWine1/XDLCompiler.git
synced 2026-07-24 12:32:21 -07:00
1494 lines
46 KiB
C#
1494 lines
46 KiB
C#
using System.CodeDom.Compiler;
|
|
using System.Collections.Immutable;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace XDLCompiler;
|
|
|
|
public class HeaderGenerator
|
|
{
|
|
private readonly List<ImportNode> _imports = [];
|
|
|
|
private readonly Dictionary<string, TypeDeclarationNode> _types = [];
|
|
|
|
private readonly Dictionary<string, TypeDeclarationNode> _importedTypes = [];
|
|
|
|
private readonly Dictionary<string, TypeDeclarationNode> _allTypes = [];
|
|
|
|
private readonly HashSet<string> _abiTypes = [];
|
|
|
|
private readonly Dictionary<string, string> _remap = [];
|
|
|
|
private readonly HashSet<Version> _versions = [];
|
|
|
|
private readonly HashSet<string> _imported = [];
|
|
|
|
private void ParseFile(string filePath, Dictionary<string, TypeDeclarationNode> types)
|
|
{
|
|
var source = File.ReadAllText(filePath);
|
|
var reader = new XdlReader(source);
|
|
var fullPath = Path.GetDirectoryName(Path.GetFullPath(filePath)) ?? Environment.CurrentDirectory;
|
|
|
|
while (reader.TryReadNext(out SyntaxNode? node))
|
|
{
|
|
if (node is ImportNode import)
|
|
{
|
|
var path = Path.GetFullPath(Path.Combine(fullPath, import.FileName.ToString()));
|
|
|
|
if (_imported.Contains(path))
|
|
continue;
|
|
|
|
_imports.Add(import);
|
|
_imported.Add(path);
|
|
ParseFile(path, _importedTypes);
|
|
}
|
|
else if (node is TypeDeclarationNode declaration)
|
|
{
|
|
types.Add(declaration.Name!, declaration);
|
|
_allTypes.TryAdd(declaration.Name!, declaration);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual string GetHeaderName(string xdlName)
|
|
{
|
|
return Path.GetFileNameWithoutExtension(xdlName) + ".g.h";
|
|
}
|
|
|
|
public void GenerateHeader(string fileName, string? prefix, IndentedTextWriter writer)
|
|
{
|
|
ParseFile(fileName, _types);
|
|
var headerName = GetHeaderName(fileName);
|
|
var includeGuard = "__" + headerName.Replace('.', '_') + "__";
|
|
writer.WriteLine("#pragma once");
|
|
writer.WriteLine($"#ifndef {includeGuard}");
|
|
writer.WriteLine($"#define {includeGuard}");
|
|
writer.WriteLine();
|
|
writer.WriteLine("#include <xcom/base.h>");
|
|
|
|
foreach (var (i, import) in _imports.Index())
|
|
{
|
|
if (i == 0)
|
|
writer.WriteLine();
|
|
|
|
var includeName = GetHeaderName(import.FileName.ToString());
|
|
writer.WriteLine($"#include \"{includeName}\"");
|
|
}
|
|
|
|
void AddAbiType(TypeDeclarationNode node)
|
|
{
|
|
var name = node.Name!;
|
|
var vtbl = name + "Vtbl";
|
|
_abiTypes.Add(name);
|
|
_abiTypes.Add(vtbl);
|
|
_remap.TryAdd(name, GetQualifiedName(node) + "<ABI>");
|
|
_remap.TryAdd(vtbl, GetQualifiedName(node) + "Vtbl<ABI>");
|
|
}
|
|
|
|
foreach (var node in _allTypes.Values)
|
|
{
|
|
_versions.Clear();
|
|
node.Accept(new AbiVersionVisitor(_allTypes, _versions));
|
|
|
|
// If the type has any ABI attributes, it is considered an ABI type.
|
|
// This means it is templated and needs to be referenced with <ABI>.
|
|
if (_versions.Count > 0 || node.TryGetAttribute<ForceAbiAttribute>(out _))
|
|
{
|
|
AddAbiType(node);
|
|
}
|
|
}
|
|
|
|
foreach (var node in _types.Values)
|
|
{
|
|
var isAbi = new StrongBox<bool>();
|
|
node.Accept(new HasAbiTypesVisitor(_abiTypes, isAbi));
|
|
|
|
// If the type has any ABI type members, it is considered an ABI type.
|
|
if (isAbi.Value)
|
|
{
|
|
AddAbiType(node);
|
|
}
|
|
}
|
|
|
|
foreach (var node in _allTypes.Values)
|
|
{
|
|
_remap.TryAdd(node.Name!, GetQualifiedName(node)!);
|
|
_remap.TryAdd(node.Name! + "Vtbl", GetQualifiedName(node)! + "Vtbl");
|
|
}
|
|
|
|
WriteTypes(writer);
|
|
writer.WriteLine();
|
|
WriteInterfaceTraits(writer);
|
|
|
|
// Determine all used ABIs
|
|
_versions.Clear();
|
|
|
|
foreach (var node in _types.Values)
|
|
node.Accept(new AbiVersionVisitor(_allTypes, _versions));
|
|
|
|
if (!string.IsNullOrEmpty(prefix))
|
|
GenerateFactory(writer, prefix);
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine($"#endif // {includeGuard}");
|
|
}
|
|
|
|
public void WriteTypes(IndentedTextWriter writer)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(writer);
|
|
|
|
string? currentNamespace = null;
|
|
|
|
void SetNamespace(string? ns)
|
|
{
|
|
if (currentNamespace == ns)
|
|
return;
|
|
|
|
if (currentNamespace != null)
|
|
{
|
|
writer.Indent--;
|
|
writer.WriteLine('}');
|
|
|
|
if (!string.IsNullOrEmpty(ns))
|
|
{
|
|
writer.WriteLine();
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(ns))
|
|
{
|
|
writer.WriteLine($"namespace {ns.Replace(".", "::")}");
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
}
|
|
|
|
currentNamespace = ns;
|
|
}
|
|
|
|
writer.WriteLine();
|
|
|
|
// Forward declarations
|
|
foreach (var node in _types.Values.OfType<TypeDeclarationNode>())
|
|
{
|
|
if (node.TryGetAttribute<NoEmitAttribute>(out _) || node is EnumNode)
|
|
continue;
|
|
|
|
SetNamespace(node.Namespace);
|
|
|
|
if (_abiTypes.Contains(node.Name!))
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
|
|
if (node is InterfaceNode)
|
|
writer.Write("struct");
|
|
else
|
|
writer.Write(node.DeclarationType);
|
|
|
|
writer.Write(' ');
|
|
writer.Write(node.Name);
|
|
writer.WriteLine(';');
|
|
writer.WriteLine();
|
|
|
|
if (node is InterfaceNode)
|
|
{
|
|
if (_abiTypes.Contains(node.Name!))
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
|
|
writer.WriteLine($"struct {node.Name}Vtbl;");
|
|
writer.WriteLine();
|
|
}
|
|
|
|
if (HasRcxReturnMethods(node))
|
|
{
|
|
writer.Write("template<abi_t ABI, typename Impl, typename Interface = ");
|
|
writer.Write(node.Name);
|
|
writer.Write("<ABI>");
|
|
writer.WriteLine('>');
|
|
writer.Write("class ");
|
|
writer.Write(node.Name);
|
|
writer.WriteLine("_Impl;");
|
|
writer.WriteLine();
|
|
}
|
|
}
|
|
|
|
int written = 0;
|
|
|
|
foreach (var (i, node) in _types.Values.OfType<EnumNode>().Index())
|
|
{
|
|
if (node.TryGetAttribute<NoEmitAttribute>(out _))
|
|
continue;
|
|
|
|
SetNamespace(node.Namespace);
|
|
|
|
if (written > 0)
|
|
writer.WriteLine();
|
|
|
|
WriteEnumDeclaration(node, writer);
|
|
written++;
|
|
}
|
|
|
|
foreach (var (i, node) in _types.Values.OfType<TypeDeclarationNode>().Index())
|
|
{
|
|
if (node.TryGetAttribute<NoEmitAttribute>(out _) || node is EnumNode)
|
|
continue;
|
|
|
|
SetNamespace(node.Namespace);
|
|
|
|
if (written > 0)
|
|
writer.WriteLine();
|
|
|
|
WriteDeclaration(node, writer);
|
|
written++;
|
|
}
|
|
|
|
SetNamespace(null);
|
|
}
|
|
|
|
public void WriteInterfaceTraits(IndentedTextWriter writer)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(writer);
|
|
|
|
foreach (var node in _types.Values.OfType<InterfaceNode>())
|
|
{
|
|
if (node.TryGetAttribute<NoEmitAttribute>(out _))
|
|
continue;
|
|
|
|
if (node.TryGetAttribute(out UuidAttribute? uuid))
|
|
{
|
|
var guid = Unsafe.BitCast<Guid, RawGuid>(uuid.Guid);
|
|
|
|
if (_abiTypes.Contains(node.Name!))
|
|
writer.Write("DECLARE_ABI_UUIDOF_HELPER");
|
|
else
|
|
writer.Write("DECLARE_UUIDOF_HELPER");
|
|
|
|
writer.Write($"({GetQualifiedName(node)}, ");
|
|
writer.Write($"0x{guid.A:X8},0x{guid.B:X4},0x{guid.C:X4},");
|
|
writer.Write($"0x{guid.D:X2},0x{guid.E:X2},0x{guid.F:X2},0x{guid.G:X2},");
|
|
writer.Write($"0x{guid.H:X2},0x{guid.I:X2},0x{guid.J:X2},0x{guid.K:X2}");
|
|
writer.WriteLine(")");
|
|
}
|
|
else if (!node.TryGetAttribute<NoUuidAttribute>(out _))
|
|
{
|
|
throw new InvalidOperationException($"{node.Name} is missing a [uuid] attribute!");
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
writer.WriteLine();
|
|
}
|
|
}
|
|
|
|
public void GenerateImpls(string prefix, IndentedTextWriter writer)
|
|
{
|
|
var remap2 = new Dictionary<string, string>();
|
|
|
|
foreach (var node in _allTypes.Values.OfType<TypeDeclarationNode>())
|
|
{
|
|
if (node.TryGetAttribute<NoEmitAttribute>(out _))
|
|
continue;
|
|
|
|
var name = GetQualifiedName(node)!;
|
|
var impl = name + "_Impl";
|
|
var vtbl = name + "Vtbl";
|
|
|
|
if (_abiTypes.Contains(node.Name!))
|
|
{
|
|
name += "<ABI>";
|
|
impl += "<ABI>";
|
|
vtbl += "<ABI>";
|
|
}
|
|
|
|
remap2.TryAdd(node.Name!, name);
|
|
|
|
if (HasRcxReturnMethods(node))
|
|
remap2.TryAdd(node.Name! + "_Impl", impl);
|
|
|
|
if (node is InterfaceNode)
|
|
{
|
|
remap2.Add(node.Name + "Vtbl", vtbl);
|
|
}
|
|
}
|
|
|
|
int implWritten = 0;
|
|
|
|
foreach (var node in _types.Values.OfType<InterfaceNode>())
|
|
{
|
|
if (node.TryGetAttribute<NoImplAttribute>(out _))
|
|
continue;
|
|
|
|
if (implWritten > 0)
|
|
writer.WriteLine();
|
|
|
|
var generated = new HashSet<TypeNode>();
|
|
var className = GetImplName(node.Name);
|
|
|
|
if (string.IsNullOrEmpty(className))
|
|
continue;
|
|
|
|
var interfaceName = node.Name;
|
|
bool hasRcxReturnMethods = HasRcxReturnMethods(node);
|
|
|
|
if (hasRcxReturnMethods)
|
|
interfaceName += "_Impl";
|
|
|
|
writer.WriteLine("//");
|
|
writer.WriteLine($"// {node.Name}");
|
|
writer.WriteLine("//");
|
|
writer.WriteLine();
|
|
|
|
writer.WriteLine("// .hpp:");
|
|
writer.WriteLine();
|
|
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
writer.WriteLine($"class {className} : public {remap2!.GetValueOrDefault(interfaceName, interfaceName)}");
|
|
writer.WriteLine('{');
|
|
writer.WriteLine("public:");
|
|
writer.Indent++;
|
|
|
|
void WriteMethodDecls(ref int written, TypeDeclarationNode node)
|
|
{
|
|
if (generated.Contains(node))
|
|
return;
|
|
|
|
generated.Add(node);
|
|
|
|
foreach (var baseType in node.BaseTypes)
|
|
{
|
|
WriteMethodDecls(ref written, _allTypes[baseType.Name]);
|
|
}
|
|
|
|
var methods = node.EnumerateAllMembers().OfType<MethodNode>();
|
|
|
|
if (methods.Any())
|
|
{
|
|
if (written > 0)
|
|
writer.WriteLine();
|
|
|
|
writer.WriteLine("//");
|
|
writer.WriteLine($"// {node.Name}");
|
|
writer.WriteLine("//");
|
|
written++;
|
|
}
|
|
|
|
foreach (var method in methods)
|
|
{
|
|
CFormatter.Write(method, writer, remap2);
|
|
writer.WriteLine(';');
|
|
}
|
|
}
|
|
|
|
void WriteMethodDefs(ref int written, TypeDeclarationNode node)
|
|
{
|
|
if (generated.Contains(node))
|
|
return;
|
|
|
|
generated.Add(node);
|
|
|
|
foreach (var baseType in node.BaseTypes)
|
|
{
|
|
WriteMethodDefs(ref written, _allTypes[baseType.Name]);
|
|
}
|
|
|
|
var methods = node.EnumerateAllMembers().OfType<MethodNode>();
|
|
|
|
if (methods.Any())
|
|
{
|
|
writer.WriteLine();
|
|
writer.WriteLine("//");
|
|
writer.WriteLine($"// {node.Name}");
|
|
writer.WriteLine("//");
|
|
written++;
|
|
}
|
|
|
|
foreach (var (index, method) in methods.Index())
|
|
{
|
|
if (index > 0)
|
|
writer.WriteLine();
|
|
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
CFormatter.Write(method with { Name = $"{className}<ABI>::{method.Name}" }, writer, remap2);
|
|
writer.WriteLine();
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
writer.WriteLine("IMPLEMENT_STUB();");
|
|
|
|
if (method.Signature.ReturnType is NamedTypeNode { Name: "HRESULT" })
|
|
writer.WriteLine("return E_NOTIMPL;");
|
|
else if (method.Signature.ReturnType is not NamedTypeNode { Name: "void" })
|
|
writer.WriteLine("return {};");
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine('}');
|
|
}
|
|
}
|
|
|
|
int written = 0;
|
|
WriteMethodDecls(ref written, node);
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine("};");
|
|
writer.WriteLine();
|
|
|
|
writer.WriteLine("#undef ABI_INTERFACE");
|
|
writer.WriteLine($"#define ABI_INTERFACE(ABI) {className}<ABI>");
|
|
writer.WriteLine($"{prefix.ToUpperInvariant()}_DECLARE_ABI_TEMPLATES(extern);");
|
|
writer.WriteLine();
|
|
writer.WriteLine("// .cpp:");
|
|
|
|
written = 0;
|
|
generated.Clear();
|
|
WriteMethodDefs(ref written, node);
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine("#undef ABI_INTERFACE");
|
|
writer.WriteLine($"#define ABI_INTERFACE(ABI) {className}<ABI>");
|
|
writer.WriteLine($"{prefix.ToUpperInvariant()}_DECLARE_ABI_TEMPLATES();");
|
|
implWritten++;
|
|
}
|
|
}
|
|
|
|
private static string? GetImplName(string? interfaceName)
|
|
{
|
|
if (string.IsNullOrEmpty(interfaceName))
|
|
return null;
|
|
|
|
if (interfaceName.StartsWith('I'))
|
|
return interfaceName[1..];
|
|
|
|
return interfaceName;
|
|
}
|
|
|
|
private void GenerateFactory(IndentedTextWriter writer, string prefix)
|
|
{
|
|
writer.WriteLine("template<template<abi_t> typename T>");
|
|
writer.WriteLine($"inline HRESULT {prefix}CreateInstance(abi_t ABI, void **ppvObject)");
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
writer.WriteLine("if (ppvObject == nullptr)");
|
|
writer.Indent++;
|
|
writer.WriteLine("return E_POINTER;");
|
|
writer.Indent--;
|
|
writer.WriteLine();
|
|
var abis = _versions.OrderDescending().ToArray();
|
|
|
|
for (int i = 0; i < abis.Length; i++)
|
|
{
|
|
var abi = $"abi_t{{{abis[i].Major},{abis[i].Minor},{abis[i].Build},{abis[i].Revision}}}";
|
|
|
|
if (i > 0)
|
|
writer.Write("else ");
|
|
|
|
writer.WriteLine($"if (ABI >= {abi})");
|
|
writer.Indent++;
|
|
writer.WriteLine($"*ppvObject = new T<{abi}>();");
|
|
writer.Indent--;
|
|
}
|
|
|
|
writer.WriteLine("else");
|
|
writer.Indent++;
|
|
writer.WriteLine("*ppvObject = new T<abi_t{}>();");
|
|
writer.Indent--;
|
|
writer.WriteLine();
|
|
writer.WriteLine("return S_OK;");
|
|
writer.Indent--;
|
|
writer.WriteLine('}');
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine($"#define {prefix.ToUpperInvariant()}_DECLARE_ABI_TEMPLATES(prefix) \\");
|
|
writer.Indent++;
|
|
|
|
foreach (var (i, abi) in _versions.Concat([new Version(0, 0, 0, 0)]).Order().Index())
|
|
{
|
|
if (i > 0)
|
|
writer.WriteLine("; \\");
|
|
|
|
var v = $"abi_t{{{abi.Major},{abi.Minor},{abi.Build},{abi.Revision}}}";
|
|
writer.Write($"prefix template class ABI_INTERFACE(({v}))");
|
|
}
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine();
|
|
}
|
|
|
|
private static void WriteEnumDeclaration(EnumNode node, IndentedTextWriter writer)
|
|
{
|
|
var abi = GetEnumAbiVersions(node);
|
|
|
|
if (abi.Length == 0 && !node.TryGetAttribute<ForceAbiAttribute>(out _))
|
|
{
|
|
WriteNonVersionedEnumDeclaration(node, writer);
|
|
return;
|
|
}
|
|
|
|
writer.WriteLine("namespace details");
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
|
|
for (int i = -1; i < abi.Length; i++)
|
|
{
|
|
if (i >= 0)
|
|
writer.WriteLine();
|
|
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
WriteRequiresClause(abi, i, writer);
|
|
writer.Write("struct enum_");
|
|
writer.Write(node.Name);
|
|
|
|
if (i >= 0)
|
|
writer.Write("<ABI>");
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
writer.Write("enum type");
|
|
|
|
foreach (var baseType in node.BaseTypes)
|
|
{
|
|
if (baseType.Exists(abi, i))
|
|
{
|
|
writer.Write(" : ");
|
|
writer.Write(baseType.Name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
|
|
foreach (var member in node.EnumMembers)
|
|
{
|
|
if (!member.Exists(abi, i))
|
|
continue;
|
|
|
|
HandleConditionalAttribute(member, writer, false);
|
|
writer.Write(member.Name);
|
|
|
|
if (member.Value != null)
|
|
{
|
|
writer.Write(" = ");
|
|
writer.Write(member.Value.Evaluate());
|
|
}
|
|
|
|
writer.WriteLine(',');
|
|
HandleConditionalAttribute(member, writer, true);
|
|
}
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine("};");
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine("};");
|
|
}
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine('}');
|
|
writer.WriteLine();
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
writer.Write("using ");
|
|
writer.Write(node.Name);
|
|
writer.Write(" = details::enum_");
|
|
writer.Write(node.Name);
|
|
writer.WriteLine("<ABI>::type;");
|
|
}
|
|
|
|
private static void WriteNonVersionedEnumDeclaration(EnumNode node, IndentedTextWriter writer)
|
|
{
|
|
writer.Write(node.DeclarationType);
|
|
writer.Write(' ');
|
|
writer.Write(node.Name);
|
|
|
|
if (node.BaseTypes.Length > 0)
|
|
{
|
|
writer.Write(" : ");
|
|
writer.Write(node.BaseTypes[0].Name);
|
|
}
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
|
|
foreach (var member in node.EnumMembers)
|
|
{
|
|
HandleConditionalAttribute(member, writer, false);
|
|
writer.Write(member.Name);
|
|
|
|
if (member.Value != null)
|
|
{
|
|
writer.Write(" = ");
|
|
writer.Write(member.Value.Evaluate());
|
|
}
|
|
|
|
writer.WriteLine(',');
|
|
HandleConditionalAttribute(member, writer, true);
|
|
}
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine("};");
|
|
}
|
|
|
|
private static void WriteRequiresClause(IReadOnlyList<Version> abis, int index, IndentedTextWriter writer)
|
|
{
|
|
if (index < 0 || index >= abis.Count)
|
|
return;
|
|
|
|
var thisABi = abis[index];
|
|
var nextAbi = index + 1 < abis.Count ? abis[index + 1] : null;
|
|
|
|
writer.Write("requires (ABI >= ");
|
|
WriteAbiVersion(thisABi, writer);
|
|
|
|
if (nextAbi != null)
|
|
{
|
|
writer.Write(" && ABI < ");
|
|
WriteAbiVersion(nextAbi, writer);
|
|
}
|
|
|
|
writer.WriteLine(')');
|
|
}
|
|
|
|
private void WriteConditionalBases(TypeDeclarationNode node, IndentedTextWriter writer)
|
|
{
|
|
var baseAbi = GetBaseTypeAbiVersions(node);
|
|
|
|
for (int i = -1; i < baseAbi.Length; i++)
|
|
{
|
|
if (i >= 0)
|
|
writer.WriteLine();
|
|
|
|
var abi = i >= 0 ? baseAbi[i] : null;
|
|
WriteConditionalBase(string.Empty);
|
|
writer.WriteLine();
|
|
WriteConditionalBase("Vtbl");
|
|
|
|
void WriteConditionalBase(string suffix)
|
|
{
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
WriteRequiresClause(baseAbi, i, writer);
|
|
writer.Write("struct ");
|
|
writer.Write(node.Name);
|
|
writer.Write(suffix);
|
|
writer.Write("Base");
|
|
|
|
if (i >= 0)
|
|
writer.Write("<ABI>");
|
|
|
|
foreach (var (j, baseType) in node.BaseTypes.Where(node => node.Exists(abi)).Index())
|
|
{
|
|
if (j > 0)
|
|
writer.Write(", ");
|
|
else
|
|
writer.Write(" : ");
|
|
|
|
var name = baseType.Name + suffix;
|
|
writer.Write(_remap.GetValueOrDefault(name, name));
|
|
}
|
|
|
|
writer.WriteLine(" {};");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void HandleConditionalAttribute(IAttributableNode node, IndentedTextWriter writer, bool end)
|
|
{
|
|
var indent = writer.Indent;
|
|
|
|
if (!end && node.TryGetAttribute(out ConditionalAttribute? conditional))
|
|
{
|
|
writer.Indent = indent - 1;
|
|
writer.WriteLine($"#if {conditional.Condition}");
|
|
}
|
|
|
|
if ((end && node.TryGetAttribute<ConditionalAttribute>(out _)) ||
|
|
(node is MemberBlockEndNode endNode && endNode.Block.TryGetAttribute<ConditionalAttribute>(out _)))
|
|
{
|
|
writer.Indent = indent - 1;
|
|
writer.WriteLine("#endif");
|
|
}
|
|
|
|
writer.Indent = indent;
|
|
}
|
|
|
|
private void WriteDeclaration(TypeDeclarationNode node, IndentedTextWriter writer)
|
|
{
|
|
if (node is EnumNode enumNode)
|
|
{
|
|
WriteEnumDeclaration(enumNode, writer);
|
|
return;
|
|
}
|
|
|
|
var isAbiType = _abiTypes.Contains(node.Name!);
|
|
var dataAbi = GetDataAbiVersions(node);
|
|
var codeAbi = GetCodeAbiVersions(node);
|
|
var mainAbi = node is InterfaceNode ? codeAbi : dataAbi;
|
|
var thisParam = new ParameterNode([], new PointerTypeNode(new NamedTypeNode("void")), null);
|
|
bool hasRcxReturnMethods = HasRcxReturnMethods(node);
|
|
bool hasConditionalBases = HasConditionalBases(node);
|
|
bool hasDataMembers = node.EnumerateAllMembers().OfType<FieldNode>().Any();
|
|
|
|
if (hasConditionalBases || (node is InterfaceNode && hasDataMembers))
|
|
{
|
|
writer.WriteLine("namespace details");
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
}
|
|
|
|
if (node is InterfaceNode && hasDataMembers)
|
|
{
|
|
for (int i = -1; i < dataAbi.Length; i++)
|
|
{
|
|
if (i >= 0)
|
|
writer.WriteLine();
|
|
|
|
var thisAbi = i >= 0 ? dataAbi[i] : null;
|
|
var nextAbi = i + 1 < dataAbi.Length ? dataAbi[i + 1] : null;
|
|
|
|
if (isAbiType)
|
|
{
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
WriteRequiresClause(dataAbi, i, writer);
|
|
}
|
|
|
|
writer.Write("struct");
|
|
writer.Write(' ');
|
|
writer.Write(node.Name);
|
|
writer.Write("Data");
|
|
|
|
if (i >= 0 && isAbiType)
|
|
writer.Write("<ABI>");
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
|
|
foreach (var member in node.EnumerateMembers(thisAbi, includeBlocks: true))
|
|
{
|
|
if (member is MemberBlockNode or MemberBlockEndNode)
|
|
HandleConditionalAttribute(member, writer, end: false);
|
|
|
|
if (member is not FieldNode)
|
|
continue;
|
|
|
|
HandleConditionalAttribute(member, writer, end: false);
|
|
CFormatter.Write(member, writer, _remap);
|
|
writer.WriteLine(';');
|
|
HandleConditionalAttribute(member, writer, end: true);
|
|
}
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine("};");
|
|
}
|
|
}
|
|
|
|
if (hasConditionalBases)
|
|
{
|
|
if (node is InterfaceNode && hasDataMembers)
|
|
writer.WriteLine();
|
|
|
|
WriteConditionalBases(node, writer);
|
|
}
|
|
|
|
if (hasConditionalBases || (node is InterfaceNode && hasDataMembers))
|
|
{
|
|
writer.Indent--;
|
|
writer.WriteLine('}');
|
|
writer.WriteLine();
|
|
}
|
|
|
|
for (int i = -1; i < mainAbi.Length; i++)
|
|
{
|
|
if (i >= 0)
|
|
writer.WriteLine();
|
|
|
|
var thisAbi = i >= 0 ? mainAbi[i] : null;
|
|
var nextAbi = i + 1 < mainAbi.Length ? mainAbi[i + 1] : null;
|
|
|
|
if (isAbiType)
|
|
{
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
WriteRequiresClause(mainAbi, i, writer);
|
|
}
|
|
|
|
if (node is InterfaceNode)
|
|
writer.Write("struct");
|
|
else
|
|
writer.Write(node.DeclarationType);
|
|
|
|
writer.Write(' ');
|
|
writer.Write(node.Name);
|
|
var baseTypes = node.BaseTypes.Where(node => node.Exists(thisAbi));
|
|
|
|
if (i >= 0 && isAbiType)
|
|
writer.Write("<ABI>");
|
|
|
|
if (hasConditionalBases || baseTypes.Any())
|
|
writer.Write(" : ");
|
|
|
|
if (hasConditionalBases)
|
|
{
|
|
if (node is ClassNode)
|
|
writer.Write("public ");
|
|
|
|
writer.Write("details::");
|
|
writer.Write(node.Name);
|
|
writer.Write("Base");
|
|
writer.Write("<ABI>");
|
|
}
|
|
else
|
|
{
|
|
foreach (var (j, baseType) in baseTypes.Index())
|
|
{
|
|
if (j > 0)
|
|
writer.Write(", ");
|
|
|
|
if (node is ClassNode)
|
|
writer.Write("public ");
|
|
|
|
writer.Write(_remap.GetValueOrDefault(baseType.Name, baseType.Name));
|
|
}
|
|
}
|
|
|
|
if (node is InterfaceNode && hasDataMembers)
|
|
{
|
|
if (baseTypes.Any())
|
|
writer.Write(", ");
|
|
|
|
writer.Write("details::");
|
|
writer.Write(node.Name);
|
|
writer.Write("Data");
|
|
|
|
if (isAbiType)
|
|
{
|
|
writer.Write("<ABI>");
|
|
}
|
|
}
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
|
|
if (hasRcxReturnMethods)
|
|
{
|
|
writer.Write("template<abi_t, typename, typename> friend class ");
|
|
writer.Write(node.Name);
|
|
writer.WriteLine("_Impl;");
|
|
}
|
|
|
|
if (node is InterfaceNode)
|
|
{
|
|
foreach (var member in node.EnumerateMembers(thisAbi, includeBlocks: true))
|
|
{
|
|
if (member is MemberBlockNode or MemberBlockEndNode)
|
|
HandleConditionalAttribute(member, writer, end: false);
|
|
|
|
if (member is not MethodNode method)
|
|
continue;
|
|
|
|
HandleConditionalAttribute(member, writer, end: false);
|
|
|
|
if (member.TryGetAttribute<RcxReturnAttribute>(out _))
|
|
{
|
|
writer.Write("private: virtual ");
|
|
var signature = method.Signature with
|
|
{
|
|
ReturnType = new PointerTypeNode(method.Signature.ReturnType),
|
|
Parameters = method.Signature.Parameters.Insert(0, thisParam),
|
|
};
|
|
|
|
CFormatter.Write(method with { Name = "_abi_" + member.Name, Signature = signature }, writer, _remap);
|
|
writer.WriteLine(" = 0; public:");
|
|
}
|
|
else
|
|
{
|
|
writer.Write("virtual ");
|
|
CFormatter.Write(member, writer, _remap);
|
|
writer.WriteLine(" = 0;");
|
|
}
|
|
|
|
HandleConditionalAttribute(member, writer, end: true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var member in node.EnumerateMembers(thisAbi, includeBlocks: true))
|
|
{
|
|
if (member is MemberBlockNode or MemberBlockEndNode)
|
|
HandleConditionalAttribute(member, writer, end: false);
|
|
|
|
if (member is not FieldNode)
|
|
continue;
|
|
|
|
HandleConditionalAttribute(member, writer, end: false);
|
|
CFormatter.Write(member, writer, _remap);
|
|
writer.WriteLine(';');
|
|
HandleConditionalAttribute(member, writer, end: true);
|
|
}
|
|
}
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine("};");
|
|
}
|
|
|
|
if (node is InterfaceNode)
|
|
{
|
|
writer.WriteLine();
|
|
WriteVTable(node, writer);
|
|
}
|
|
|
|
if (hasRcxReturnMethods)
|
|
{
|
|
writer.WriteLine();
|
|
WriteRcxReturnThunks(node, writer);
|
|
}
|
|
}
|
|
|
|
private TypeDeclarationNode? GetInterfaceBase(TypeDeclarationNode node, Version? abi)
|
|
{
|
|
foreach (var baseType in node.BaseTypes)
|
|
{
|
|
if (baseType.Exists(abi) && _allTypes.TryGetValue(baseType.Name, out var baseNode))
|
|
{
|
|
return baseNode;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void WriteRcxReturnThunks(TypeDeclarationNode node, IndentedTextWriter writer)
|
|
{
|
|
var abi = GetRcxReturnAbiVersions(node);
|
|
var thisParam = new ParameterNode([], new PointerTypeNode(new NamedTypeNode("void")), "this_");
|
|
|
|
for (int i = -1; i < abi.Length; i++)
|
|
{
|
|
if (i >= 0)
|
|
writer.WriteLine();
|
|
|
|
var thisAbi = i >= 0 ? abi[i] : null;
|
|
var nextAbi = i + 1 < abi.Length ? abi[i + 1] : null;
|
|
|
|
writer.WriteLine("template<abi_t ABI, typename Impl, typename Interface>");
|
|
WriteRequiresClause(abi, i, writer);
|
|
writer.Write("class ");
|
|
writer.Write(node.Name);
|
|
writer.Write("_Impl");
|
|
|
|
if (i >= 0)
|
|
writer.Write("<ABI, Impl, Interface>");
|
|
|
|
writer.Write(" : public ");
|
|
|
|
if (GetInterfaceBase(node, thisAbi) is { } baseNode && HasRcxReturnMethods(baseNode))
|
|
{
|
|
writer.Write(baseNode.Name);
|
|
writer.Write("_Impl");
|
|
writer.Write("<ABI, Impl, Interface>");
|
|
}
|
|
else
|
|
{
|
|
writer.Write("Interface");
|
|
}
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
bool newLine = false;
|
|
|
|
foreach (var member in node.EnumerateMembers(thisAbi, includeBlocks: true))
|
|
{
|
|
if (member is MemberBlockNode or MemberBlockEndNode)
|
|
{
|
|
MemberBlockNode block;
|
|
|
|
if (member is MemberBlockEndNode end)
|
|
block = end.Block;
|
|
else
|
|
block = (MemberBlockNode)member;
|
|
|
|
if (!block.EnumerateMembers(thisAbi).Any(n => n.TryGetAttribute<RcxReturnAttribute>(out _)))
|
|
continue;
|
|
|
|
if (newLine && member is MemberBlockNode)
|
|
{
|
|
writer.WriteLine();
|
|
newLine = false;
|
|
}
|
|
|
|
HandleConditionalAttribute(member, writer, end: member is MemberBlockEndNode);
|
|
continue;
|
|
}
|
|
|
|
if (member is not MethodNode method || !member.TryGetAttribute<RcxReturnAttribute>(out _))
|
|
continue;
|
|
|
|
if (newLine)
|
|
writer.WriteLine();
|
|
|
|
HandleConditionalAttribute(method, writer, false);
|
|
var builder = ImmutableArray.CreateBuilder<ParameterNode>();
|
|
builder.Add(thisParam);
|
|
|
|
foreach (var (k, param) in method.Signature.Parameters.Index())
|
|
builder.Add(param with { Name = "a" + (k + 1) });
|
|
|
|
var signature = method.Signature with
|
|
{
|
|
ReturnType = new PointerTypeNode(method.Signature.ReturnType),
|
|
Parameters = builder.DrainToImmutable(),
|
|
};
|
|
|
|
CFormatter.Write(method with { Name = "_abi_" + method.Name, Signature = signature }, writer, _remap);
|
|
writer.WriteLine(" final");
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
CFormatter.Write(method.Signature.ReturnType, writer, _remap, null);
|
|
writer.Write("(Impl::*fn)(");
|
|
|
|
foreach (var (k, param) in method.Signature.Parameters.Index())
|
|
{
|
|
if (k > 0)
|
|
writer.Write(", ");
|
|
|
|
CFormatter.Write(param.Type, writer, _remap, null);
|
|
}
|
|
|
|
writer.Write(") = &Impl::");
|
|
writer.Write(method.Name);
|
|
writer.WriteLine(';');
|
|
writer.Write("return (*(");
|
|
CFormatter.Write(signature.ReturnType, writer, _remap, null);
|
|
writer.Write("(**)(void *, void *");
|
|
|
|
foreach (var (k, param) in method.Signature.Parameters.Index())
|
|
{
|
|
writer.Write(", ");
|
|
CFormatter.Write(param.Type, writer, _remap, null);
|
|
}
|
|
|
|
writer.Write("))&fn)(this_, this");
|
|
|
|
for (int k = 0; k < method.Signature.Parameters.Length; k++)
|
|
{
|
|
writer.Write(", ");
|
|
writer.Write("a" + (k + 1));
|
|
}
|
|
|
|
writer.WriteLine(");");
|
|
writer.Indent--;
|
|
writer.WriteLine('}');
|
|
HandleConditionalAttribute(method, writer, true);
|
|
newLine = true;
|
|
}
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine("};");
|
|
}
|
|
}
|
|
|
|
private void WriteVTable(TypeDeclarationNode node, IndentedTextWriter writer)
|
|
{
|
|
var isAbiType = _abiTypes.Contains(node.Name!);
|
|
var methodAbi = GetCodeAbiVersions(node);
|
|
var thisParam = new ParameterNode([], new PointerTypeNode(new NamedTypeNode("void")), null);
|
|
bool hasConditionalBases = HasConditionalBases(node);
|
|
|
|
for (int i = -1; i < methodAbi.Length; i++)
|
|
{
|
|
if (i >= 0)
|
|
writer.WriteLine();
|
|
|
|
var thisAbi = i >= 0 ? methodAbi[i] : null;
|
|
var nextAbi = i + 1 < methodAbi.Length ? methodAbi[i + 1] : null;
|
|
var methods = node.EnumerateMembers(thisAbi).OfType<MethodNode>();
|
|
|
|
if (isAbiType)
|
|
{
|
|
writer.WriteLine("template<abi_t ABI>");
|
|
WriteRequiresClause(methodAbi, i, writer);
|
|
}
|
|
|
|
writer.Write("struct ");
|
|
writer.Write(node.Name);
|
|
writer.Write("Vtbl");
|
|
|
|
if (i >= 0 && isAbiType)
|
|
writer.Write("<ABI>");
|
|
|
|
if (hasConditionalBases)
|
|
{
|
|
writer.Write(" : ");
|
|
writer.Write("details::");
|
|
writer.Write(node.Name);
|
|
writer.Write("VtblBase");
|
|
writer.Write("<ABI>");
|
|
}
|
|
else
|
|
{
|
|
foreach (var (j, baseType) in node.BaseTypes.Where(node => node.Exists(thisAbi)).Index())
|
|
{
|
|
if (j > 0)
|
|
writer.Write(", ");
|
|
else
|
|
writer.Write(" : ");
|
|
|
|
var name = baseType.Name + "Vtbl";
|
|
writer.Write(_remap.GetValueOrDefault(name, name));
|
|
}
|
|
}
|
|
|
|
writer.WriteLine();
|
|
writer.WriteLine('{');
|
|
writer.Indent++;
|
|
|
|
foreach (var member in node.EnumerateMembers(thisAbi, includeBlocks: true))
|
|
{
|
|
if (member is MemberBlockNode or MemberBlockEndNode)
|
|
HandleConditionalAttribute(member, writer, end: false);
|
|
|
|
if (member is not MethodNode method)
|
|
continue;
|
|
|
|
HandleConditionalAttribute(member, writer, end: false);
|
|
var signature = method.Signature with { Parameters = method.Signature.Parameters.Insert(0, thisParam) };
|
|
CFormatter.Write(new PointerTypeNode(signature, false), writer, _remap, method.Name);
|
|
writer.WriteLine(';');
|
|
HandleConditionalAttribute(member, writer, end: true);
|
|
}
|
|
|
|
writer.Indent--;
|
|
writer.WriteLine("};");
|
|
}
|
|
}
|
|
|
|
private static bool HasConditionalBases(TypeDeclarationNode node)
|
|
{
|
|
foreach (var baseType in node.BaseTypes)
|
|
{
|
|
if (baseType.TryGetAttribute<AbiAddedAttribute>(out _) ||
|
|
baseType.TryGetAttribute<AbiRemovedAttribute>(out _))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool HasRcxReturnMethods(TypeDeclarationNode node)
|
|
{
|
|
foreach (var baseType in node.BaseTypes)
|
|
{
|
|
if (_allTypes.TryGetValue(baseType.Name, out var baseTypeNode))
|
|
{
|
|
if (HasRcxReturnMethods(baseTypeNode))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var method in node.EnumerateAllMembers().OfType<MethodNode>())
|
|
{
|
|
if (method.TryGetAttribute<RcxReturnAttribute>(out _))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static Version[] GetEnumAbiVersions(EnumNode node)
|
|
{
|
|
var versions = new HashSet<Version>();
|
|
node.Accept(new EnumAbiVersionCollector(versions));
|
|
return [.. versions.Order()];
|
|
}
|
|
|
|
private static Version[] GetDataAbiVersions(SyntaxNode node)
|
|
{
|
|
var versions = new HashSet<Version>();
|
|
node.Accept(new DataAbiVersionCollector(versions));
|
|
return [.. versions.Order()];
|
|
}
|
|
|
|
private static Version[] GetCodeAbiVersions(SyntaxNode node)
|
|
{
|
|
var versions = new HashSet<Version>();
|
|
node.Accept(new CodeAbiVersionCollector(versions));
|
|
return [.. versions.Order()];
|
|
}
|
|
|
|
private static Version[] GetRcxReturnAbiVersions(SyntaxNode node)
|
|
{
|
|
var versions = new HashSet<Version>();
|
|
node.Accept(new RcxReturnAbiVersionCollector(versions));
|
|
return [.. versions.Order()];
|
|
}
|
|
|
|
private static Version[] GetBaseTypeAbiVersions(SyntaxNode node)
|
|
{
|
|
var versions = new HashSet<Version>();
|
|
node.Accept(new BaseTypeAbiVersionCollector(versions));
|
|
return [.. versions.Order()];
|
|
}
|
|
|
|
private static void WriteAbiVersion(Version version, TextWriter writer)
|
|
{
|
|
writer.Write($"abi_t{{{version.Major},{version.Minor},{version.Build},{version.Revision}}}");
|
|
}
|
|
|
|
private static string? GetQualifiedName(TypeDeclarationNode node)
|
|
{
|
|
if (string.IsNullOrEmpty(node.Namespace) || string.IsNullOrEmpty(node.Name))
|
|
return node.Name;
|
|
|
|
return node.Namespace.Replace(".", "::") + "::" + node.Name;
|
|
}
|
|
|
|
private sealed class EnumAbiVersionCollector(ICollection<Version> versions) : SyntaxVisitor
|
|
{
|
|
public override void Visit(AttributeNode node)
|
|
{
|
|
base.Visit(node);
|
|
|
|
if (node.TryGetAttribute(out AbiAddedAttribute? added))
|
|
{
|
|
versions.Add(added.Version);
|
|
}
|
|
|
|
if (node.TryGetAttribute(out AbiRemovedAttribute? removed))
|
|
{
|
|
versions.Add(removed.Version);
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class DataAbiVersionCollector(ICollection<Version> versions) : SyntaxVisitor
|
|
{
|
|
public override void Visit(AttributeNode node)
|
|
{
|
|
base.Visit(node);
|
|
|
|
if (node.TryGetAttribute(out AbiAddedAttribute? added))
|
|
{
|
|
versions.Add(added.Version);
|
|
}
|
|
|
|
if (node.TryGetAttribute(out AbiRemovedAttribute? removed))
|
|
{
|
|
versions.Add(removed.Version);
|
|
}
|
|
}
|
|
|
|
public override void Visit(MemberBlockNode node)
|
|
{
|
|
var counter = new MemberCounter();
|
|
node.Accept(counter);
|
|
|
|
if (counter.FieldCount == 0)
|
|
return;
|
|
|
|
base.Visit(node);
|
|
}
|
|
|
|
public override void Visit(BaseTypeNode node)
|
|
{
|
|
}
|
|
|
|
public override void Visit(MethodNode node)
|
|
{
|
|
}
|
|
}
|
|
|
|
private sealed class RcxReturnAbiVersionCollector(ICollection<Version> versions) : SyntaxVisitor
|
|
{
|
|
private readonly Stack<MemberBlockNode> _blocks = [];
|
|
|
|
public override void Visit(MethodNode node)
|
|
{
|
|
if (!node.TryGetAttribute<RcxReturnAttribute>(out _))
|
|
return;
|
|
|
|
if (node.TryGetAttribute(out AbiAddedAttribute? added))
|
|
{
|
|
versions.Add(added.Version);
|
|
}
|
|
|
|
if (node.TryGetAttribute(out AbiRemovedAttribute? removed))
|
|
{
|
|
versions.Add(removed.Version);
|
|
}
|
|
|
|
foreach (var block in _blocks)
|
|
{
|
|
if (block.TryGetAttribute(out added))
|
|
{
|
|
versions.Add(added.Version);
|
|
}
|
|
|
|
if (block.TryGetAttribute(out removed))
|
|
{
|
|
versions.Add(removed.Version);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Visit(MemberBlockNode node)
|
|
{
|
|
_blocks.Push(node);
|
|
base.Visit(node);
|
|
_blocks.Pop();
|
|
}
|
|
}
|
|
|
|
private sealed class CodeAbiVersionCollector(ICollection<Version> versions) : SyntaxVisitor
|
|
{
|
|
public override void Visit(AttributeNode node)
|
|
{
|
|
base.Visit(node);
|
|
|
|
if (node.TryGetAttribute(out AbiAddedAttribute? added))
|
|
{
|
|
versions.Add(added.Version);
|
|
}
|
|
|
|
if (node.TryGetAttribute(out AbiRemovedAttribute? removed))
|
|
{
|
|
versions.Add(removed.Version);
|
|
}
|
|
}
|
|
|
|
public override void Visit(MemberBlockNode node)
|
|
{
|
|
var counter = new MemberCounter();
|
|
node.Accept(counter);
|
|
|
|
if (counter.MethodCount == 0)
|
|
return;
|
|
|
|
base.Visit(node);
|
|
}
|
|
|
|
public override void Visit(BaseTypeNode node)
|
|
{
|
|
}
|
|
|
|
public override void Visit(FieldNode node)
|
|
{
|
|
}
|
|
}
|
|
|
|
private sealed class BaseTypeAbiVersionCollector(ICollection<Version> versions) : SyntaxVisitor
|
|
{
|
|
public override void Visit(AttributeNode node)
|
|
{
|
|
base.Visit(node);
|
|
|
|
if (node.TryGetAttribute(out AbiAddedAttribute? added))
|
|
{
|
|
versions.Add(added.Version);
|
|
}
|
|
|
|
if (node.TryGetAttribute(out AbiRemovedAttribute? removed))
|
|
{
|
|
versions.Add(removed.Version);
|
|
}
|
|
}
|
|
|
|
public override void Visit(MemberBlockNode node)
|
|
{
|
|
}
|
|
|
|
public override void Visit(FieldNode node)
|
|
{
|
|
}
|
|
|
|
public override void Visit(MethodNode node)
|
|
{
|
|
}
|
|
}
|
|
|
|
private sealed class MemberCounter : SyntaxVisitor
|
|
{
|
|
public int MethodCount { get; private set; }
|
|
|
|
public int FieldCount { get; private set; }
|
|
|
|
public override void Visit(FieldNode node)
|
|
{
|
|
FieldCount++;
|
|
base.Visit(node);
|
|
}
|
|
|
|
public override void Visit(MethodNode node)
|
|
{
|
|
MethodCount++;
|
|
base.Visit(node);
|
|
}
|
|
}
|
|
|
|
private sealed class AbiVersionVisitor(Dictionary<string, TypeDeclarationNode> types, ICollection<Version> versions) : SyntaxVisitor
|
|
{
|
|
private HashSet<string> _visited = [];
|
|
|
|
public override void Visit(AttributeNode node)
|
|
{
|
|
base.Visit(node);
|
|
|
|
if (node.TryGetAttribute(out AbiAddedAttribute? added))
|
|
{
|
|
versions.Add(added.Version);
|
|
}
|
|
|
|
if (node.TryGetAttribute(out AbiRemovedAttribute? removed))
|
|
{
|
|
versions.Add(removed.Version);
|
|
}
|
|
}
|
|
|
|
public override void Visit(BaseTypeNode node)
|
|
{
|
|
if (types.TryGetValue(node.Name, out var baseType) && !_visited.Contains(node.Name))
|
|
{
|
|
_visited.Add(node.Name);
|
|
baseType.Accept(this);
|
|
}
|
|
|
|
base.Visit(node);
|
|
}
|
|
|
|
public override void Visit(NamedTypeNode node)
|
|
{
|
|
if (types.TryGetValue(node.Name, out var type) && type != node && !_visited.Contains(node.Name))
|
|
{
|
|
_visited.Add(node.Name);
|
|
type.Accept(this);
|
|
}
|
|
|
|
base.Visit(node);
|
|
}
|
|
}
|
|
|
|
private sealed class HasAbiTypesVisitor(ISet<string> abiTypes, StrongBox<bool> result) : SyntaxVisitor
|
|
{
|
|
public override void Visit(BaseTypeNode node)
|
|
{
|
|
if (abiTypes.Contains(node.Name))
|
|
{
|
|
result.Value = true;
|
|
}
|
|
}
|
|
|
|
public override void Visit(NamedTypeNode node)
|
|
{
|
|
if (abiTypes.Contains(node.Name))
|
|
{
|
|
result.Value = true;
|
|
}
|
|
}
|
|
}
|
|
}
|